diff options
Diffstat (limited to 'drivers/net/wireless')
796 files changed, 94556 insertions, 35509 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 200020eb300..b2137e8f7ca 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -53,7 +53,7 @@ config LIBERTAS_THINFIRM_USB config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" - depends on ISA_DMA_API && (PCI || BROKEN) + depends on CFG80211 && ISA_DMA_API && (PCI || BROKEN) select WIRELESS_EXT select CRYPTO select WEXT_SPY @@ -73,7 +73,7 @@ config AIRO config ATMEL tristate "Atmel at76c50x chipset 802.11b support" - depends on (PCI || PCMCIA) + depends on CFG80211 && (PCI || PCMCIA) select WIRELESS_EXT select WEXT_PRIV select FW_LOADER @@ -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 @@ -138,7 +138,7 @@ config AIRO_CS config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" - depends on PCMCIA + depends on CFG80211 && PCMCIA select WIRELESS_EXT select WEXT_SPY help @@ -168,7 +168,7 @@ config PRISM54 config USB_ZD1201 tristate "USB ZD1201 based Wireless device support" - depends on USB + depends on CFG80211 && USB select WIRELESS_EXT select WEXT_PRIV select FW_LOADER @@ -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/adm8211.c b/drivers/net/wireless/adm8211.c index f9a24e599de..f35f93c31b0 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -15,7 +15,6 @@ * more details. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/if.h> #include <linux/skbuff.h> @@ -1314,7 +1313,7 @@ static void adm8211_bss_info_changed(struct ieee80211_hw *dev, if (!(changes & BSS_CHANGED_BSSID)) return; - if (memcmp(conf->bssid, priv->bssid, ETH_ALEN)) { + if (!ether_addr_equal(conf->bssid, priv->bssid)) { adm8211_set_bssid(dev, conf->bssid); memcpy(priv->bssid, conf->bssid, ETH_ALEN); } @@ -1866,7 +1865,6 @@ static int adm8211_probe(struct pci_dev *pdev, dev->flags = IEEE80211_HW_SIGNAL_UNSPEC; dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); - dev->channel_change_time = 1000; dev->max_signal = 100; /* FIXME: find better value */ dev->queues = 1; /* ADM8211C supports more, maybe ADM8211B too */ @@ -1924,7 +1922,6 @@ static int adm8211_probe(struct pci_dev *pdev, pci_iounmap(pdev, priv->map); err_free_dev: - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7fe19648f10..64747d457bb 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -36,7 +36,7 @@ #include <linux/bitops.h> #include <linux/scatterlist.h> #include <linux/crypto.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/unaligned.h> #include <linux/netdevice.h> @@ -45,11 +45,11 @@ #include <linux/if_arp.h> #include <linux/ioport.h> #include <linux/pci.h> -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include <linux/kthread.h> #include <linux/freezer.h> -#include <linux/ieee80211.h> +#include <net/cfg80211.h> #include <net/iw_handler.h> #include "airo.h" @@ -5570,7 +5570,6 @@ static void airo_pci_remove(struct pci_dev *pdev) airo_print_info(dev->name, "Unregistering..."); stop_airo_card(dev, 1); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) @@ -5798,7 +5797,7 @@ static int airo_set_freq(struct net_device *dev, /* Hack to fall through... */ fwrq->e = 0; - fwrq->m = ieee80211_freq_to_dsss_chan(f); + fwrq->m = ieee80211_frequency_to_channel(f); } /* Setting by channel number */ if((fwrq->m > 1000) || (fwrq->e > 0)) @@ -5842,7 +5841,8 @@ static int airo_get_freq(struct net_device *dev, ch = le16_to_cpu(status_rid.channel); if((ch > 0) && (ch < 15)) { - fwrq->m = ieee80211_dsss_chan_to_freq(ch) * 100000; + fwrq->m = 100000 * + ieee80211_channel_to_frequency(ch, IEEE80211_BAND_2GHZ); fwrq->e = 1; } else { fwrq->m = ch; @@ -6899,7 +6899,8 @@ static int airo_get_range(struct net_device *dev, k = 0; for(i = 0; i < 14; i++) { range->freq[k].i = i + 1; /* List index */ - range->freq[k].m = ieee80211_dsss_chan_to_freq(i + 1) * 100000; + range->freq[k].m = 100000 * + ieee80211_channel_to_frequency(i + 1, IEEE80211_BAND_2GHZ); range->freq[k++].e = 1; /* Values in MHz -> * 10^5 * 10 */ } range->num_frequency = k; @@ -7298,7 +7299,8 @@ static inline char *airo_translate_scan(struct net_device *dev, /* Add frequency */ iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = le16_to_cpu(bss->dsChannel); - iwe.u.freq.m = ieee80211_dsss_chan_to_freq(iwe.u.freq.m) * 100000; + iwe.u.freq.m = 100000 * + ieee80211_channel_to_frequency(iwe.u.freq.m, IEEE80211_BAND_2GHZ); iwe.u.freq.e = 1; current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, IW_EV_FREQ_LEN); diff --git a/drivers/net/wireless/airo_cs.c b/drivers/net/wireless/airo_cs.c index 14128fd265a..7e9ede6c579 100644 --- a/drivers/net/wireless/airo_cs.c +++ b/drivers/net/wireless/airo_cs.c @@ -23,7 +23,6 @@ #ifdef __IN_PCMCIA_PACKAGE__ #include <pcmcia/k_compat.h> #endif -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/ptrace.h> diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 34c8a33cac0..d48776e4f34 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -365,15 +365,15 @@ static inline unsigned long at76_get_timeout(struct dfu_status *s) static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, int manifest_sync_timeout) { - u8 *block; - struct dfu_status dfu_stat_buf; int ret = 0; int need_dfu_state = 1; int is_done = 0; - u8 dfu_state = 0; u32 dfu_timeout = 0; int bsize = 0; int blockno = 0; + struct dfu_status *dfu_stat_buf = NULL; + u8 *dfu_state = NULL; + u8 *block = NULL; at76_dbg(DBG_DFU, "%s( %p, %u, %d)", __func__, buf, size, manifest_sync_timeout); @@ -383,13 +383,28 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, return -EINVAL; } + dfu_stat_buf = kmalloc(sizeof(struct dfu_status), GFP_KERNEL); + if (!dfu_stat_buf) { + ret = -ENOMEM; + goto exit; + } + block = kmalloc(FW_BLOCK_SIZE, GFP_KERNEL); - if (!block) - return -ENOMEM; + if (!block) { + ret = -ENOMEM; + goto exit; + } + + dfu_state = kmalloc(sizeof(u8), GFP_KERNEL); + if (!dfu_state) { + ret = -ENOMEM; + goto exit; + } + *dfu_state = 0; do { if (need_dfu_state) { - ret = at76_dfu_get_state(udev, &dfu_state); + ret = at76_dfu_get_state(udev, dfu_state); if (ret < 0) { dev_err(&udev->dev, "cannot get DFU state: %d\n", ret); @@ -398,13 +413,13 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, need_dfu_state = 0; } - switch (dfu_state) { + switch (*dfu_state) { case STATE_DFU_DOWNLOAD_SYNC: at76_dbg(DBG_DFU, "STATE_DFU_DOWNLOAD_SYNC"); - ret = at76_dfu_get_status(udev, &dfu_stat_buf); + ret = at76_dfu_get_status(udev, dfu_stat_buf); if (ret >= 0) { - dfu_state = dfu_stat_buf.state; - dfu_timeout = at76_get_timeout(&dfu_stat_buf); + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); need_dfu_state = 0; } else dev_err(&udev->dev, @@ -447,12 +462,12 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, case STATE_DFU_MANIFEST_SYNC: at76_dbg(DBG_DFU, "STATE_DFU_MANIFEST_SYNC"); - ret = at76_dfu_get_status(udev, &dfu_stat_buf); + ret = at76_dfu_get_status(udev, dfu_stat_buf); if (ret < 0) break; - dfu_state = dfu_stat_buf.state; - dfu_timeout = at76_get_timeout(&dfu_stat_buf); + *dfu_state = dfu_stat_buf->state; + dfu_timeout = at76_get_timeout(dfu_stat_buf); need_dfu_state = 0; /* override the timeout from the status response, @@ -484,14 +499,17 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, break; default: - at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", dfu_state); + at76_dbg(DBG_DFU, "DFU UNKNOWN STATE (%d)", *dfu_state); ret = -EINVAL; break; } } while (!is_done && (ret >= 0)); exit: + kfree(dfu_state); kfree(block); + kfree(dfu_stat_buf); + if (ret >= 0) ret = 0; @@ -1277,6 +1295,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) dev_err(&udev->dev, "loading %dth firmware block failed: %d\n", blockno, ret); + ret = -EIO; goto exit; } buf += bsize; @@ -1410,6 +1429,8 @@ static int at76_startup_device(struct at76_priv *priv) /* remove BSSID from previous run */ memset(priv->bssid, 0, ETH_ALEN); + priv->scanning = false; + if (at76_set_radio(priv, 1) == 1) at76_wait_completion(priv, CMD_RADIO_ON); @@ -1483,6 +1504,52 @@ static void at76_work_submit_rx(struct work_struct *work) mutex_unlock(&priv->mtx); } +/* This is a workaround to make scan working: + * currently mac80211 does not process frames with no frequency + * information. + * However during scan the HW performs a sweep by itself, and we + * are unable to know where the radio is actually tuned. + * This function tries to do its best to guess this information.. + * During scan, If the current frame is a beacon or a probe response, + * the channel information is extracted from it. + * When not scanning, for other frames, or if it happens that for + * whatever reason we fail to parse beacons and probe responses, this + * function returns the priv->channel information, that should be correct + * at least when we are not scanning. + */ +static inline int at76_guess_freq(struct at76_priv *priv) +{ + size_t el_off; + const u8 *el; + int channel = priv->channel; + int len = priv->rx_skb->len; + struct ieee80211_hdr *hdr = (void *)priv->rx_skb->data; + + if (!priv->scanning) + goto exit; + + if (len < 24) + goto exit; + + if (ieee80211_is_probe_resp(hdr->frame_control)) { + el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable; + } else if (ieee80211_is_beacon(hdr->frame_control)) { + el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable); + el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable; + } else { + goto exit; + } + len -= el_off; + + el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len); + if (el && el[1] > 0) + channel = el[2]; + +exit: + return ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); +} + static void at76_rx_tasklet(unsigned long param) { struct urb *urb = (struct urb *)param; @@ -1523,6 +1590,8 @@ static void at76_rx_tasklet(unsigned long param) rx_status.signal = buf->rssi; rx_status.flag |= RX_FLAG_DECRYPTED; rx_status.flag |= RX_FLAG_IV_STRIPPED; + rx_status.band = IEEE80211_BAND_2GHZ; + rx_status.freq = at76_guess_freq(priv); at76_dbg(DBG_MAC80211, "calling ieee80211_rx_irqsafe(): %d/%d", priv->rx_skb->len, priv->rx_skb->data_len); @@ -1721,7 +1790,7 @@ static void at76_mac80211_tx(struct ieee80211_hw *hw, * following workaround is necessary. If the TX frame is an * authentication frame extract the bssid and send the CMD_JOIN. */ if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) { - if (!ether_addr_equal(priv->bssid, mgmt->bssid)) { + if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) { memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); ieee80211_queue_work(hw, &priv->work_join_bssid); dev_kfree_skb_any(skb); @@ -1875,6 +1944,8 @@ static void at76_dwork_hw_scan(struct work_struct *work) if (is_valid_ether_addr(priv->bssid)) at76_join(priv); + priv->scanning = false; + mutex_unlock(&priv->mtx); ieee80211_scan_completed(priv->hw, false); @@ -1929,6 +2000,7 @@ static int at76_hw_scan(struct ieee80211_hw *hw, goto exit; } + priv->scanning = true; ieee80211_queue_delayed_work(priv->hw, &priv->dwork_hw_scan, SCAN_POLL_INTERVAL); @@ -2020,6 +2092,44 @@ static void at76_configure_filter(struct ieee80211_hw *hw, ieee80211_queue_work(hw, &priv->work_set_promisc); } +static int at76_set_wep(struct at76_priv *priv) +{ + int ret = 0; + struct mib_mac_wep *mib_data = &priv->mib_buf.data.wep_mib; + + priv->mib_buf.type = MIB_MAC_WEP; + priv->mib_buf.size = sizeof(struct mib_mac_wep); + priv->mib_buf.index = 0; + + memset(mib_data, 0, sizeof(*mib_data)); + + if (priv->wep_enabled) { + if (priv->wep_keys_len[priv->wep_key_id] > WEP_SMALL_KEY_LEN) + mib_data->encryption_level = 2; + else + mib_data->encryption_level = 1; + + /* always exclude unencrypted if WEP is active */ + mib_data->exclude_unencrypted = 1; + } else { + mib_data->exclude_unencrypted = 0; + mib_data->encryption_level = 0; + } + + mib_data->privacy_invoked = priv->wep_enabled; + mib_data->wep_default_key_id = priv->wep_key_id; + memcpy(mib_data->wep_default_keyvalue, priv->wep_keys, + sizeof(priv->wep_keys)); + + ret = at76_set_mib(priv, &priv->mib_buf); + + if (ret < 0) + wiphy_err(priv->hw->wiphy, + "set_mib (wep) failed: %d\n", ret); + + return ret; +} + static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) @@ -2062,7 +2172,7 @@ static int at76_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, priv->wep_enabled = 1; } - at76_startup_device(priv); + at76_set_wep(priv); mutex_unlock(&priv->mtx); @@ -2112,7 +2222,6 @@ static struct at76_priv *at76_alloc_new_device(struct usb_device *udev) priv->pm_period = 0; /* unit us */ - priv->hw->channel_change_time = 100000; return priv; } @@ -2331,16 +2440,22 @@ static int at76_probe(struct usb_interface *interface, struct usb_device *udev; int op_mode; int need_ext_fw = 0; - struct mib_fw_version fwv; + struct mib_fw_version *fwv = NULL; int board_type = (int)id->driver_info; udev = usb_get_dev(interface_to_usbdev(interface)); + fwv = kmalloc(sizeof(*fwv), GFP_KERNEL); + if (!fwv) { + ret = -ENOMEM; + goto exit; + } + /* Load firmware into kernel memory */ fwe = at76_load_firmware(udev, board_type); if (!fwe) { ret = -ENOENT; - goto error; + goto exit; } op_mode = at76_get_op_mode(udev); @@ -2354,7 +2469,7 @@ static int at76_probe(struct usb_interface *interface, dev_err(&interface->dev, "cannot handle a device in HW_CONFIG_MODE\n"); ret = -EBUSY; - goto error; + goto exit; } if (op_mode != OPMODE_NORMAL_NIC_WITH_FLASH @@ -2367,10 +2482,10 @@ static int at76_probe(struct usb_interface *interface, dev_err(&interface->dev, "error %d downloading internal firmware\n", ret); - goto error; + goto exit; } usb_put_dev(udev); - return ret; + goto exit; } /* Internal firmware already inside the device. Get firmware @@ -2383,8 +2498,8 @@ static int at76_probe(struct usb_interface *interface, * query the device for the fw version */ if ((fwe->fw_version.major > 0 || fwe->fw_version.minor >= 100) || (op_mode == OPMODE_NORMAL_NIC_WITH_FLASH)) { - ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); - if (ret < 0 || (fwv.major | fwv.minor) == 0) + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); + if (ret < 0 || (fwv->major | fwv->minor) == 0) need_ext_fw = 1; } else /* No way to check firmware version, reload to be sure */ @@ -2395,37 +2510,37 @@ static int at76_probe(struct usb_interface *interface, "downloading external firmware\n"); ret = at76_load_external_fw(udev, fwe); - if (ret) - goto error; + if (ret < 0) + goto exit; /* Re-check firmware version */ - ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); + ret = at76_get_mib(udev, MIB_FW_VERSION, fwv, sizeof(*fwv)); if (ret < 0) { dev_err(&interface->dev, "error %d getting firmware version\n", ret); - goto error; + goto exit; } } priv = at76_alloc_new_device(udev); if (!priv) { ret = -ENOMEM; - goto error; + goto exit; } usb_set_intfdata(interface, priv); - memcpy(&priv->fw_version, &fwv, sizeof(struct mib_fw_version)); + memcpy(&priv->fw_version, fwv, sizeof(struct mib_fw_version)); priv->board_type = board_type; ret = at76_init_new_device(priv, interface); if (ret < 0) at76_delete_device(priv); - return ret; - -error: - usb_put_dev(udev); +exit: + kfree(fwv); + if (ret < 0) + usb_put_dev(udev); return ret; } diff --git a/drivers/net/wireless/at76c50x-usb.h b/drivers/net/wireless/at76c50x-usb.h index f14a65473fe..55090a38ac9 100644 --- a/drivers/net/wireless/at76c50x-usb.h +++ b/drivers/net/wireless/at76c50x-usb.h @@ -219,18 +219,6 @@ struct at76_req_join { u8 reserved; } __packed; -struct set_mib_buffer { - u8 type; - u8 size; - u8 index; - u8 reserved; - union { - u8 byte; - __le16 word; - u8 addr[ETH_ALEN]; - } data; -} __packed; - struct mib_local { u16 reserved0; u8 beacon_enable; @@ -334,6 +322,19 @@ struct mib_mdomain { u8 channel_list[14]; /* 0 for invalid channels */ } __packed; +struct set_mib_buffer { + u8 type; + u8 size; + u8 index; + u8 reserved; + union { + u8 byte; + __le16 word; + u8 addr[ETH_ALEN]; + struct mib_mac_wep wep_mib; + } data; +} __packed; + struct at76_fw_header { __le32 crc; /* CRC32 of the whole image */ __le32 board_type; /* firmware compatibility code */ @@ -417,6 +418,7 @@ struct at76_priv { int scan_max_time; /* scan max channel time */ int scan_mode; /* SCAN_TYPE_ACTIVE, SCAN_TYPE_PASSIVE */ int scan_need_any; /* if set, need to scan for any ESSID */ + bool scanning; /* if set, the scan is running */ u16 assoc_id; /* current association ID, if associated */ diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1abf1d42117..c63d1159db5 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -25,6 +25,23 @@ config ATH_DEBUG Say Y, if you want to debug atheros wireless drivers. Right now only ath9k makes use of this. +config ATH_REG_DYNAMIC_USER_REG_HINTS + bool "Atheros dynamic user regulatory hints" + depends on CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled in countries where + this feature is explicitly allowed and only on cards that + specifically have been tested for this. + +config ATH_REG_DYNAMIC_USER_CERT_TESTING + bool "Atheros dynamic user regulatory testing" + depends on ATH_REG_DYNAMIC_USER_REG_HINTS && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems + undergoing certification testing. + source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" @@ -32,5 +49,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" source "drivers/net/wireless/ath/ath10k/Kconfig" +source "drivers/net/wireless/ath/wcn36xx/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index fb05cfd1936..7d023b0f13b 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -5,13 +5,16 @@ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH10K) += ath10k/ +obj-$(CONFIG_WCN36XX) += wcn36xx/ obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ regd.o \ hw.o \ - key.o + key.o \ + dfs_pattern_detector.o \ + dfs_pri_detector.o ath-$(CONFIG_ATH_DEBUG) += debug.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 17d7fece35d..f92050617ae 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -25,7 +25,6 @@ * that and only has minimal functionality. */ #include <linux/compiler.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/list.h> @@ -1091,7 +1090,8 @@ static int ar5523_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static void ar5523_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void ar5523_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ar5523 *ar = hw->priv; @@ -1762,9 +1762,10 @@ static struct usb_device_id ar5523_id_table[] = { AR5523_DEVICE_UX(0x2001, 0x3a00), /* Dlink / DWLAG132 */ AR5523_DEVICE_UG(0x2001, 0x3a02), /* Dlink / DWLG132 */ AR5523_DEVICE_UX(0x2001, 0x3a04), /* Dlink / DWLAG122 */ + AR5523_DEVICE_UG(0x07d1, 0x3a07), /* D-Link / WUA-2340 rev A1 */ AR5523_DEVICE_UG(0x1690, 0x0712), /* Gigaset / AR5523 */ AR5523_DEVICE_UG(0x1690, 0x0710), /* Gigaset / SMCWUSBTG */ - AR5523_DEVICE_UG(0x129b, 0x160c), /* Gigaset / USB stick 108 + AR5523_DEVICE_UG(0x129b, 0x160b), /* Gigaset / USB stick 108 (CyberTAN Technology) */ AR5523_DEVICE_UG(0x16ab, 0x7801), /* Globalsun / AR5523_1 */ AR5523_DEVICE_UX(0x16ab, 0x7811), /* Globalsun / AR5523_2 */ diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index e0ba7cd1425..a889fd66fc6 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -17,6 +17,7 @@ #ifndef ATH_H #define ATH_H +#include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/if_ether.h> #include <linux/spinlock.h> @@ -55,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, @@ -62,7 +72,7 @@ enum ath_bus_type { }; struct reg_dmn_pair_mapping { - u16 regDmnEnum; + u16 reg_domain; u16 reg_5ghz_ctl; u16 reg_2ghz_ctl; }; @@ -129,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; @@ -160,11 +171,15 @@ struct ath_common { bool btcoex_enabled; bool disable_ani; bool bt_ant_diversity; + + int last_rssi; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; }; struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, u32 len, gfp_t gfp_mask); +bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr); void ath_hw_setbssidmask(struct ath_common *common); void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf *key); diff --git a/drivers/net/wireless/ath/ath10k/Kconfig b/drivers/net/wireless/ath/ath10k/Kconfig index 82e8088ca9b..a6f5285235a 100644 --- a/drivers/net/wireless/ath/ath10k/Kconfig +++ b/drivers/net/wireless/ath/ath10k/Kconfig @@ -37,3 +37,10 @@ config ATH10K_TRACING ---help--- Select this to ath10k use tracing infrastructure. +config ATH10K_DFS_CERTIFIED + bool "Atheros DFS support for certified platforms" + depends on ATH10K && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + This option enables DFS support for initiating radiation on + ath10k. diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 744da6d1c40..17d221abd58 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -22,7 +22,8 @@ void ath10k_bmi_start(struct ath10k *ar) { - ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n"); + ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n"); + ar->bmi.done_sent = false; } @@ -32,8 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar) u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n"); + if (ar->bmi.done_sent) { - ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__); + ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n"); return 0; } @@ -46,7 +49,6 @@ int ath10k_bmi_done(struct ath10k *ar) return ret; } - ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n"); return 0; } @@ -59,6 +61,8 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, u32 resplen = sizeof(resp.get_target_info); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n"); + if (ar->bmi.done_sent) { ath10k_warn("BMI Get Target Info Command disallowed\n"); return -EBUSY; @@ -80,6 +84,7 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, target_info->version = __le32_to_cpu(resp.get_target_info.version); target_info->type = __le32_to_cpu(resp.get_target_info.type); + return 0; } @@ -92,15 +97,14 @@ int ath10k_bmi_read_memory(struct ath10k *ar, u32 rxlen; int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", + address, length); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; } - ath10k_dbg(ATH10K_DBG_CORE, - "%s: (device: 0x%p, address: 0x%x, length: %d)\n", - __func__, ar, address, length); - while (length) { rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE); @@ -133,15 +137,14 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 txlen; int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", + address, length); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; } - ath10k_dbg(ATH10K_DBG_CORE, - "%s: (device: 0x%p, address: 0x%x, length: %d)\n", - __func__, ar, address, length); - while (length) { txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); @@ -172,7 +175,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, return 0; } -int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) +int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) { struct bmi_cmd cmd; union bmi_resp resp; @@ -180,18 +183,17 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) u32 resplen = sizeof(resp.execute); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", + address, param); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; } - ath10k_dbg(ATH10K_DBG_CORE, - "%s: (device: 0x%p, address: 0x%x, param: %d)\n", - __func__, ar, address, *param); - cmd.id = __cpu_to_le32(BMI_EXECUTE); cmd.execute.addr = __cpu_to_le32(address); - cmd.execute.param = __cpu_to_le32(*param); + cmd.execute.param = __cpu_to_le32(param); ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); if (ret) { @@ -202,10 +204,13 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) if (resplen < sizeof(resp.execute)) { ath10k_warn("invalid execute response length (%d)\n", resplen); - return ret; + return -EIO; } - *param = __le32_to_cpu(resp.execute.result); + *result = __le32_to_cpu(resp.execute.result); + + ath10k_dbg(ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result); + return 0; } @@ -216,6 +221,9 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) u32 txlen; int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", + buffer, length); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; @@ -250,6 +258,9 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", + address); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; @@ -275,6 +286,10 @@ int ath10k_bmi_fast_download(struct ath10k *ar, u32 trailer_len = length - head_len; int ret; + ath10k_dbg(ATH10K_DBG_BMI, + "bmi fast download address 0x%x buffer 0x%p length %d\n", + address, buffer, length); + ret = ath10k_bmi_lz_stream_start(ar, address); if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath10k/bmi.h b/drivers/net/wireless/ath/ath10k/bmi.h index 8d81ce1cec2..111ab701465 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.h +++ b/drivers/net/wireless/ath/ath10k/bmi.h @@ -201,7 +201,8 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, \ addr = host_interest_item_address(HI_ITEM(item)); \ ret = ath10k_bmi_read_memory(ar, addr, (u8 *)&tmp, 4); \ - *val = __le32_to_cpu(tmp); \ + if (!ret) \ + *val = __le32_to_cpu(tmp); \ ret; \ }) @@ -217,7 +218,7 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 address, ret; \ }) -int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param); +int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result); int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address); int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length); int ath10k_bmi_fast_download(struct ath10k *ar, u32 address, diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index f8b969f518f..d185dc0cd12 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -76,36 +76,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *indicator_addr; - - if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { - ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); - return; - } - - /* workaround for QCA988x_1.0 HW CE */ - indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS; - - if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) { - iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr); - } else { - unsigned long irq_flags; - local_irq_save(irq_flags); - iowrite32(1, indicator_addr); - - /* - * PCIE write waits for ACK in IPQ8K, there is no - * need to read back value. - */ - (void)ioread32(indicator_addr); - (void)ioread32(indicator_addr); /* conservative */ - - ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); - - iowrite32(0, indicator_addr); - local_irq_restore(irq_flags); - } + ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); } static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, @@ -272,6 +243,16 @@ static inline void ath10k_ce_error_intr_enable(struct ath10k *ar, misc_ie_addr | CE_ERROR_MASK); } +static inline void ath10k_ce_error_intr_disable(struct ath10k *ar, + u32 ce_ctrl_addr) +{ + u32 misc_ie_addr = ath10k_pci_read32(ar, + ce_ctrl_addr + MISC_IE_ADDRESS); + + ath10k_pci_write32(ar, ce_ctrl_addr + MISC_IE_ADDRESS, + misc_ie_addr & ~CE_ERROR_MASK); +} + static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int mask) @@ -285,15 +266,15 @@ 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 ce_state *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 ce_ring_state *src_ring = ce_state->src_ring; + struct ath10k_ce_ring *src_ring = ce_state->src_ring; struct ce_desc *desc, *sdesc; unsigned int nentries_mask = src_ring->nentries_mask; unsigned int sw_index = src_ring->sw_index; @@ -306,11 +287,13 @@ static int ath10k_ce_send_nolock(struct ce_state *ce_state, ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n", __func__, nbytes, ce_state->src_sz_max); - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + return ret; if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) <= 0)) { - ret = -EIO; + ret = -ENOSR; goto exit; } @@ -346,7 +329,34 @@ exit: return ret; } -int ath10k_ce_send(struct ce_state *ce_state, +void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe) +{ + struct ath10k *ar = pipe->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_ring *src_ring = pipe->src_ring; + u32 ctrl_addr = pipe->ctrl_addr; + + lockdep_assert_held(&ar_pci->ce_lock); + + /* + * This function must be called only if there is an incomplete + * scatter-gather transfer (before index register is updated) + * that needs to be cleaned up. + */ + if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index)) + return; + + if (WARN_ON_ONCE(src_ring->write_index == + ath10k_ce_src_ring_write_index_get(ar, ctrl_addr))) + return; + + src_ring->write_index--; + src_ring->write_index &= src_ring->nentries_mask; + + src_ring->per_transfer_context[src_ring->write_index] = NULL; +} + +int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, void *per_transfer_context, u32 buffer, unsigned int nbytes, @@ -365,77 +375,26 @@ int ath10k_ce_send(struct ce_state *ce_state, return ret; } -void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer, - unsigned int nbytes, u32 flags) -{ - unsigned int num_items = sendlist->num_items; - struct ce_sendlist_item *item; - - item = &sendlist->item[num_items]; - item->data = buffer; - item->u.nbytes = nbytes; - item->flags = flags; - sendlist->num_items++; -} - -int ath10k_ce_sendlist_send(struct ce_state *ce_state, - void *per_transfer_context, - struct ce_sendlist *sendlist, - unsigned int transfer_id) +int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) { - struct ce_ring_state *src_ring = ce_state->src_ring; - struct ce_sendlist_item *item; - struct ath10k *ar = ce_state->ar; + struct ath10k *ar = pipe->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - unsigned int nentries_mask = src_ring->nentries_mask; - unsigned int num_items = sendlist->num_items; - unsigned int sw_index; - unsigned int write_index; - int i, delta, ret = -ENOMEM; + int delta; spin_lock_bh(&ar_pci->ce_lock); - - sw_index = src_ring->sw_index; - write_index = src_ring->write_index; - - delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); - - if (delta >= num_items) { - /* - * Handle all but the last item uniformly. - */ - for (i = 0; i < num_items - 1; i++) { - item = &sendlist->item[i]; - ret = ath10k_ce_send_nolock(ce_state, - CE_SENDLIST_ITEM_CTXT, - (u32) item->data, - item->u.nbytes, transfer_id, - item->flags | - CE_SEND_FLAG_GATHER); - if (ret) - ath10k_warn("CE send failed for item: %d\n", i); - } - /* - * Provide valid context pointer for final item. - */ - item = &sendlist->item[i]; - ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, - (u32) item->data, item->u.nbytes, - transfer_id, item->flags); - if (ret) - ath10k_warn("CE send failed for last item: %d\n", i); - } - + delta = CE_RING_DELTA(pipe->src_ring->nentries_mask, + pipe->src_ring->write_index, + pipe->src_ring->sw_index - 1); spin_unlock_bh(&ar_pci->ce_lock); - return ret; + return delta; } -int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, +int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, void *per_recv_context, u32 buffer) { - struct ce_ring_state *dest_ring = ce_state->dest_ring; + struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -448,7 +407,9 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, write_index = dest_ring->write_index; sw_index = dest_ring->sw_index; - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + goto out; if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) { struct ce_desc *base = dest_ring->base_addr_owner_space; @@ -470,6 +431,8 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, ret = -EIO; } ath10k_pci_sleep(ar); + +out: spin_unlock_bh(&ar_pci->ce_lock); return ret; @@ -479,14 +442,14 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, * Guts of ath10k_ce_completed_recv_next. * The caller takes responsibility for any necessary locking. */ -static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state, +static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp, unsigned int *flagsp) { - struct ce_ring_state *dest_ring = ce_state->dest_ring; + struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; unsigned int sw_index = dest_ring->sw_index; @@ -535,7 +498,7 @@ static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state, return 0; } -int ath10k_ce_completed_recv_next(struct ce_state *ce_state, +int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -556,11 +519,11 @@ int ath10k_ce_completed_recv_next(struct ce_state *ce_state, return ret; } -int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, +int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp) { - struct ce_ring_state *dest_ring; + struct ath10k_ce_ring *dest_ring; unsigned int nentries_mask; unsigned int sw_index; unsigned int write_index; @@ -612,19 +575,20 @@ int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, * Guts of ath10k_ce_completed_send_next. * The caller takes responsibility for any necessary locking. */ -static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, +static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp) { - struct ce_ring_state *src_ring = ce_state->src_ring; + struct ath10k_ce_ring *src_ring = ce_state->src_ring; u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; unsigned int nentries_mask = src_ring->nentries_mask; unsigned int sw_index = src_ring->sw_index; + struct ce_desc *sdesc, *sbase; unsigned int read_index; - int ret = -EIO; + int ret; if (src_ring->hw_index == sw_index) { /* @@ -634,48 +598,54 @@ static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, * the SW has really caught up to the HW, or if the cached * value of the HW index has become stale. */ - ath10k_pci_wake(ar); + + ret = ath10k_pci_wake(ar); + if (ret) + return ret; + src_ring->hw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); src_ring->hw_index &= nentries_mask; + ath10k_pci_sleep(ar); } + read_index = src_ring->hw_index; - if ((read_index != sw_index) && (read_index != 0xffffffff)) { - struct ce_desc *sbase = src_ring->shadow_base; - struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); + if ((read_index == sw_index) || (read_index == 0xffffffff)) + return -EIO; - /* Return data from completed source descriptor */ - *bufferp = __le32_to_cpu(sdesc->addr); - *nbytesp = __le16_to_cpu(sdesc->nbytes); - *transfer_idp = MS(__le16_to_cpu(sdesc->flags), - CE_DESC_FLAGS_META_DATA); + sbase = src_ring->shadow_base; + sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); - if (per_transfer_contextp) - *per_transfer_contextp = - src_ring->per_transfer_context[sw_index]; + /* Return data from completed source descriptor */ + *bufferp = __le32_to_cpu(sdesc->addr); + *nbytesp = __le16_to_cpu(sdesc->nbytes); + *transfer_idp = MS(__le16_to_cpu(sdesc->flags), + CE_DESC_FLAGS_META_DATA); - /* sanity */ - src_ring->per_transfer_context[sw_index] = NULL; + if (per_transfer_contextp) + *per_transfer_contextp = + src_ring->per_transfer_context[sw_index]; - /* Update sw_index */ - sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); - src_ring->sw_index = sw_index; - ret = 0; - } + /* sanity */ + src_ring->per_transfer_context[sw_index] = NULL; - return ret; + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + src_ring->sw_index = sw_index; + + return 0; } /* NB: Modeled after ath10k_ce_completed_send_next */ -int ath10k_ce_cancel_send_next(struct ce_state *ce_state, +int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp) { - struct ce_ring_state *src_ring; + struct ath10k_ce_ring *src_ring; unsigned int nentries_mask; unsigned int sw_index; unsigned int write_index; @@ -727,7 +697,7 @@ int ath10k_ce_cancel_send_next(struct ce_state *ce_state, return ret; } -int ath10k_ce_completed_send_next(struct ce_state *ce_state, +int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -756,53 +726,29 @@ int ath10k_ce_completed_send_next(struct ce_state *ce_state, void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; u32 ctrl_addr = ce_state->ctrl_addr; - void *transfer_context; - u32 buf; - unsigned int nbytes; - unsigned int id; - unsigned int flags; + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) + return; - ath10k_pci_wake(ar); spin_lock_bh(&ar_pci->ce_lock); /* Clear the copy-complete interrupts that will be handled here. */ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, HOST_IS_COPY_COMPLETE_MASK); - if (ce_state->recv_cb) { - /* - * Pop completed recv buffers and call the registered - * recv callback for each - */ - while (ath10k_ce_completed_recv_next_nolock(ce_state, - &transfer_context, - &buf, &nbytes, - &id, &flags) == 0) { - spin_unlock_bh(&ar_pci->ce_lock); - ce_state->recv_cb(ce_state, transfer_context, buf, - nbytes, id, flags); - spin_lock_bh(&ar_pci->ce_lock); - } - } + spin_unlock_bh(&ar_pci->ce_lock); - if (ce_state->send_cb) { - /* - * Pop completed send buffers and call the registered - * send callback for each - */ - while (ath10k_ce_completed_send_next_nolock(ce_state, - &transfer_context, - &buf, - &nbytes, - &id) == 0) { - spin_unlock_bh(&ar_pci->ce_lock); - ce_state->send_cb(ce_state, transfer_context, - buf, nbytes, id); - spin_lock_bh(&ar_pci->ce_lock); - } - } + if (ce_state->recv_cb) + ce_state->recv_cb(ce_state); + + if (ce_state->send_cb) + ce_state->send_cb(ce_state); + + spin_lock_bh(&ar_pci->ce_lock); /* * Misc CE interrupts are not being handled, but still need @@ -822,14 +768,16 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) void ath10k_ce_per_engine_service_any(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ce_id; + int ce_id, ret; u32 intr_summary; - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + return; + intr_summary = CE_INTERRUPT_SUMMARY(ar); - for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) { + for (ce_id = 0; intr_summary && (ce_id < CE_COUNT); ce_id++) { if (intr_summary & (1 << ce_id)) intr_summary &= ~(1 << ce_id); else @@ -849,13 +797,16 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) * * Called with ce_lock held. */ -static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state, +static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state, int disable_copy_compl_intr) { u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; + int ret; - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + return; if ((!disable_copy_compl_intr) && (ce_state->send_cb || ce_state->recv_cb)) @@ -868,27 +819,29 @@ static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state, ath10k_pci_sleep(ar); } -void ath10k_ce_disable_interrupts(struct ath10k *ar) +int ath10k_ce_disable_interrupts(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ce_id; + int ce_id, ret; - ath10k_pci_wake(ar); - for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) { - struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; - u32 ctrl_addr = ce_state->ctrl_addr; + ret = ath10k_pci_wake(ar); + if (ret) + return ret; + + for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { + u32 ctrl_addr = ath10k_ce_base_address(ce_id); ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); + ath10k_ce_error_intr_disable(ar, ctrl_addr); + ath10k_ce_watermark_intr_disable(ar, ctrl_addr); } + ath10k_pci_sleep(ar); + + return 0; } -void ath10k_ce_send_cb_register(struct ce_state *ce_state, - void (*send_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id), +void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, + void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts) { struct ath10k *ar = ce_state->ar; @@ -900,13 +853,8 @@ void ath10k_ce_send_cb_register(struct ce_state *ce_state, spin_unlock_bh(&ar_pci->ce_lock); } -void ath10k_ce_recv_cb_register(struct ce_state *ce_state, - void (*recv_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags)) +void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, + void (*recv_cb)(struct ath10k_ce_pipe *)) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -919,37 +867,18 @@ void ath10k_ce_recv_cb_register(struct ce_state *ce_state, static int ath10k_ce_init_src_ring(struct ath10k *ar, unsigned int ce_id, - struct ce_state *ce_state, const struct ce_attr *attr) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_ring_state *src_ring; - unsigned int nentries = attr->src_nentries; - unsigned int ce_nbytes; - u32 ctrl_addr = ath10k_ce_base_address(ce_id); - dma_addr_t base_addr; - char *ptr; - - nentries = roundup_pow_of_two(nentries); - - if (ce_state->src_ring) { - WARN_ON(ce_state->src_ring->nentries != nentries); - return 0; - } - - ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); - ptr = kzalloc(ce_nbytes, GFP_KERNEL); - if (ptr == NULL) - return -ENOMEM; + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce_ring *src_ring = ce_state->src_ring; + u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id); - ce_state->src_ring = (struct ce_ring_state *)ptr; - src_ring = ce_state->src_ring; + nentries = roundup_pow_of_two(attr->src_nentries); - ptr += sizeof(struct ce_ring_state); - src_ring->nentries = nentries; - src_ring->nentries_mask = nentries - 1; + memset(src_ring->per_transfer_context, 0, + nentries * sizeof(*src_ring->per_transfer_context)); - ath10k_pci_wake(ar); src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); src_ring->sw_index &= src_ring->nentries_mask; src_ring->hw_index = src_ring->sw_index; @@ -957,19 +886,90 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, src_ring->write_index = ath10k_ce_src_ring_write_index_get(ar, ctrl_addr); src_ring->write_index &= src_ring->nentries_mask; - ath10k_pci_sleep(ar); - src_ring->per_transfer_context = (void **)ptr; + ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, + src_ring->base_addr_ce_space); + ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries); + ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max); + ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); + + ath10k_dbg(ATH10K_DBG_BOOT, + "boot init ce src ring id %d entries %d base_addr %p\n", + ce_id, nentries, src_ring->base_addr_owner_space); + + return 0; +} + +static int ath10k_ce_init_dest_ring(struct ath10k *ar, + unsigned int ce_id, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; + u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id); + + nentries = roundup_pow_of_two(attr->dest_nentries); + + memset(dest_ring->per_transfer_context, 0, + nentries * sizeof(*dest_ring->per_transfer_context)); + + dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr); + dest_ring->sw_index &= dest_ring->nentries_mask; + dest_ring->write_index = + ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr); + dest_ring->write_index &= dest_ring->nentries_mask; + + ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, + dest_ring->base_addr_ce_space); + ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries); + ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); + + ath10k_dbg(ATH10K_DBG_BOOT, + "boot ce dest ring id %d entries %d base_addr %p\n", + ce_id, nentries, dest_ring->base_addr_owner_space); + + return 0; +} + +static struct ath10k_ce_ring * +ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id, + const struct ce_attr *attr) +{ + struct ath10k_ce_ring *src_ring; + u32 nentries = attr->src_nentries; + dma_addr_t base_addr; + + nentries = roundup_pow_of_two(nentries); + + src_ring = kzalloc(sizeof(*src_ring) + + (nentries * + sizeof(*src_ring->per_transfer_context)), + GFP_KERNEL); + if (src_ring == NULL) + return ERR_PTR(-ENOMEM); + + src_ring->nentries = nentries; + src_ring->nentries_mask = nentries - 1; /* * Legacy platforms that do not support cache * coherent DMA are unsupported */ src_ring->base_addr_owner_space_unaligned = - pci_alloc_consistent(ar_pci->pdev, - (nentries * sizeof(struct ce_desc) + - CE_DESC_RING_ALIGN), - &base_addr); + dma_alloc_coherent(ar->dev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + &base_addr, GFP_KERNEL); + if (!src_ring->base_addr_owner_space_unaligned) { + kfree(src_ring); + return ERR_PTR(-ENOMEM); + } + src_ring->base_addr_ce_space_unaligned = base_addr; src_ring->base_addr_owner_space = PTR_ALIGN( @@ -986,75 +986,57 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, src_ring->shadow_base_unaligned = kmalloc((nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN), GFP_KERNEL); + if (!src_ring->shadow_base_unaligned) { + dma_free_coherent(ar->dev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + src_ring->base_addr_owner_space, + src_ring->base_addr_ce_space); + kfree(src_ring); + return ERR_PTR(-ENOMEM); + } src_ring->shadow_base = PTR_ALIGN( src_ring->shadow_base_unaligned, CE_DESC_RING_ALIGN); - ath10k_pci_wake(ar); - ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, - src_ring->base_addr_ce_space); - ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries); - ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, attr->src_sz_max); - ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0); - ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0); - ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); - ath10k_pci_sleep(ar); - - return 0; + return src_ring; } -static int ath10k_ce_init_dest_ring(struct ath10k *ar, - unsigned int ce_id, - struct ce_state *ce_state, - const struct ce_attr *attr) +static struct ath10k_ce_ring * +ath10k_ce_alloc_dest_ring(struct ath10k *ar, unsigned int ce_id, + const struct ce_attr *attr) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_ring_state *dest_ring; - unsigned int nentries = attr->dest_nentries; - unsigned int ce_nbytes; - u32 ctrl_addr = ath10k_ce_base_address(ce_id); + struct ath10k_ce_ring *dest_ring; + u32 nentries; dma_addr_t base_addr; - char *ptr; - - nentries = roundup_pow_of_two(nentries); - - if (ce_state->dest_ring) { - WARN_ON(ce_state->dest_ring->nentries != nentries); - return 0; - } - ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); - ptr = kzalloc(ce_nbytes, GFP_KERNEL); - if (ptr == NULL) - return -ENOMEM; + nentries = roundup_pow_of_two(attr->dest_nentries); - ce_state->dest_ring = (struct ce_ring_state *)ptr; - dest_ring = ce_state->dest_ring; + dest_ring = kzalloc(sizeof(*dest_ring) + + (nentries * + sizeof(*dest_ring->per_transfer_context)), + GFP_KERNEL); + if (dest_ring == NULL) + return ERR_PTR(-ENOMEM); - ptr += sizeof(struct ce_ring_state); dest_ring->nentries = nentries; dest_ring->nentries_mask = nentries - 1; - ath10k_pci_wake(ar); - dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr); - dest_ring->sw_index &= dest_ring->nentries_mask; - dest_ring->write_index = - ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr); - dest_ring->write_index &= dest_ring->nentries_mask; - ath10k_pci_sleep(ar); - - dest_ring->per_transfer_context = (void **)ptr; - /* * Legacy platforms that do not support cache * coherent DMA are unsupported */ dest_ring->base_addr_owner_space_unaligned = - pci_alloc_consistent(ar_pci->pdev, - (nentries * sizeof(struct ce_desc) + - CE_DESC_RING_ALIGN), - &base_addr); + dma_alloc_coherent(ar->dev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + &base_addr, GFP_KERNEL); + if (!dest_ring->base_addr_owner_space_unaligned) { + kfree(dest_ring); + return ERR_PTR(-ENOMEM); + } + dest_ring->base_addr_ce_space_unaligned = base_addr; /* @@ -1071,124 +1053,161 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, dest_ring->base_addr_ce_space_unaligned, CE_DESC_RING_ALIGN); - ath10k_pci_wake(ar); - ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, - dest_ring->base_addr_ce_space); - ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries); - ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0); - ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0); - ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); - ath10k_pci_sleep(ar); - - return 0; + return dest_ring; } -static struct ce_state *ath10k_ce_init_state(struct ath10k *ar, - unsigned int ce_id, - const struct ce_attr *attr) +/* + * Initialize a Copy Engine based on caller-supplied attributes. + * This may be called once to initialize both source and destination + * rings or it may be called twice for separate source and destination + * initialization. It may be that only one side or the other is + * initialized by software/firmware. + */ +int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, + const struct ce_attr *attr) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_state = NULL; - u32 ctrl_addr = ath10k_ce_base_address(ce_id); + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + int ret; + + /* + * Make sure there's enough CE ringbuffer entries for HTT TX to avoid + * additional TX locking checks. + * + * For the lack of a better place do the check here. + */ + BUILD_BUG_ON(2*TARGET_NUM_MSDU_DESC > + (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); + BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC > + (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); + + ret = ath10k_pci_wake(ar); + if (ret) + return ret; spin_lock_bh(&ar_pci->ce_lock); + ce_state->ar = ar; + ce_state->id = ce_id; + ce_state->ctrl_addr = ath10k_ce_base_address(ce_id); + ce_state->attr_flags = attr->flags; + ce_state->src_sz_max = attr->src_sz_max; + spin_unlock_bh(&ar_pci->ce_lock); - if (!ar_pci->ce_id_to_state[ce_id]) { - ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC); - if (ce_state == NULL) { - spin_unlock_bh(&ar_pci->ce_lock); - return NULL; + if (attr->src_nentries) { + ret = ath10k_ce_init_src_ring(ar, ce_id, attr); + if (ret) { + ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n", + ce_id, ret); + goto out; } + } - ar_pci->ce_id_to_state[ce_id] = ce_state; - ce_state->ar = ar; - ce_state->id = ce_id; - ce_state->ctrl_addr = ctrl_addr; - ce_state->state = CE_RUNNING; - /* Save attribute flags */ - ce_state->attr_flags = attr->flags; - ce_state->src_sz_max = attr->src_sz_max; + if (attr->dest_nentries) { + ret = ath10k_ce_init_dest_ring(ar, ce_id, attr); + if (ret) { + ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n", + ce_id, ret); + goto out; + } } - spin_unlock_bh(&ar_pci->ce_lock); +out: + ath10k_pci_sleep(ar); + return ret; +} + +static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id) +{ + u32 ctrl_addr = ath10k_ce_base_address(ce_id); - return ce_state; + ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_dmax_set(ar, ctrl_addr, 0); + ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, 0); } -/* - * Initialize a Copy Engine based on caller-supplied attributes. - * This may be called once to initialize both source and destination - * rings or it may be called twice for separate source and destination - * initialization. It may be that only one side or the other is - * initialized by software/firmware. - */ -struct ce_state *ath10k_ce_init(struct ath10k *ar, - unsigned int ce_id, - const struct ce_attr *attr) +static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id) { - struct ce_state *ce_state; u32 ctrl_addr = ath10k_ce_base_address(ce_id); - ce_state = ath10k_ce_init_state(ar, ce_id, attr); - if (!ce_state) { - ath10k_err("Failed to initialize CE state for ID: %d\n", ce_id); - return NULL; - } + ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0); + ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, 0); +} + +void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id) +{ + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) + return; + + ath10k_ce_deinit_src_ring(ar, ce_id); + ath10k_ce_deinit_dest_ring(ar, ce_id); + + ath10k_pci_sleep(ar); +} + +int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, + const struct ce_attr *attr) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; + int ret; if (attr->src_nentries) { - if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) { - ath10k_err("Failed to initialize CE src ring for ID: %d\n", - ce_id); - ath10k_ce_deinit(ce_state); - return NULL; + ce_state->src_ring = ath10k_ce_alloc_src_ring(ar, ce_id, attr); + if (IS_ERR(ce_state->src_ring)) { + ret = PTR_ERR(ce_state->src_ring); + ath10k_err("failed to allocate copy engine source ring %d: %d\n", + ce_id, ret); + ce_state->src_ring = NULL; + return ret; } } if (attr->dest_nentries) { - if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) { - ath10k_err("Failed to initialize CE dest ring for ID: %d\n", - ce_id); - ath10k_ce_deinit(ce_state); - return NULL; + ce_state->dest_ring = ath10k_ce_alloc_dest_ring(ar, ce_id, + attr); + if (IS_ERR(ce_state->dest_ring)) { + ret = PTR_ERR(ce_state->dest_ring); + ath10k_err("failed to allocate copy engine destination ring %d: %d\n", + ce_id, ret); + ce_state->dest_ring = NULL; + return ret; } } - /* Enable CE error interrupts */ - ath10k_pci_wake(ar); - ath10k_ce_error_intr_enable(ar, ctrl_addr); - ath10k_pci_sleep(ar); - - return ce_state; + return 0; } -void ath10k_ce_deinit(struct ce_state *ce_state) +void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id) { - unsigned int ce_id = ce_state->id; - struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - - ce_state->state = CE_UNUSED; - ar_pci->ce_id_to_state[ce_id] = NULL; + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; if (ce_state->src_ring) { kfree(ce_state->src_ring->shadow_base_unaligned); - pci_free_consistent(ar_pci->pdev, - (ce_state->src_ring->nentries * - sizeof(struct ce_desc) + - CE_DESC_RING_ALIGN), - ce_state->src_ring->base_addr_owner_space, - ce_state->src_ring->base_addr_ce_space); + dma_free_coherent(ar->dev, + (ce_state->src_ring->nentries * + sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + ce_state->src_ring->base_addr_owner_space, + ce_state->src_ring->base_addr_ce_space); kfree(ce_state->src_ring); } if (ce_state->dest_ring) { - pci_free_consistent(ar_pci->pdev, - (ce_state->dest_ring->nentries * - sizeof(struct ce_desc) + - CE_DESC_RING_ALIGN), - ce_state->dest_ring->base_addr_owner_space, - ce_state->dest_ring->base_addr_ce_space); + dma_free_coherent(ar->dev, + (ce_state->dest_ring->nentries * + sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + ce_state->dest_ring->base_addr_owner_space, + ce_state->dest_ring->base_addr_ce_space); kfree(ce_state->dest_ring); } - kfree(ce_state); + + ce_state->src_ring = NULL; + ce_state->dest_ring = NULL; } diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index c17f07c026f..7a5a36fc59c 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -23,11 +23,10 @@ /* 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 -#define CE_SENDLIST_ITEMS_MAX 12 #define CE_SEND_FLAG_GATHER 0x00010000 /* @@ -36,16 +35,9 @@ * how to use copy engines. */ -struct ce_state; +struct ath10k_ce_pipe; -/* Copy Engine operational state */ -enum ce_op_state { - CE_UNUSED, - CE_PAUSED, - CE_RUNNING, -}; - #define CE_DESC_FLAGS_GATHER (1 << 0) #define CE_DESC_FLAGS_BYTE_SWAP (1 << 1) #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC @@ -57,8 +49,7 @@ struct ce_desc { __le16 flags; /* %CE_DESC_FLAGS_ */ }; -/* Copy Engine Ring internal state */ -struct ce_ring_state { +struct ath10k_ce_ring { /* Number of entries in this ring; must be power of 2 */ unsigned int nentries; unsigned int nentries_mask; @@ -113,52 +104,24 @@ struct ce_ring_state { void *shadow_base_unaligned; struct ce_desc *shadow_base; - void **per_transfer_context; + /* keep last */ + void *per_transfer_context[0]; }; -/* Copy Engine internal state */ -struct ce_state { +struct ath10k_ce_pipe { struct ath10k *ar; unsigned int id; unsigned int attr_flags; u32 ctrl_addr; - enum ce_op_state state; - - void (*send_cb) (struct ce_state *ce_state, - void *per_transfer_send_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id); - void (*recv_cb) (struct ce_state *ce_state, - void *per_transfer_recv_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags); - - unsigned int src_sz_max; - struct ce_ring_state *src_ring; - struct ce_ring_state *dest_ring; -}; -struct ce_sendlist_item { - /* e.g. buffer or desc list */ - dma_addr_t data; - union { - /* simple buffer */ - unsigned int nbytes; - /* Rx descriptor list */ - unsigned int ndesc; - } u; - /* externally-specified flags; OR-ed with internal flags */ - u32 flags; -}; + void (*send_cb)(struct ath10k_ce_pipe *); + void (*recv_cb)(struct ath10k_ce_pipe *); -struct ce_sendlist { - unsigned int num_items; - struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX]; + unsigned int src_sz_max; + struct ath10k_ce_ring *src_ring; + struct ath10k_ce_ring *dest_ring; }; /* Copy Engine settable attributes */ @@ -182,7 +145,7 @@ struct ce_attr; * * Implementation note: pushes 1 buffer to Source ring */ -int ath10k_ce_send(struct ce_state *ce_state, +int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, void *per_transfer_send_context, u32 buffer, unsigned int nbytes, @@ -190,36 +153,20 @@ int ath10k_ce_send(struct ce_state *ce_state, unsigned int transfer_id, unsigned int flags); -void ath10k_ce_send_cb_register(struct ce_state *ce_state, - void (*send_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id), - int disable_interrupts); +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); -/* Append a simple buffer (address/length) to a sendlist. */ -void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, - u32 buffer, - unsigned int nbytes, - /* OR-ed with internal flags */ - u32 flags); +void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe); -/* - * Queue a "sendlist" of buffers to be sent using gather to a single - * anonymous destination buffer - * ce - which copy engine to use - * sendlist - list of simple buffers to send using gather - * transfer_id - arbitrary ID; reflected to destination - * Returns 0 on success; otherwise an error status. - * - * Implemenation note: Pushes multiple buffers with Gather to Source ring. - */ -int ath10k_ce_sendlist_send(struct ce_state *ce_state, - void *per_transfer_send_context, - struct ce_sendlist *sendlist, - /* 14 bits */ - unsigned int transfer_id); +void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, + void (*send_cb)(struct ath10k_ce_pipe *), + int disable_interrupts); + +int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe); /*==================Recv=======================*/ @@ -233,17 +180,12 @@ int ath10k_ce_sendlist_send(struct ce_state *ce_state, * * Implemenation note: Pushes a buffer to Dest ring. */ -int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, +int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, void *per_transfer_recv_context, u32 buffer); -void ath10k_ce_recv_cb_register(struct ce_state *ce_state, - void (*recv_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags)); +void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, + void (*recv_cb)(struct ath10k_ce_pipe *)); /* recv flags */ /* Data is byte-swapped */ @@ -253,7 +195,7 @@ void ath10k_ce_recv_cb_register(struct ce_state *ce_state, * Supply data for the next completed unprocessed receive descriptor. * Pops buffer from Dest ring. */ -int ath10k_ce_completed_recv_next(struct ce_state *ce_state, +int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -263,7 +205,7 @@ int ath10k_ce_completed_recv_next(struct ce_state *ce_state, * Supply data for the next completed unprocessed send descriptor. * Pops 1 completed send buffer from Source ring. */ -int ath10k_ce_completed_send_next(struct ce_state *ce_state, +int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -271,10 +213,12 @@ int ath10k_ce_completed_send_next(struct ce_state *ce_state, /*==================CE Engine Initialization=======================*/ -/* Initialize an instance of a CE */ -struct ce_state *ath10k_ce_init(struct ath10k *ar, - unsigned int ce_id, - const struct ce_attr *attr); +int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, + const struct ce_attr *attr); +void ath10k_ce_deinit_pipe(struct ath10k *ar, unsigned int ce_id); +int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, + const struct ce_attr *attr); +void ath10k_ce_free_pipe(struct ath10k *ar, int ce_id); /*==================CE Engine Shutdown=======================*/ /* @@ -282,7 +226,7 @@ struct ce_state *ath10k_ce_init(struct ath10k *ar, * receive buffers. Target DMA must be stopped before using * this API. */ -int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, +int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp); @@ -291,18 +235,16 @@ int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, * pending sends. Target DMA must be stopped before using * this API. */ -int ath10k_ce_cancel_send_next(struct ce_state *ce_state, +int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp); -void ath10k_ce_deinit(struct ce_state *ce_state); - /*==================CE Interrupt Handlers====================*/ void ath10k_ce_per_engine_service_any(struct ath10k *ar); void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id); -void ath10k_ce_disable_interrupts(struct ath10k *ar); +int ath10k_ce_disable_interrupts(struct ath10k *ar); /* ce_attr.flags values */ /* Use NonSnooping PCIe accesses? */ @@ -322,9 +264,6 @@ struct ce_attr { /* CE_ATTR_* values */ unsigned int flags; - /* currently not in use */ - unsigned int priority; - /* #entries in source ring - Must be a power of 2 */ unsigned int src_nentries; @@ -336,21 +275,8 @@ struct ce_attr { /* #entries in destination ring - Must be a power of 2 */ unsigned int dest_nentries; - - /* Future use */ - void *reserved; }; -/* - * When using sendlist_send to transfer multiple buffer fragments, the - * transfer context of each fragment, except last one, will be filled - * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for - * each fragment done with send and the transfer context would be - * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the - * status of a send completion. - */ -#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef) - #define SR_BA_ADDRESS 0x0000 #define SR_SIZE_ADDRESS 0x0004 #define DR_BA_ADDRESS 0x0008 diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7226c23b956..e6c56c5bb0f 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -39,17 +39,6 @@ MODULE_PARM_DESC(p2p, "Enable ath10k P2P support"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { { - .id = QCA988X_HW_1_0_VERSION, - .name = "qca988x hw1.0", - .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR, - .fw = { - .dir = QCA988X_HW_1_0_FW_DIR, - .fw = QCA988X_HW_1_0_FW_FILE, - .otp = QCA988X_HW_1_0_OTP_FILE, - .board = QCA988X_HW_1_0_BOARD_DATA_FILE, - }, - }, - { .id = QCA988X_HW_2_0_VERSION, .name = "qca988x hw2.0", .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, @@ -64,61 +53,9 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { static void ath10k_send_suspend_complete(struct ath10k *ar) { - ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__); - - ar->is_target_paused = true; - wake_up(&ar->event_queue); -} - -static int ath10k_check_fw_version(struct ath10k *ar) -{ - char version[32]; - - if (ar->fw_version_major >= SUPPORTED_FW_MAJOR && - ar->fw_version_minor >= SUPPORTED_FW_MINOR && - ar->fw_version_release >= SUPPORTED_FW_RELEASE && - ar->fw_version_build >= SUPPORTED_FW_BUILD) - return 0; - - snprintf(version, sizeof(version), "%u.%u.%u.%u", - SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR, - SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD); - - ath10k_warn("WARNING: Firmware version %s is not officially supported.\n", - ar->hw->wiphy->fw_version); - ath10k_warn("Please upgrade to version %s (or newer)\n", version); - - return 0; -} - -static int ath10k_init_connect_htc(struct ath10k *ar) -{ - int status; - - status = ath10k_wmi_connect_htc_service(ar); - if (status) - goto conn_fail; - - /* Start HTC */ - status = ath10k_htc_start(&ar->htc); - if (status) - goto conn_fail; + ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n"); - /* Wait for WMI event to be ready */ - status = ath10k_wmi_wait_for_service_ready(ar); - if (status <= 0) { - ath10k_warn("wmi service ready event not received"); - status = -ETIMEDOUT; - goto timeout; - } - - ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n"); - return 0; - -timeout: - ath10k_htc_stop(&ar->htc); -conn_fail: - return status; + complete(&ar->target_suspend); } static int ath10k_init_configure_target(struct ath10k *ar) @@ -200,8 +137,7 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, return fw; } -static int ath10k_push_board_ext_data(struct ath10k *ar, - const struct firmware *fw) +static int ath10k_push_board_ext_data(struct ath10k *ar) { u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; @@ -214,21 +150,21 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, return ret; } - ath10k_dbg(ATH10K_DBG_CORE, - "ath10k: Board extended Data download addr: 0x%x\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot push board extended data addr 0x%x\n", board_ext_data_addr); if (board_ext_data_addr == 0) return 0; - if (fw->size != (board_data_size + board_ext_data_size)) { + if (ar->board_len != (board_data_size + board_ext_data_size)) { ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n", - fw->size, board_data_size, board_ext_data_size); + ar->board_len, board_data_size, board_ext_data_size); return -EINVAL; } ret = ath10k_bmi_write_memory(ar, board_ext_data_addr, - fw->data + board_data_size, + ar->board_data + board_data_size, board_ext_data_size); if (ret) { ath10k_err("could not write board ext data (%d)\n", ret); @@ -247,12 +183,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, static int ath10k_download_board_data(struct ath10k *ar) { - const struct firmware *fw = ar->board_data; u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 address; int ret; - ret = ath10k_push_board_ext_data(ar, fw); + ret = ath10k_push_board_ext_data(ar); if (ret) { ath10k_err("could not push board ext data (%d)\n", ret); goto exit; @@ -264,8 +199,9 @@ static int ath10k_download_board_data(struct ath10k *ar) goto exit; } - ret = ath10k_bmi_write_memory(ar, address, fw->data, - min_t(u32, board_data_size, fw->size)); + ret = ath10k_bmi_write_memory(ar, address, ar->board_data, + min_t(u32, board_data_size, + ar->board_len)); if (ret) { ath10k_err("could not write board data (%d)\n", ret); goto exit; @@ -283,42 +219,51 @@ exit: static int ath10k_download_and_run_otp(struct ath10k *ar) { - const struct firmware *fw = ar->otp; - u32 address = ar->hw_params.patch_load_addr; - u32 exec_param; + u32 result, address = ar->hw_params.patch_load_addr; int ret; /* OTP is optional */ - if (!ar->otp) + if (!ar->otp_data || !ar->otp_len) { + ath10k_warn("Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n", + ar->otp_data, ar->otp_len); return 0; + } + + ath10k_dbg(ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n", + address, ar->otp_len); - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len); if (ret) { ath10k_err("could not write otp (%d)\n", ret); - goto exit; + return ret; } - exec_param = 0; - ret = ath10k_bmi_execute(ar, address, &exec_param); + ret = ath10k_bmi_execute(ar, address, 0, &result); if (ret) { ath10k_err("could not execute otp (%d)\n", ret); - goto exit; + return ret; } -exit: - return ret; + ath10k_dbg(ATH10K_DBG_BOOT, "boot otp execute result %d\n", result); + + if (result != 0) { + ath10k_err("otp calibration failed: %d", result); + return -EINVAL; + } + + return 0; } static int ath10k_download_fw(struct ath10k *ar) { - const struct firmware *fw = ar->firmware; u32 address; int ret; address = ar->hw_params.patch_load_addr; - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data, + ar->firmware_len); if (ret) { ath10k_err("could not write fw (%d)\n", ret); goto exit; @@ -330,8 +275,8 @@ exit: static void ath10k_core_free_firmware_files(struct ath10k *ar) { - if (ar->board_data && !IS_ERR(ar->board_data)) - release_firmware(ar->board_data); + if (ar->board && !IS_ERR(ar->board)) + release_firmware(ar->board); if (ar->otp && !IS_ERR(ar->otp)) release_firmware(ar->otp); @@ -339,12 +284,20 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) if (ar->firmware && !IS_ERR(ar->firmware)) release_firmware(ar->firmware); + ar->board = NULL; ar->board_data = NULL; + ar->board_len = 0; + ar->otp = NULL; + ar->otp_data = NULL; + ar->otp_len = 0; + ar->firmware = NULL; + ar->firmware_data = NULL; + ar->firmware_len = 0; } -static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) { int ret = 0; @@ -358,15 +311,18 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) return -EINVAL; } - ar->board_data = ath10k_fetch_fw_file(ar, - ar->hw_params.fw.dir, - ar->hw_params.fw.board); - if (IS_ERR(ar->board_data)) { - ret = PTR_ERR(ar->board_data); + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board)) { + ret = PTR_ERR(ar->board); ath10k_err("could not fetch board data (%d)\n", ret); goto err; } + ar->board_data = ar->board->data; + ar->board_len = ar->board->size; + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, ar->hw_params.fw.fw); @@ -376,6 +332,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->firmware_data = ar->firmware->data; + ar->firmware_len = ar->firmware->size; + /* OTP may be undefined. If so, don't fetch it at all */ if (ar->hw_params.fw.otp == NULL) return 0; @@ -389,6 +348,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->otp_data = ar->otp->data; + ar->otp_len = ar->otp->size; + return 0; err: @@ -396,21 +358,220 @@ err: return ret; } +static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) +{ + size_t magic_len, len, ie_len; + int ie_id, i, index, bit, ret; + struct ath10k_fw_ie *hdr; + const u8 *data; + __le32 *timestamp; + + /* first fetch the firmware file (firmware-*.bin) */ + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); + if (IS_ERR(ar->firmware)) { + ath10k_err("could not fetch firmware file '%s/%s': %ld\n", + ar->hw_params.fw.dir, name, PTR_ERR(ar->firmware)); + return PTR_ERR(ar->firmware); + } + + data = ar->firmware->data; + len = ar->firmware->size; + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ath10k_err("firmware file '%s/%s' too small to contain magic: %zu\n", + ar->hw_params.fw.dir, name, len); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { + ath10k_err("invalid firmware magic\n"); + ret = -EINVAL; + goto err; + } + + /* jump over the padding */ + magic_len = ALIGN(magic_len, 4); + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath10k_fw_ie)) { + hdr = (struct ath10k_fw_ie *)data; + + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ath10k_err("invalid length for FW IE %d (%zu < %zu)\n", + ie_id, len, ie_len); + ret = -EINVAL; + goto err; + } + + switch (ie_id) { + case ATH10K_FW_IE_FW_VERSION: + if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1) + break; + + memcpy(ar->hw->wiphy->fw_version, data, ie_len); + ar->hw->wiphy->fw_version[ie_len] = '\0'; + + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw version %s\n", + ar->hw->wiphy->fw_version); + break; + case ATH10K_FW_IE_TIMESTAMP: + if (ie_len != sizeof(u32)) + break; + + timestamp = (__le32 *)data; + + ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n", + le32_to_cpup(timestamp)); + break; + case ATH10K_FW_IE_FEATURES: + ath10k_dbg(ATH10K_DBG_BOOT, + "found firmware features ie (%zd B)\n", + ie_len); + + for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) { + index = i / 8; + bit = i % 8; + + if (index == ie_len) + break; + + if (data[index] & (1 << bit)) { + ath10k_dbg(ATH10K_DBG_BOOT, + "Enabling feature bit: %i\n", + i); + __set_bit(i, ar->fw_features); + } + } + + ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", + ar->fw_features, + sizeof(ar->fw_features)); + break; + case ATH10K_FW_IE_FW_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw image ie (%zd B)\n", + ie_len); + + ar->firmware_data = data; + ar->firmware_len = ie_len; + + break; + case ATH10K_FW_IE_OTP_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found otp image ie (%zd B)\n", + ie_len); + + ar->otp_data = data; + ar->otp_len = ie_len; + + break; + default: + ath10k_warn("Unknown FW IE: %u\n", + le32_to_cpu(hdr->id)); + break; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + + if (!ar->firmware_data || !ar->firmware_len) { + ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n", + ar->hw_params.fw.dir, name); + ret = -ENOMEDIUM; + goto err; + } + + /* now fetch the board file */ + if (ar->hw_params.fw.board == NULL) { + ath10k_err("board data file not defined"); + ret = -EINVAL; + goto err; + } + + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board)) { + ret = PTR_ERR(ar->board); + ath10k_err("could not fetch board data '%s/%s' (%d)\n", + ar->hw_params.fw.dir, ar->hw_params.fw.board, + ret); + goto err; + } + + ar->board_data = ar->board->data; + ar->board_len = ar->board->size; + + return 0; + +err: + ath10k_core_free_firmware_files(ar); + return ret; +} + +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +{ + int ret; + + ar->fw_api = 2; + ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE); + if (ret == 0) + goto success; + + ar->fw_api = 1; + ath10k_dbg(ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + + ret = ath10k_core_fetch_firmware_api_1(ar); + if (ret) + return ret; + +success: + ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api); + + return 0; +} + static int ath10k_init_download_firmware(struct ath10k *ar) { int ret; ret = ath10k_download_board_data(ar); - if (ret) + if (ret) { + ath10k_err("failed to download board data: %d\n", ret); return ret; + } ret = ath10k_download_and_run_otp(ar); - if (ret) + if (ret) { + ath10k_err("failed to run otp: %d\n", ret); return ret; + } ret = ath10k_download_fw(ar); - if (ret) + if (ret) { + ath10k_err("failed to download firmware: %d\n", ret); return ret; + } return ret; } @@ -429,10 +590,8 @@ static int ath10k_init_uart(struct ath10k *ar) return ret; } - if (!uart_print) { - ath10k_info("UART prints disabled\n"); + if (!uart_print) return 0; - } ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7); if (ret) { @@ -446,6 +605,13 @@ static int ath10k_init_uart(struct ath10k *ar) return ret; } + /* Set the UART baud rate to 19200. */ + ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200); + if (ret) { + ath10k_warn("could not set the baud rate (%d)\n", ret); + return ret; + } + ath10k_info("UART prints enabled\n"); return 0; } @@ -470,8 +636,8 @@ static int ath10k_init_hw_params(struct ath10k *ar) ar->hw_params = *hw_params; - ath10k_info("Hardware name %s version 0x%x\n", - ar->hw_params.name, ar->target_version); + ath10k_dbg(ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n", + ar->hw_params.name, ar->target_version); return 0; } @@ -484,15 +650,19 @@ static void ath10k_core_restart(struct work_struct *work) switch (ar->state) { case ATH10K_STATE_ON: - ath10k_halt(ar); ar->state = ATH10K_STATE_RESTARTING; + del_timer_sync(&ar->scan.timeout); + ath10k_reset_scan((unsigned long)ar); ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: - /* this can happen if driver is being unloaded */ + /* this can happen if driver is being unloaded + * or if the crash happens during FW probing */ ath10k_warn("cannot restart a device that hasn't been started\n"); break; case ATH10K_STATE_RESTARTING: + /* hw restart might be requested from multiple places */ + break; case ATH10K_STATE_RESTARTED: ar->state = ATH10K_STATE_WEDGED; /* fall through */ @@ -504,72 +674,12 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); } -struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, - const struct ath10k_hif_ops *hif_ops) -{ - struct ath10k *ar; - - ar = ath10k_mac_create(); - if (!ar) - return NULL; - - ar->ath_common.priv = ar; - ar->ath_common.hw = ar->hw; - - ar->p2p = !!ath10k_p2p; - ar->dev = dev; - - ar->hif.priv = hif_priv; - ar->hif.ops = hif_ops; - - init_completion(&ar->scan.started); - init_completion(&ar->scan.completed); - init_completion(&ar->scan.on_channel); - - init_completion(&ar->install_key_done); - init_completion(&ar->vdev_setup_done); - - setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); - - ar->workqueue = create_singlethread_workqueue("ath10k_wq"); - if (!ar->workqueue) - goto err_wq; - - mutex_init(&ar->conf_mutex); - spin_lock_init(&ar->data_lock); - - INIT_LIST_HEAD(&ar->peers); - init_waitqueue_head(&ar->peer_mapping_wq); - - init_completion(&ar->offchan_tx_completed); - INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); - skb_queue_head_init(&ar->offchan_tx_queue); - - init_waitqueue_head(&ar->event_queue); - - INIT_WORK(&ar->restart_work, ath10k_core_restart); - - return ar; - -err_wq: - ath10k_mac_destroy(ar); - return NULL; -} -EXPORT_SYMBOL(ath10k_core_create); - -void ath10k_core_destroy(struct ath10k *ar) -{ - flush_workqueue(ar->workqueue); - destroy_workqueue(ar->workqueue); - - ath10k_mac_destroy(ar); -} -EXPORT_SYMBOL(ath10k_core_destroy); - int ath10k_core_start(struct ath10k *ar) { int status; + lockdep_assert_held(&ar->conf_mutex); + ath10k_bmi_start(ar); if (ath10k_init_configure_target(ar)) { @@ -604,51 +714,116 @@ int ath10k_core_start(struct ath10k *ar) goto err; } - status = ath10k_htc_wait_target(&ar->htc); - if (status) + status = ath10k_htt_init(ar); + if (status) { + ath10k_err("failed to init htt: %d\n", status); goto err_wmi_detach; + } - status = ath10k_htt_attach(ar); + status = ath10k_htt_tx_alloc(&ar->htt); if (status) { - ath10k_err("could not attach htt (%d)\n", status); + ath10k_err("failed to alloc htt tx: %d\n", status); goto err_wmi_detach; } - status = ath10k_init_connect_htc(ar); - if (status) - goto err_htt_detach; + status = ath10k_htt_rx_alloc(&ar->htt); + if (status) { + ath10k_err("failed to alloc htt rx: %d\n", status); + goto err_htt_tx_detach; + } - ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version); + status = ath10k_hif_start(ar); + if (status) { + ath10k_err("could not start HIF: %d\n", status); + goto err_htt_rx_detach; + } - status = ath10k_check_fw_version(ar); - if (status) - goto err_disconnect_htc; + status = ath10k_htc_wait_target(&ar->htc); + if (status) { + ath10k_err("failed to connect to HTC: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_htt_connect(&ar->htt); + if (status) { + ath10k_err("failed to connect htt (%d)\n", status); + goto err_hif_stop; + } + + status = ath10k_wmi_connect(ar); + if (status) { + ath10k_err("could not connect wmi: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_htc_start(&ar->htc); + if (status) { + ath10k_err("failed to start htc: %d\n", status); + goto err_hif_stop; + } + + status = ath10k_wmi_wait_for_service_ready(ar); + if (status <= 0) { + ath10k_warn("wmi service ready event not received"); + status = -ETIMEDOUT; + goto err_htc_stop; + } + + ath10k_dbg(ATH10K_DBG_BOOT, "firmware %s booted\n", + ar->hw->wiphy->fw_version); status = ath10k_wmi_cmd_init(ar); if (status) { ath10k_err("could not send WMI init command (%d)\n", status); - goto err_disconnect_htc; + goto err_htc_stop; } status = ath10k_wmi_wait_for_unified_ready(ar); if (status <= 0) { ath10k_err("wmi unified ready event not received\n"); status = -ETIMEDOUT; - goto err_disconnect_htc; + goto err_htc_stop; + } + + status = ath10k_htt_setup(&ar->htt); + if (status) { + ath10k_err("failed to setup htt: %d\n", status); + goto err_htc_stop; } - status = ath10k_htt_attach_target(&ar->htt); + status = ath10k_debug_start(ar); if (status) - goto err_disconnect_htc; + goto err_htc_stop; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ar->free_vdev_map = (1 << TARGET_10X_NUM_VDEVS) - 1; + else + ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + + INIT_LIST_HEAD(&ar->arvifs); - ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) + ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n", + ar->hw_params.name, + ar->target_version, + ar->chip_id, + ar->hw->wiphy->fw_version, + ar->fw_api, + ar->htt.target_version_major, + ar->htt.target_version_minor); + + __set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags); return 0; -err_disconnect_htc: +err_htc_stop: ath10k_htc_stop(&ar->htc); -err_htt_detach: - ath10k_htt_detach(&ar->htt); +err_hif_stop: + ath10k_hif_stop(ar); +err_htt_rx_detach: + ath10k_htt_rx_free(&ar->htt); +err_htt_tx_detach: + ath10k_htt_tx_free(&ar->htt); err_wmi_detach: ath10k_wmi_detach(ar); err: @@ -656,10 +831,41 @@ err: } EXPORT_SYMBOL(ath10k_core_start); +int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt) +{ + int ret; + + reinit_completion(&ar->target_suspend); + + ret = ath10k_wmi_pdev_suspend_target(ar, suspend_opt); + if (ret) { + ath10k_warn("could not suspend target (%d)\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&ar->target_suspend, 1 * HZ); + + if (ret == 0) { + ath10k_warn("suspend timed out - target pause event never came\n"); + return -ETIMEDOUT; + } + + return 0; +} + void ath10k_core_stop(struct ath10k *ar) { + lockdep_assert_held(&ar->conf_mutex); + + /* try to suspend target */ + if (ar->state != ATH10K_STATE_RESTARTING) + ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR); + + ath10k_debug_stop(ar); ath10k_htc_stop(&ar->htc); - ath10k_htt_detach(&ar->htt); + ath10k_hif_stop(ar); + ath10k_htt_tx_free(&ar->htt); + ath10k_htt_rx_free(&ar->htt); ath10k_wmi_detach(ar); } EXPORT_SYMBOL(ath10k_core_stop); @@ -704,27 +910,62 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return ret; } + mutex_lock(&ar->conf_mutex); + ret = ath10k_core_start(ar); if (ret) { ath10k_err("could not init core (%d)\n", ret); ath10k_core_free_firmware_files(ar); ath10k_hif_power_down(ar); + mutex_unlock(&ar->conf_mutex); return ret; } ath10k_core_stop(ar); + + mutex_unlock(&ar->conf_mutex); + ath10k_hif_power_down(ar); return 0; } -int ath10k_core_register(struct ath10k *ar) +static int ath10k_core_check_chip_id(struct ath10k *ar) +{ + u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV); + + ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n", + ar->chip_id, hw_revision); + + /* Check that we are not using hw1.0 (some of them have same pci id + * as hw2.0) before doing anything else as ath10k crashes horribly + * due to missing hw1.0 workarounds. */ + switch (hw_revision) { + case QCA988X_HW_1_0_CHIP_ID_REV: + ath10k_err("ERROR: qca988x hw1.0 is not supported\n"); + return -EOPNOTSUPP; + + case QCA988X_HW_2_0_CHIP_ID_REV: + /* known hardware revision, continue normally */ + return 0; + + default: + ath10k_warn("Warning: hardware revision unknown (0x%x), expect problems\n", + ar->chip_id); + return 0; + } + + return 0; +} + +static void ath10k_core_register_work(struct work_struct *work) { + struct ath10k *ar = container_of(work, struct ath10k, register_work); int status; status = ath10k_core_probe_fw(ar); if (status) { ath10k_err("could not probe fw (%d)\n", status); - return status; + goto err; } status = ath10k_mac_register(ar); @@ -739,26 +980,119 @@ int ath10k_core_register(struct ath10k *ar) goto err_unregister_mac; } - return 0; + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); + return; err_unregister_mac: ath10k_mac_unregister(ar); err_release_fw: ath10k_core_free_firmware_files(ar); - return status; +err: + device_release_driver(ar->dev); + return; +} + +int ath10k_core_register(struct ath10k *ar, u32 chip_id) +{ + int status; + + ar->chip_id = chip_id; + + status = ath10k_core_check_chip_id(ar); + if (status) { + ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); + return status; + } + + queue_work(ar->workqueue, &ar->register_work); + + return 0; } EXPORT_SYMBOL(ath10k_core_register); void ath10k_core_unregister(struct ath10k *ar) { + cancel_work_sync(&ar->register_work); + + if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) + return; + /* We must unregister from mac80211 before we stop HTC and HIF. * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ ath10k_mac_unregister(ar); + ath10k_core_free_firmware_files(ar); + + ath10k_debug_destroy(ar); } EXPORT_SYMBOL(ath10k_core_unregister); +struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, + const struct ath10k_hif_ops *hif_ops) +{ + struct ath10k *ar; + + ar = ath10k_mac_create(); + if (!ar) + return NULL; + + ar->ath_common.priv = ar; + ar->ath_common.hw = ar->hw; + + ar->p2p = !!ath10k_p2p; + ar->dev = dev; + + ar->hif.priv = hif_priv; + ar->hif.ops = hif_ops; + + init_completion(&ar->scan.started); + init_completion(&ar->scan.completed); + init_completion(&ar->scan.on_channel); + init_completion(&ar->target_suspend); + + init_completion(&ar->install_key_done); + init_completion(&ar->vdev_setup_done); + + setup_timer(&ar->scan.timeout, ath10k_reset_scan, (unsigned long)ar); + + ar->workqueue = create_singlethread_workqueue("ath10k_wq"); + if (!ar->workqueue) + goto err_wq; + + mutex_init(&ar->conf_mutex); + spin_lock_init(&ar->data_lock); + + INIT_LIST_HEAD(&ar->peers); + init_waitqueue_head(&ar->peer_mapping_wq); + + init_completion(&ar->offchan_tx_completed); + INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); + skb_queue_head_init(&ar->offchan_tx_queue); + + INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + + INIT_WORK(&ar->register_work, ath10k_core_register_work); + INIT_WORK(&ar->restart_work, ath10k_core_restart); + + return ar; + +err_wq: + ath10k_mac_destroy(ar); + return NULL; +} +EXPORT_SYMBOL(ath10k_core_create); + +void ath10k_core_destroy(struct ath10k *ar) +{ + flush_workqueue(ar->workqueue); + destroy_workqueue(ar->workqueue); + + ath10k_mac_destroy(ar); +} +EXPORT_SYMBOL(ath10k_core_destroy); + MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Core module for QCA988X PCIe devices."); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index e4bba563ed4..68ceef61933 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -30,6 +30,7 @@ #include "wmi.h" #include "../ath.h" #include "../regd.h" +#include "../dfs_pattern_detector.h" #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -43,27 +44,37 @@ /* Antenna noise floor */ #define ATH10K_DEFAULT_NOISE_FLOOR -95 +#define ATH10K_MAX_NUM_MGMT_PENDING 128 + +/* number of failed packets */ +#define ATH10K_KICKOUT_THRESHOLD 50 + +/* + * Use insanely high numbers to make sure that the firmware implementation + * won't start, we have the same functionality already in hostapd. Unit + * is seconds. + */ +#define ATH10K_KEEPALIVE_MIN_IDLE 3747 +#define ATH10K_KEEPALIVE_MAX_IDLE 3895 +#define ATH10K_KEEPALIVE_MAX_UNRESPONSIVE 3900 + struct ath10k; struct ath10k_skb_cb { dma_addr_t paddr; - bool is_mapped; - bool is_aborted; + u8 vdev_id; struct { - u8 vdev_id; - u16 msdu_id; u8 tid; bool is_offchan; - bool is_conf; - bool discard; - bool no_ack; - u8 refcount; - struct sk_buff *txfrag; - struct sk_buff *msdu; + struct ath10k_htt_txbuf *txbuf; + u32 txbuf_paddr; } __packed htt; - /* 4 bytes left on 64bit arch */ + struct { + bool dtim_zero; + bool deliver_cab; + } bcn; } __packed; static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) @@ -73,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; @@ -108,21 +93,33 @@ struct ath10k_bmi { bool done_sent; }; +#define ATH10K_MAX_MEM_REQS 16 + +struct ath10k_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + struct ath10k_wmi { enum ath10k_htc_ep_id eid; struct completion service_ready; struct completion unified_ready; - atomic_t pending_tx_count; - wait_queue_head_t wq; + wait_queue_head_t tx_credits_wq; + struct wmi_cmd_map *cmd; + struct wmi_vdev_param_map *vdev_param; + struct wmi_pdev_param_map *pdev_param; - struct sk_buff_head wmi_event_list; - struct work_struct wmi_event_work; + u32 num_mem_chunks; + struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS]; }; struct ath10k_peer_stat { u8 peer_macaddr[ETH_ALEN]; u32 peer_rssi; u32 peer_tx_rate; + u32 peer_rx_rate; /* 10x only */ }; struct ath10k_target_stats { @@ -134,6 +131,12 @@ struct ath10k_target_stats { u32 cycle_count; u32 phy_err_count; u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; /* PDEV TX stats */ s32 comp_queued; @@ -185,6 +188,14 @@ struct ath10k_target_stats { }; +struct ath10k_dfs_stats { + u32 phy_errors; + u32 pulses_total; + u32 pulses_detected; + u32 pulses_discarded; + u32 radar_detected; +}; + #define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */ struct ath10k_peer { @@ -195,26 +206,49 @@ struct ath10k_peer { struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1]; }; +struct ath10k_sta { + struct ath10k_vif *arvif; + + /* the following are protected by ar->data_lock */ + u32 changed; /* IEEE80211_RC_* */ + u32 bw; + u32 nss; + u32 smps; + + struct work_struct update_wk; +}; + #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) struct ath10k_vif { + struct list_head list; + u32 vdev_id; enum wmi_vdev_type vdev_type; enum wmi_vdev_subtype vdev_subtype; u32 beacon_interval; u32 dtim_period; + struct sk_buff *beacon; + /* protected by data_lock */ + bool beacon_sent; struct ath10k *ar; struct ieee80211_vif *vif; + bool is_started; + bool is_up; + u32 aid; + u8 bssid[ETH_ALEN]; + + struct work_struct wep_key_work; struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; - u8 def_wep_key_index; + u8 def_wep_key_idx; + u8 def_wep_key_newidx; u16 tx_seq_no; union { struct { - u8 bssid[ETH_ALEN]; u32 uapsd; } sta; struct { @@ -228,10 +262,13 @@ struct ath10k_vif { u32 noa_len; u8 *noa_data; } ap; - struct { - u8 bssid[ETH_ALEN]; - } ibss; } u; + + u8 fixed_rate; + u8 fixed_nss; + u8 force_sgi; + bool use_cts_prot; + int num_legacy_stations; }; struct ath10k_vif_iter { @@ -246,6 +283,13 @@ struct ath10k_debug { u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; struct completion event_stats_compl; + + unsigned long htt_stats_mask; + struct delayed_work htt_stats_dwork; + struct ath10k_dfs_stats dfs_stats; + struct ath_dfs_pool_stats dfs_pool_stats; + + u32 fw_dbglog_mask; }; enum ath10k_state { @@ -270,12 +314,37 @@ enum ath10k_state { ATH10K_STATE_WEDGED, }; +enum ath10k_fw_features { + /* wmi_mgmt_rx_hdr contains extra RSSI information */ + ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0, + + /* firmware from 10X branch */ + ATH10K_FW_FEATURE_WMI_10X = 1, + + /* firmware support tx frame management over WMI, otherwise it's HTT */ + ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2, + + /* Firmware does not support P2P */ + ATH10K_FW_FEATURE_NO_P2P = 3, + + /* keep last */ + ATH10K_FW_FEATURE_COUNT, +}; + +enum ath10k_dev_flags { + /* Indicates that ath10k device is during CAC phase of DFS */ + ATH10K_CAC_RUNNING, + ATH10K_FLAG_FIRST_BOOT_DONE, + ATH10K_FLAG_CORE_REGISTERED, +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; struct device *dev; u8 mac_addr[ETH_ALEN]; + u32 chip_id; u32 target_version; u8 fw_version_major; u32 fw_version_minor; @@ -288,6 +357,8 @@ struct ath10k { u32 vht_cap_info; u32 num_rf_chains; + DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT); + struct targetdef *targetdef; struct hostdef *hostdef; @@ -298,8 +369,7 @@ struct ath10k { const struct ath10k_hif_ops *ops; } hif; - wait_queue_head_t event_queue; - bool is_target_paused; + struct completion target_suspend; struct ath10k_bmi bmi; struct ath10k_wmi wmi; @@ -319,9 +389,19 @@ struct ath10k { } fw; } hw_params; - const struct firmware *board_data; + const struct firmware *board; + const void *board_data; + size_t board_len; + const struct firmware *otp; + const void *otp_data; + size_t otp_len; + const struct firmware *firmware; + const void *firmware_data; + size_t firmware_len; + + int fw_api; struct { struct completion started; @@ -345,11 +425,27 @@ struct ath10k { /* valid during scan; needed for mgmt rx during scan */ struct ieee80211_channel *scan_channel; + /* current operating channel definition */ + struct cfg80211_chan_def chandef; + int free_vdev_map; + bool promisc; + bool monitor; int monitor_vdev_id; - bool monitor_enabled; - bool monitor_present; + bool monitor_started; unsigned int filter_flags; + unsigned long dev_flags; + u32 dfs_block_radar_events; + + /* protected by conf_mutex */ + bool radar_enabled; + int num_started_vdevs; + + /* Protected by conf-mutex */ + u8 supp_tx_chainmask; + u8 supp_rx_chainmask; + u8 cfg_tx_chainmask; + u8 cfg_rx_chainmask; struct wmi_pdev_set_wmm_params_arg wmm_params; struct completion install_key_done; @@ -364,16 +460,24 @@ struct ath10k { /* protects shared structure data */ spinlock_t data_lock; + struct list_head arvifs; struct list_head peers; wait_queue_head_t peer_mapping_wq; + /* number of created peers; protected by data_lock */ + int num_peers; + struct work_struct offchan_tx_work; struct sk_buff_head offchan_tx_queue; struct completion offchan_tx_completed; struct sk_buff *offchan_tx_skb; + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + enum ath10k_state state; + struct work_struct register_work; struct work_struct restart_work; /* cycle count is reported twice for each visited channel during scan. @@ -382,6 +486,8 @@ struct ath10k { u32 survey_last_cycle_count; struct survey_info survey[ATH10K_NUM_CHANS]; + struct dfs_pattern_detector *dfs_detector; + #ifdef CONFIG_ATH10K_DEBUGFS struct ath10k_debug debug; #endif @@ -392,8 +498,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, void ath10k_core_destroy(struct ath10k *ar); int ath10k_core_start(struct ath10k *ar); +int ath10k_wait_for_suspend(struct ath10k *ar, u32 suspend_opt); void ath10k_core_stop(struct ath10k *ar); -int ath10k_core_register(struct ath10k *ar); +int ath10k_core_register(struct ath10k *ar, u32 chip_id); void ath10k_core_unregister(struct ath10k *ar); #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 3d65594fa09..1b7ff4ba122 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -21,6 +21,9 @@ #include "core.h" #include "debug.h" +/* ms */ +#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000 + static int ath10k_printk(const char *level, const char *fmt, ...) { struct va_format vaf; @@ -158,7 +161,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, u8 *tmp = ev->data; struct ath10k_target_stats *stats; int num_pdev_stats, num_vdev_stats, num_peer_stats; - struct wmi_pdev_stats *ps; + struct wmi_pdev_stats_10x *ps; int i; spin_lock_bh(&ar->data_lock); @@ -170,7 +173,7 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, num_peer_stats = __le32_to_cpu(ev->num_peer_stats); /* 0 or max peers */ if (num_pdev_stats) { - ps = (struct wmi_pdev_stats *)tmp; + ps = (struct wmi_pdev_stats_10x *)tmp; stats->ch_noise_floor = __le32_to_cpu(ps->chan_nf); stats->tx_frame_count = __le32_to_cpu(ps->tx_frame_count); @@ -225,7 +228,18 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, stats->phy_err_drop = __le32_to_cpu(ps->wal.rx.phy_err_drop); stats->mpdu_errs = __le32_to_cpu(ps->wal.rx.mpdu_errs); - tmp += sizeof(struct wmi_pdev_stats); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, + ar->fw_features)) { + stats->ack_rx_bad = __le32_to_cpu(ps->ack_rx_bad); + stats->rts_bad = __le32_to_cpu(ps->rts_bad); + stats->rts_good = __le32_to_cpu(ps->rts_good); + stats->fcs_bad = __le32_to_cpu(ps->fcs_bad); + stats->no_beacons = __le32_to_cpu(ps->no_beacons); + stats->mib_int_count = __le32_to_cpu(ps->mib_int_count); + tmp += sizeof(struct wmi_pdev_stats_10x); + } else { + tmp += sizeof(struct wmi_pdev_stats_old); + } } /* 0 or max vdevs */ @@ -240,27 +254,33 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, } if (num_peer_stats) { - struct wmi_peer_stats *peer_stats; + struct wmi_peer_stats_10x *peer_stats; struct ath10k_peer_stat *s; stats->peers = num_peer_stats; for (i = 0; i < num_peer_stats; i++) { - peer_stats = (struct wmi_peer_stats *)tmp; + peer_stats = (struct wmi_peer_stats_10x *)tmp; s = &stats->peer_stat[i]; - WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats->peer_macaddr, - s->peer_macaddr); + memcpy(s->peer_macaddr, &peer_stats->peer_macaddr.addr, + ETH_ALEN); s->peer_rssi = __le32_to_cpu(peer_stats->peer_rssi); s->peer_tx_rate = __le32_to_cpu(peer_stats->peer_tx_rate); - - tmp += sizeof(struct wmi_peer_stats); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, + ar->fw_features)) { + s->peer_rx_rate = + __le32_to_cpu(peer_stats->peer_rx_rate); + tmp += sizeof(struct wmi_peer_stats_10x); + + } else { + tmp += sizeof(struct wmi_peer_stats_old); + } } } spin_unlock_bh(&ar->data_lock); - mutex_unlock(&ar->conf_mutex); complete(&ar->debug.event_stats_compl); } @@ -270,7 +290,7 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, struct ath10k *ar = file->private_data; struct ath10k_target_stats *fw_stats; char *buf = NULL; - unsigned int len = 0, buf_len = 2500; + unsigned int len = 0, buf_len = 8000; ssize_t ret_cnt = 0; long left; int i; @@ -318,6 +338,16 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, "Cycle count", fw_stats->cycle_count); len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", "PHY error count", fw_stats->phy_err_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RTS bad count", fw_stats->rts_bad); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RTS good count", fw_stats->rts_good); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "FCS bad count", fw_stats->fcs_bad); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "No beacon count", fw_stats->no_beacons); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MIB int count", fw_stats->mib_int_count); len += scnprintf(buf + len, buf_len - len, "\n"); len += scnprintf(buf + len, buf_len - len, "%30s\n", @@ -409,8 +439,8 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, "MPDU errors (FCS, MIC, ENC)", fw_stats->mpdu_errs); len += scnprintf(buf + len, buf_len - len, "\n"); - len += scnprintf(buf + len, buf_len - len, "%30s\n", - "ath10k PEER stats"); + len += scnprintf(buf + len, buf_len - len, "%30s (%d)\n", + "ath10k PEER stats", fw_stats->peers); len += scnprintf(buf + len, buf_len - len, "%30s\n\n", "================="); @@ -423,6 +453,9 @@ static ssize_t ath10k_read_fw_stats(struct file *file, char __user *user_buf, len += scnprintf(buf + len, buf_len - len, "%30s %u\n", "Peer TX rate", fw_stats->peer_stat[i].peer_tx_rate); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Peer RX rate", + fw_stats->peer_stat[i].peer_rx_rate); len += scnprintf(buf + len, buf_len - len, "\n"); } spin_unlock_bh(&ar->data_lock); @@ -449,27 +482,37 @@ static ssize_t ath10k_read_simulate_fw_crash(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - const char buf[] = "To simulate firmware crash write the keyword" - " `crash` to this file.\nThis will force firmware" - " to report a crash to the host system.\n"; + const char buf[] = "To simulate firmware crash write one of the" + " keywords to this file:\n `soft` - this will send" + " WMI_FORCE_FW_HANG_ASSERT to firmware if FW" + " supports that command.\n `hard` - this will send" + " to firmware command with illegal parameters" + " causing firmware crash.\n"; + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); } +/* Simulate firmware crash: + * 'soft': Call wmi command causing firmware hang. This firmware hang is + * recoverable by warm firmware reset. + * 'hard': Force firmware crash by setting any vdev parameter for not allowed + * vdev id. This is hard firmware crash because it is recoverable only by cold + * firmware reset. + */ static ssize_t ath10k_write_simulate_fw_crash(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct ath10k *ar = file->private_data; - char buf[32] = {}; + char buf[32]; int ret; mutex_lock(&ar->conf_mutex); simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); - if (strcmp(buf, "crash") && strcmp(buf, "crash\n")) { - ret = -EINVAL; - goto exit; - } + + /* make sure that buf is null terminated */ + buf[sizeof(buf) - 1] = 0; if (ar->state != ATH10K_STATE_ON && ar->state != ATH10K_STATE_RESTARTED) { @@ -477,14 +520,30 @@ static ssize_t ath10k_write_simulate_fw_crash(struct file *file, goto exit; } - ath10k_info("simulating firmware crash\n"); + /* drop the possible '\n' from the end */ + if (buf[count - 1] == '\n') { + buf[count - 1] = 0; + count--; + } - ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0); - if (ret) - ath10k_warn("failed to force fw hang (%d)\n", ret); + if (!strcmp(buf, "soft")) { + ath10k_info("simulating soft firmware crash\n"); + ret = ath10k_wmi_force_fw_hang(ar, WMI_FORCE_FW_HANG_ASSERT, 0); + } else if (!strcmp(buf, "hard")) { + ath10k_info("simulating hard firmware crash\n"); + ret = ath10k_wmi_vdev_set_param(ar, TARGET_NUM_VDEVS + 1, + ar->wmi.vdev_param->rts_threshold, 0); + } else { + ret = -EINVAL; + goto exit; + } + + if (ret) { + ath10k_warn("failed to simulate firmware crash: %d\n", ret); + goto exit; + } - if (ret == 0) - ret = count; + ret = count; exit: mutex_unlock(&ar->conf_mutex); @@ -499,6 +558,287 @@ static const struct file_operations fops_simulate_fw_crash = { .llseek = default_llseek, }; +static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[50]; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_chip_id = { + .read = ath10k_read_chip_id, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath10k_debug_htt_stats_req(struct ath10k *ar) +{ + u64 cookie; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->debug.htt_stats_mask == 0) + /* htt stats are disabled */ + return 0; + + if (ar->state != ATH10K_STATE_ON) + return 0; + + cookie = get_jiffies_64(); + + ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask, + cookie); + if (ret) { + ath10k_warn("failed to send htt stats request: %d\n", ret); + return ret; + } + + queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork, + msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL)); + + return 0; +} + +static void ath10k_debug_htt_stats_dwork(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + debug.htt_stats_dwork.work); + + mutex_lock(&ar->conf_mutex); + + ath10k_debug_htt_stats_req(ar); + + mutex_unlock(&ar->conf_mutex); +} + +static ssize_t ath10k_read_htt_stats_mask(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + unsigned int len; + + len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_htt_stats_mask(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long mask; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &mask); + if (ret) + return ret; + + /* max 8 bit masks (for now) */ + if (mask > 0xff) + return -E2BIG; + + mutex_lock(&ar->conf_mutex); + + ar->debug.htt_stats_mask = mask; + + ret = ath10k_debug_htt_stats_req(ar); + if (ret) + goto out; + + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_htt_stats_mask = { + .read = ath10k_read_htt_stats_mask, + .write = ath10k_write_htt_stats_mask, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath10k_read_fw_dbglog(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[32]; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", + ar->debug.fw_dbglog_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_fw_dbglog(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long mask; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &mask); + if (ret) + return ret; + + mutex_lock(&ar->conf_mutex); + + ar->debug.fw_dbglog_mask = mask; + + if (ar->state == ATH10K_STATE_ON) { + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + if (ret) { + ath10k_warn("dbglog cfg failed from debugfs: %d\n", + ret); + goto exit; + } + } + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_fw_dbglog = { + .read = ath10k_read_fw_dbglog, + .write = ath10k_write_fw_dbglog, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath10k_debug_start(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_debug_htt_stats_req(ar); + if (ret) + /* continue normally anyway, this isn't serious */ + ath10k_warn("failed to start htt stats workqueue: %d\n", ret); + + if (ar->debug.fw_dbglog_mask) { + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + if (ret) + /* not serious */ + ath10k_warn("failed to enable dbglog during start: %d", + ret); + } + + return 0; +} + +void ath10k_debug_stop(struct ath10k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + /* Must not use _sync to avoid deadlock, we do that in + * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid + * warning from del_timer(). */ + if (ar->debug.htt_stats_mask != 0) + cancel_delayed_work(&ar->debug.htt_stats_dwork); +} + +static ssize_t ath10k_write_simulate_radar(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + + ieee80211_radar_detected(ar->hw); + + return count; +} + +static const struct file_operations fops_simulate_radar = { + .write = ath10k_write_simulate_radar, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +#define ATH10K_DFS_STAT(s, p) (\ + len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \ + ar->debug.dfs_stats.p)) + +#define ATH10K_DFS_POOL_STAT(s, p) (\ + len += scnprintf(buf + len, size - len, "%-28s : %10u\n", s, \ + ar->debug.dfs_pool_stats.p)) + +static ssize_t ath10k_read_dfs_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + int retval = 0, len = 0; + const int size = 8000; + struct ath10k *ar = file->private_data; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (!ar->dfs_detector) { + len += scnprintf(buf + len, size - len, "DFS not enabled\n"); + goto exit; + } + + ar->debug.dfs_pool_stats = + ar->dfs_detector->get_stats(ar->dfs_detector); + + len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n"); + + ATH10K_DFS_STAT("reported phy errors", phy_errors); + ATH10K_DFS_STAT("pulse events reported", pulses_total); + ATH10K_DFS_STAT("DFS pulses detected", pulses_detected); + ATH10K_DFS_STAT("DFS pulses discarded", pulses_discarded); + ATH10K_DFS_STAT("Radars detected", radar_detected); + + len += scnprintf(buf + len, size - len, "Global Pool statistics:\n"); + ATH10K_DFS_POOL_STAT("Pool references", pool_reference); + ATH10K_DFS_POOL_STAT("Pulses allocated", pulse_allocated); + ATH10K_DFS_POOL_STAT("Pulses alloc error", pulse_alloc_error); + ATH10K_DFS_POOL_STAT("Pulses in use", pulse_used); + ATH10K_DFS_POOL_STAT("Seqs. allocated", pseq_allocated); + ATH10K_DFS_POOL_STAT("Seqs. alloc error", pseq_alloc_error); + ATH10K_DFS_POOL_STAT("Seqs. in use", pseq_used); + +exit: + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_dfs_stats = { + .read = ath10k_read_dfs_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.debugfs_phy = debugfs_create_dir("ath10k", @@ -507,6 +847,9 @@ int ath10k_debug_create(struct ath10k *ar) if (!ar->debug.debugfs_phy) return -ENOMEM; + INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork, + ath10k_debug_htt_stats_dwork); + init_completion(&ar->debug.event_stats_compl); debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, @@ -518,8 +861,37 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash); + debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_chip_id); + + debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_htt_stats_mask); + + debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_fw_dbglog); + + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) { + debugfs_create_file("dfs_simulate_radar", S_IWUSR, + ar->debug.debugfs_phy, ar, + &fops_simulate_radar); + + debugfs_create_bool("dfs_block_radar_events", S_IWUSR, + ar->debug.debugfs_phy, + &ar->dfs_block_radar_events); + + debugfs_create_file("dfs_stats", S_IRUSR, + ar->debug.debugfs_phy, ar, + &fops_dfs_stats); + } + return 0; } + +void ath10k_debug_destroy(struct ath10k *ar) +{ + cancel_delayed_work_sync(&ar->debug.htt_stats_dwork); +} + #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 168140c5402..a5824990bd2 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -27,34 +27,54 @@ enum ath10k_debug_mask { ATH10K_DBG_HTC = 0x00000004, ATH10K_DBG_HTT = 0x00000008, ATH10K_DBG_MAC = 0x00000010, - ATH10K_DBG_CORE = 0x00000020, + ATH10K_DBG_BOOT = 0x00000020, ATH10K_DBG_PCI_DUMP = 0x00000040, ATH10K_DBG_HTT_DUMP = 0x00000080, ATH10K_DBG_MGMT = 0x00000100, ATH10K_DBG_DATA = 0x00000200, + ATH10K_DBG_BMI = 0x00000400, + ATH10K_DBG_REGULATORY = 0x00000800, ATH10K_DBG_ANY = 0xffffffff, }; extern unsigned int ath10k_debug_mask; -extern __printf(1, 2) int ath10k_info(const char *fmt, ...); -extern __printf(1, 2) int ath10k_err(const char *fmt, ...); -extern __printf(1, 2) int ath10k_warn(const char *fmt, ...); +__printf(1, 2) int ath10k_info(const char *fmt, ...); +__printf(1, 2) int ath10k_err(const char *fmt, ...); +__printf(1, 2) int ath10k_warn(const char *fmt, ...); #ifdef CONFIG_ATH10K_DEBUGFS +int ath10k_debug_start(struct ath10k *ar); +void ath10k_debug_stop(struct ath10k *ar); int ath10k_debug_create(struct ath10k *ar); +void ath10k_debug_destroy(struct ath10k *ar); void ath10k_debug_read_service_map(struct ath10k *ar, void *service_map, size_t map_size); void ath10k_debug_read_target_stats(struct ath10k *ar, struct wmi_stats_event *ev); +#define ATH10K_DFS_STAT_INC(ar, c) (ar->debug.dfs_stats.c++) + #else +static inline int ath10k_debug_start(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_debug_stop(struct ath10k *ar) +{ +} + static inline int ath10k_debug_create(struct ath10k *ar) { return 0; } +static inline void ath10k_debug_destroy(struct ath10k *ar) +{ +} + static inline void ath10k_debug_read_service_map(struct ath10k *ar, void *service_map, size_t map_size) @@ -65,11 +85,14 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar, struct wmi_stats_event *ev) { } + +#define ATH10K_DFS_STAT_INC(ar, c) do { } while (0) + #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG -extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, - const char *fmt, ...); +__printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, + const char *fmt, ...); void ath10k_dbg_dump(enum ath10k_debug_mask mask, const char *msg, const char *prefix, const void *buf, size_t len); diff --git a/drivers/net/wireless/ath/ath10k/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 ef3329ef52f..e493db4b4a4 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)); } @@ -103,10 +105,10 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, struct ath10k_htc_hdr *hdr; hdr = (struct ath10k_htc_hdr *)skb->data; - memset(hdr, 0, sizeof(*hdr)); hdr->eid = ep->eid; hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr)); + hdr->flags = 0; spin_lock_bh(&ep->htc->tx_lock); hdr->seq_no = ep->seq_no++; @@ -117,134 +119,16 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, spin_unlock_bh(&ep->htc->tx_lock); } -static int ath10k_htc_issue_skb(struct ath10k_htc *htc, - struct ath10k_htc_ep *ep, - struct sk_buff *skb, - u8 credits) -{ - struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - int ret; - - ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, - ep->eid, skb); - - ath10k_htc_prepare_tx_skb(ep, skb); - - ret = ath10k_skb_map(htc->ar->dev, skb); - if (ret) - goto err; - - ret = ath10k_hif_send_head(htc->ar, - ep->ul_pipe_id, - ep->eid, - skb->len, - skb); - if (unlikely(ret)) - goto err; - - return 0; -err: - ath10k_warn("HTC issue failed: %d\n", ret); - - spin_lock_bh(&htc->tx_lock); - ep->tx_credits += credits; - spin_unlock_bh(&htc->tx_lock); - - /* this is the simplest way to handle out-of-resources for non-credit - * based endpoints. credit based endpoints can still get -ENOSR, but - * this is highly unlikely as credit reservation should prevent that */ - if (ret == -ENOSR) { - spin_lock_bh(&htc->tx_lock); - __skb_queue_head(&ep->tx_queue, skb); - spin_unlock_bh(&htc->tx_lock); - - return ret; - } - - skb_cb->is_aborted = true; - ath10k_htc_notify_tx_completion(ep, skb); - - return ret; -} - -static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc, - struct ath10k_htc_ep *ep, - u8 *credits) -{ - struct sk_buff *skb; - struct ath10k_skb_cb *skb_cb; - int credits_required; - int remainder; - unsigned int transfer_len; - - lockdep_assert_held(&htc->tx_lock); - - skb = __skb_dequeue(&ep->tx_queue); - if (!skb) - return NULL; - - skb_cb = ATH10K_SKB_CB(skb); - transfer_len = skb->len; - - if (likely(transfer_len <= htc->target_credit_size)) { - credits_required = 1; - } else { - /* figure out how many credits this message requires */ - credits_required = transfer_len / htc->target_credit_size; - remainder = transfer_len % htc->target_credit_size; - - if (remainder) - credits_required++; - } - - ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n", - credits_required, ep->tx_credits); - - if (ep->tx_credits < credits_required) { - __skb_queue_head(&ep->tx_queue, skb); - return NULL; - } - - ep->tx_credits -= credits_required; - *credits = credits_required; - return skb; -} - -static void ath10k_htc_send_work(struct work_struct *work) -{ - struct ath10k_htc_ep *ep = container_of(work, - struct ath10k_htc_ep, send_work); - struct ath10k_htc *htc = ep->htc; - struct sk_buff *skb; - u8 credits = 0; - int ret; - - while (true) { - if (ep->ul_is_polled) - ath10k_htc_send_complete_check(ep, 0); - - spin_lock_bh(&htc->tx_lock); - if (ep->tx_credit_flow_enabled) - skb = ath10k_htc_get_skb_credit_based(htc, ep, - &credits); - else - skb = __skb_dequeue(&ep->tx_queue); - spin_unlock_bh(&htc->tx_lock); - - if (!skb) - break; - - ret = ath10k_htc_issue_skb(htc, ep, skb, credits); - if (ret == -ENOSR) - break; - } -} - int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, 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; if (htc->ar->state == ATH10K_STATE_WEDGED) return -ECOMM; @@ -254,18 +138,67 @@ int ath10k_htc_send(struct ath10k_htc *htc, return -ENOENT; } + /* FIXME: This looks ugly, can we fix it? */ spin_lock_bh(&htc->tx_lock); if (htc->stopped) { spin_unlock_bh(&htc->tx_lock); return -ESHUTDOWN; } + spin_unlock_bh(&htc->tx_lock); - __skb_queue_tail(&ep->tx_queue, skb); skb_push(skb, sizeof(struct ath10k_htc_hdr)); - spin_unlock_bh(&htc->tx_lock); - queue_work(htc->ar->workqueue, &ep->send_work); + if (ep->tx_credit_flow_enabled) { + credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credits < credits) { + spin_unlock_bh(&htc->tx_lock); + ret = -EAGAIN; + goto err_pull; + } + ep->tx_credits -= credits; + ath10k_dbg(ATH10K_DBG_HTC, + "htc ep %d consumed %d credits (total %d)\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + } + + ath10k_htc_prepare_tx_skb(ep, 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; + + 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: + 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); + ep->tx_credits += credits; + ath10k_dbg(ATH10K_DBG_HTC, + "htc ep %d reverted %d credits back (total %d)\n", + eid, credits, ep->tx_credits); + spin_unlock_bh(&htc->tx_lock); + + if (ep->ep_ops.ep_tx_credits) + ep->ep_ops.ep_tx_credits(htc->ar); + } +err_pull: + skb_pull(skb, sizeof(struct ath10k_htc_hdr)); + return ret; } static int ath10k_htc_tx_completion_handler(struct ath10k *ar, @@ -275,42 +208,15 @@ 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 (WARN_ON_ONCE(!skb)) + return 0; + ath10k_htc_notify_tx_completion(ep, skb); /* the skb now belongs to the completion handler */ - /* note: when using TX credit flow, the re-checking of queues happens - * when credits flow back from the target. in the non-TX credit case, - * we recheck after the packet completes */ - spin_lock_bh(&htc->tx_lock); - if (!ep->tx_credit_flow_enabled && !htc->stopped) - queue_work(ar->workqueue, &ep->send_work); - spin_unlock_bh(&htc->tx_lock); - return 0; } -/* flush endpoint TX queue */ -static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc, - struct ath10k_htc_ep *ep) -{ - struct sk_buff *skb; - struct ath10k_skb_cb *skb_cb; - - spin_lock_bh(&htc->tx_lock); - for (;;) { - skb = __skb_dequeue(&ep->tx_queue); - if (!skb) - break; - - skb_cb = ATH10K_SKB_CB(skb); - skb_cb->is_aborted = true; - ath10k_htc_notify_tx_completion(ep, skb); - } - spin_unlock_bh(&htc->tx_lock); - - cancel_work_sync(&ep->send_work); -} - /***********/ /* Receive */ /***********/ @@ -334,14 +240,17 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc, if (report->eid >= ATH10K_HTC_EP_COUNT) break; - ath10k_dbg(ATH10K_DBG_HTC, "ep %d got %d credits\n", - report->eid, report->credits); - ep = &htc->endpoint[report->eid]; ep->tx_credits += report->credits; - if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue)) - queue_work(htc->ar->workqueue, &ep->send_work); + ath10k_dbg(ATH10K_DBG_HTC, "htc ep %d got %d credits (total %d)\n", + report->eid, report->credits, ep->tx_credits); + + if (ep->ep_ops.ep_tx_credits) { + spin_unlock_bh(&htc->tx_lock); + ep->ep_ops.ep_tx_credits(htc->ar); + spin_lock_bh(&htc->tx_lock); + } } spin_unlock_bh(&htc->tx_lock); } @@ -599,10 +508,8 @@ static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc) ep->max_ep_message_len = 0; ep->max_tx_queue_depth = 0; ep->eid = i; - skb_queue_head_init(&ep->tx_queue); ep->htc = htc; ep->tx_credit_flow_enabled = true; - INIT_WORK(&ep->send_work, ath10k_htc_send_work); } } @@ -647,14 +554,6 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) u16 credit_count; u16 credit_size; - INIT_COMPLETION(htc->ctl_resp); - - status = ath10k_hif_start(htc->ar); - if (status) { - ath10k_err("could not start HIF (%d)\n", status); - goto err_start; - } - status = wait_for_completion_timeout(&htc->ctl_resp, ATH10K_HTC_WAIT_TIMEOUT_HZ); if (status <= 0) { @@ -662,15 +561,13 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) status = -ETIMEDOUT; ath10k_err("ctl_resp never came in (%d)\n", status); - goto err_target; + return status; } if (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->ready)) { ath10k_err("Invalid HTC ready msg len:%d\n", htc->control_resp_len); - - status = -ECOMM; - goto err_target; + return -ECOMM; } msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; @@ -680,8 +577,7 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) if (message_id != ATH10K_HTC_MSG_READY_ID) { ath10k_err("Invalid HTC ready msg: 0x%x\n", message_id); - status = -ECOMM; - goto err_target; + return -ECOMM; } htc->total_transmit_credits = credit_count; @@ -694,9 +590,8 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) if ((htc->total_transmit_credits == 0) || (htc->target_credit_size == 0)) { - status = -ECOMM; ath10k_err("Invalid credit size received\n"); - goto err_target; + return -ECOMM; } ath10k_htc_setup_target_buffer_assignments(htc); @@ -713,14 +608,10 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc) status = ath10k_htc_connect_service(htc, &conn_req, &conn_resp); if (status) { ath10k_err("could not connect to htc service (%d)\n", status); - goto err_target; + return status; } return 0; -err_target: - ath10k_hif_stop(htc->ar); -err_start: - return status; } int ath10k_htc_connect_service(struct ath10k_htc *htc, @@ -752,8 +643,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, tx_alloc = ath10k_htc_get_credit_allocation(htc, conn_req->service_id); if (!tx_alloc) - ath10k_dbg(ATH10K_DBG_HTC, - "HTC Service %s does not allocate target credits\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc service %s does not allocate target credits\n", htc_service_name(conn_req->service_id)); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); @@ -772,17 +663,17 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC); - req_msg = &msg->connect_service; - req_msg->flags = __cpu_to_le16(flags); - req_msg->service_id = __cpu_to_le16(conn_req->service_id); - /* Only enable credit flow control for WMI ctrl service */ if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) { flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; disable_credit_flow_ctrl = true; } - INIT_COMPLETION(htc->ctl_resp); + req_msg = &msg->connect_service; + req_msg->flags = __cpu_to_le16(flags); + req_msg->service_id = __cpu_to_le16(conn_req->service_id); + + reinit_completion(&htc->ctl_resp); status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); if (status) { @@ -873,19 +764,19 @@ setup: if (status) return status; - ath10k_dbg(ATH10K_DBG_HTC, - "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", htc_service_name(ep->service_id), ep->ul_pipe_id, ep->dl_pipe_id, ep->eid); - ath10k_dbg(ATH10K_DBG_HTC, - "EP %d UL polled: %d, DL polled: %d\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc ep %d ul polled %d dl polled %d\n", ep->eid, ep->ul_is_polled, ep->dl_is_polled); if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { ep->tx_credit_flow_enabled = false; - ath10k_dbg(ATH10K_DBG_HTC, - "HTC service: %s eid: %d TX flow control disabled\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc service '%s' eid %d TX flow control disabled\n", htc_service_name(ep->service_id), assigned_eid); } @@ -939,25 +830,11 @@ int ath10k_htc_start(struct ath10k_htc *htc) return 0; } -/* - * stop HTC communications, i.e. stop interrupt reception, and flush all - * queued buffers - */ void ath10k_htc_stop(struct ath10k_htc *htc) { - int i; - struct ath10k_htc_ep *ep; - spin_lock_bh(&htc->tx_lock); htc->stopped = true; spin_unlock_bh(&htc->tx_lock); - - for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { - ep = &htc->endpoint[i]; - ath10k_htc_flush_endpoint_tx(htc, ep); - } - - ath10k_hif_stop(htc->ar); } /* registered target arrival callback from the HIF layer */ diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index e1dd8c76185..4716d331e6b 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -276,6 +276,7 @@ struct ath10k_htc_ops { struct ath10k_htc_ep_ops { void (*ep_tx_complete)(struct ath10k *, struct sk_buff *); void (*ep_rx_complete)(struct ath10k *, struct sk_buff *); + void (*ep_tx_credits)(struct ath10k *); }; /* service connection information */ @@ -315,15 +316,11 @@ struct ath10k_htc_ep { int ul_is_polled; /* call HIF to get tx completions */ int dl_is_polled; /* call HIF to fetch rx (not implemented) */ - struct sk_buff_head tx_queue; - u8 seq_no; /* for debugging */ int tx_credits; int tx_credit_size; int tx_credits_per_max_message; bool tx_credit_flow_enabled; - - struct work_struct send_work; }; struct ath10k_htc_svc_tx_credits { diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 39342c5cfcb..19c12cc8d66 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -22,7 +22,7 @@ #include "core.h" #include "debug.h" -static int ath10k_htt_htc_attach(struct ath10k_htt *htt) +int ath10k_htt_connect(struct ath10k_htt *htt) { struct ath10k_htc_svc_conn_req conn_req; struct ath10k_htc_svc_conn_resp conn_resp; @@ -48,39 +48,14 @@ static int ath10k_htt_htc_attach(struct ath10k_htt *htt) return 0; } -int ath10k_htt_attach(struct ath10k *ar) +int ath10k_htt_init(struct ath10k *ar) { struct ath10k_htt *htt = &ar->htt; - int ret; htt->ar = ar; htt->max_throughput_mbps = 800; /* - * Connect to HTC service. - * This has to be done before calling ath10k_htt_rx_attach, - * since ath10k_htt_rx_attach involves sending a rx ring configure - * message to the target. - */ - ret = ath10k_htt_htc_attach(htt); - if (ret) { - ath10k_err("could not attach htt htc (%d)\n", ret); - goto err_htc_attach; - } - - ret = ath10k_htt_tx_attach(htt); - if (ret) { - ath10k_err("could not attach htt tx (%d)\n", ret); - goto err_htc_attach; - } - - ret = ath10k_htt_rx_attach(htt); - if (ret) { - ath10k_err("could not attach htt rx (%d)\n", ret); - goto err_rx_attach; - } - - /* * Prefetch enough data to satisfy target * classification engine. * This is for LL chips. HL chips will probably @@ -93,36 +68,26 @@ int ath10k_htt_attach(struct ath10k *ar) 2; /* ip4 dscp or ip6 priority */ return 0; - -err_rx_attach: - ath10k_htt_tx_detach(htt); -err_htc_attach: - return ret; } #define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ) static int ath10k_htt_verify_version(struct ath10k_htt *htt) { - ath10k_dbg(ATH10K_DBG_HTT, - "htt target version %d.%d; host version %d.%d\n", - htt->target_version_major, - htt->target_version_minor, - HTT_CURRENT_VERSION_MAJOR, - HTT_CURRENT_VERSION_MINOR); - - if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) { - ath10k_err("htt major versions are incompatible!\n"); + ath10k_dbg(ATH10K_DBG_BOOT, "htt target version %d.%d\n", + htt->target_version_major, htt->target_version_minor); + + if (htt->target_version_major != 2 && + htt->target_version_major != 3) { + ath10k_err("unsupported htt major version %d. supported versions are 2 and 3\n", + htt->target_version_major); return -ENOTSUPP; } - if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR) - ath10k_warn("htt minor version differ but still compatible\n"); - return 0; } -int ath10k_htt_attach_target(struct ath10k_htt *htt) +int ath10k_htt_setup(struct ath10k_htt *htt) { int status; @@ -145,9 +110,3 @@ int ath10k_htt_attach_target(struct ath10k_htt *htt) return ath10k_htt_send_rx_ring_cfg_ll(htt); } - -void ath10k_htt_detach(struct ath10k_htt *htt) -{ - ath10k_htt_rx_detach(htt); - ath10k_htt_tx_detach(htt); -} diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 318be4629cd..9a263462c79 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -19,13 +19,13 @@ #define _HTT_H_ #include <linux/bug.h> +#include <linux/interrupt.h> +#include <linux/dmapool.h> +#include <net/mac80211.h> #include "htc.h" #include "rx_desc.h" -#define HTT_CURRENT_VERSION_MAJOR 2 -#define HTT_CURRENT_VERSION_MINOR 1 - enum htt_dbg_stats_type { HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0, HTT_DBG_STATS_RX_REORDER = 1 << 1, @@ -45,6 +45,9 @@ enum htt_h2t_msg_type { /* host-to-target */ HTT_H2T_MSG_TYPE_SYNC = 4, HTT_H2T_MSG_TYPE_AGGR_CFG = 5, HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6, + + /* This command is used for sending management frames in HTT < 3.0. + * HTT >= 3.0 uses TX_FRM for everything. */ HTT_H2T_MSG_TYPE_MGMT_TX = 7, HTT_H2T_NUM_MSGS /* keep this last */ @@ -1170,18 +1173,12 @@ struct htt_peer_unmap_event { u16 peer_id; }; -struct htt_rx_info { - struct sk_buff *skb; - enum htt_rx_mpdu_status status; - enum htt_rx_mpdu_encrypt_type encrypt_type; - s8 signal; - struct { - u8 info0; - u32 info1; - u32 info2; - } rate; - bool fcs_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; @@ -1264,10 +1261,21 @@ 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; + + /* rx_status template */ + struct ieee80211_rx_status rx_status; }; #define RX_HTT_HDR_STATUS_LEN 64 @@ -1308,6 +1316,10 @@ struct htt_rx_desc { #define HTT_RX_BUF_SIZE 1920 #define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) +/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle + * aggregated traffic more nicely. */ +#define ATH10K_HTT_MAX_NUM_REFILL 16 + /* * DMA_MAP expects the buffer to be an integral number of cache lines. * Rather than checking the actual cache line size, this code makes a @@ -1316,17 +1328,20 @@ struct htt_rx_desc { #define HTT_LOG2_MAX_CACHE_LINE_SIZE 7 /* 2^7 = 128 */ #define HTT_MAX_CACHE_LINE_SIZE_MASK ((1 << HTT_LOG2_MAX_CACHE_LINE_SIZE) - 1) -int ath10k_htt_attach(struct ath10k *ar); -int ath10k_htt_attach_target(struct ath10k_htt *htt); -void ath10k_htt_detach(struct ath10k_htt *htt); +int ath10k_htt_connect(struct ath10k_htt *htt); +int ath10k_htt_init(struct ath10k *ar); +int ath10k_htt_setup(struct ath10k_htt *htt); + +int ath10k_htt_tx_alloc(struct ath10k_htt *htt); +void ath10k_htt_tx_free(struct ath10k_htt *htt); + +int ath10k_htt_rx_alloc(struct ath10k_htt *htt); +void ath10k_htt_rx_free(struct ath10k_htt *htt); -int ath10k_htt_tx_attach(struct ath10k_htt *htt); -void ath10k_htt_tx_detach(struct ath10k_htt *htt); -int ath10k_htt_rx_attach(struct ath10k_htt *htt); -void ath10k_htt_rx_detach(struct ath10k_htt *htt); void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie); int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); @@ -1334,4 +1349,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 e784c40b904..eebc860c365 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -20,6 +20,7 @@ #include "htt.h" #include "txrx.h" #include "debug.h" +#include "trace.h" #include <linux/log2.h> @@ -40,6 +41,10 @@ /* when under memory pressure rx ring refill may fail and needs a retry */ #define HTT_RX_RING_REFILL_RETRY_MS 50 + +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) { int size; @@ -177,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) { - int ret, num_to_fill; + int ret, num_deficit, num_to_fill; + /* Refilling the whole RX ring buffer proves to be a bad idea. The + * reason is RX may take up significant amount of CPU cycles and starve + * other tasks, e.g. TX on an ethernet device while acting as a bridge + * with ath10k wlan interface. This ended up with very poor performance + * once CPU the host system was overwhelmed with RX on ath10k. + * + * By limiting the number of refills the replenishing occurs + * progressively. This in turns makes use of the fact tasklets are + * processed in FIFO order. This means actual RX processing can starve + * out refilling. If there's not enough buffers on RX ring FW will not + * report RX until it is refilled with enough buffers. This + * automatically balances load wrt to CPU power. + * + * This probably comes at a cost of lower maximum throughput but + * improves the avarage and stability. */ spin_lock_bh(&htt->rx_ring.lock); - num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; + num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; + num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit); + num_deficit -= num_to_fill; ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill); if (ret == -ENOMEM) { /* @@ -191,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) */ mod_timer(&htt->rx_ring.refill_retry_timer, jiffies + msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS)); + } else if (num_deficit > 0) { + tasklet_schedule(&htt->rx_replenish_task); } spin_unlock_bh(&htt->rx_ring.lock); } @@ -201,30 +225,34 @@ 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) +static void ath10k_htt_rx_ring_clean_up(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; + struct sk_buff *skb; + int i; + + for (i = 0; i < htt->rx_ring.size; i++) { + skb = htt->rx_ring.netbufs_ring[i]; + if (!skb) + continue; + + dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + htt->rx_ring.netbufs_ring[i] = NULL; + } } -void ath10k_htt_rx_detach(struct ath10k_htt *htt) +void ath10k_htt_rx_free(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); - while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { - struct sk_buff *skb = - htt->rx_ring.netbufs_ring[sw_rd_idx]; - struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb); + skb_queue_purge(&htt->tx_compl_q); + skb_queue_purge(&htt->rx_compl_q); - dma_unmap_single(htt->ar->dev, cb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - dev_kfree_skb_any(htt->rx_ring.netbufs_ring[sw_rd_idx]); - sw_rd_idx++; - sw_rd_idx &= htt->rx_ring.size_mask; - } + ath10k_htt_rx_ring_clean_up(htt); dma_free_coherent(htt->ar->dev, (htt->rx_ring.size * @@ -245,20 +273,22 @@ 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]; + htt->rx_ring.netbufs_ring[idx] = NULL; idx++; idx &= htt->rx_ring.size_mask; htt->rx_ring.sw_rd_idx.msdu_payld = idx; htt->rx_ring.fill_cnt--; - spin_unlock_bh(&htt->rx_ring.lock); return msdu; } @@ -273,6 +303,7 @@ static void ath10k_htt_rx_free_msdu_chain(struct sk_buff *skb) } } +/* return: < 0 fatal error, 0 - non chained msdu, 1 chained msdu */ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, u8 **fw_desc, int *fw_desc_len, struct sk_buff **head_msdu, @@ -282,12 +313,11 @@ 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"); - return 0; + return -1; } msdu = *head_msdu = ath10k_htt_rx_netbuf_pop(htt); @@ -299,7 +329,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx pop: ", msdu->data, msdu->len + skb_tailroom(msdu)); rx_desc = (struct htt_rx_desc *)msdu->data; @@ -392,8 +422,8 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, next->len + skb_tailroom(next), DMA_FROM_DEVICE); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx: ", - next->data, + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, + "htt rx chained: ", next->data, next->len + skb_tailroom(next)); skb_trim(next, 0); @@ -405,12 +435,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, msdu_chaining = 1; } - if (msdu_len > 0) { - /* This may suggest FW bug? */ - ath10k_warn("htt rx msdu len not consumed (%d)\n", - msdu_len); - } - last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & RX_MSDU_END_INFO0_LAST_MSDU; @@ -425,6 +449,9 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, } *tail_msdu = msdu; + if (*head_msdu == NULL) + msdu_chaining = -1; + /* * Don't refill the ring yet. * @@ -441,7 +468,13 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, return msdu_chaining; } -int ath10k_htt_rx_attach(struct ath10k_htt *htt) +static void ath10k_htt_rx_replenish_task(unsigned long ptr) +{ + struct ath10k_htt *htt = (struct ath10k_htt *)ptr; + ath10k_htt_rx_msdu_buff_replenish(htt); +} + +int ath10k_htt_rx_alloc(struct ath10k_htt *htt) { dma_addr_t paddr; void *vaddr; @@ -467,7 +500,7 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) htt->rx_ring.fill_level = ath10k_htt_rx_ring_fill_level(htt); htt->rx_ring.netbufs_ring = - kmalloc(htt->rx_ring.size * sizeof(struct sk_buff *), + kzalloc(htt->rx_ring.size * sizeof(struct sk_buff *), GFP_KERNEL); if (!htt->rx_ring.netbufs_ring) goto err_netbuf; @@ -501,7 +534,16 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) goto err_fill_ring; - ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n", + 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; @@ -590,138 +632,342 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr) return false; } -static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt, - struct htt_rx_info *info) +struct rfc1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +} __packed; + +struct amsdu_subframe_hdr { + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + __be16 len; +} __packed; + +static const u8 rx_legacy_rate_idx[] = { + 3, /* 0x00 - 11Mbps */ + 2, /* 0x01 - 5.5Mbps */ + 1, /* 0x02 - 2Mbps */ + 0, /* 0x03 - 1Mbps */ + 3, /* 0x04 - 11Mbps */ + 2, /* 0x05 - 5.5Mbps */ + 1, /* 0x06 - 2Mbps */ + 0, /* 0x07 - 1Mbps */ + 10, /* 0x08 - 48Mbps */ + 8, /* 0x09 - 24Mbps */ + 6, /* 0x0A - 12Mbps */ + 4, /* 0x0B - 6Mbps */ + 11, /* 0x0C - 54Mbps */ + 9, /* 0x0D - 36Mbps */ + 7, /* 0x0E - 18Mbps */ + 5, /* 0x0F - 9Mbps */ +}; + +static void ath10k_htt_rx_h_rates(struct ath10k *ar, + enum ieee80211_band band, + u8 info0, u32 info1, u32 info2, + struct ieee80211_rx_status *status) +{ + u8 cck, rate, rate_idx, bw, sgi, mcs, nss; + u8 preamble = 0; + + /* Check if valid fields */ + if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID)) + return; + + preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE); + + switch (preamble) { + case HTT_RX_LEGACY: + cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK; + rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE); + rate_idx = 0; + + if (rate < 0x08 || rate > 0x0F) + break; + + switch (band) { + case IEEE80211_BAND_2GHZ: + if (cck) + rate &= ~BIT(3); + rate_idx = rx_legacy_rate_idx[rate]; + break; + case IEEE80211_BAND_5GHZ: + rate_idx = rx_legacy_rate_idx[rate]; + /* We are using same rate table registering + HW - ath10k_rates[]. In case of 5GHz skip + CCK rates, so -4 here */ + rate_idx -= 4; + break; + default: + break; + } + + status->rate_idx = rate_idx; + break; + case HTT_RX_HT: + case HTT_RX_HT_WITH_TXBF: + /* HT-SIG - Table 20-11 in info1 and info2 */ + mcs = info1 & 0x1F; + nss = mcs >> 3; + bw = (info1 >> 7) & 1; + sgi = (info2 >> 7) & 1; + + status->rate_idx = mcs; + status->flag |= RX_FLAG_HT; + if (sgi) + status->flag |= RX_FLAG_SHORT_GI; + if (bw) + status->flag |= RX_FLAG_40MHZ; + break; + case HTT_RX_VHT: + case HTT_RX_VHT_WITH_TXBF: + /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2 + TODO check this */ + mcs = (info2 >> 4) & 0x0F; + nss = ((info1 >> 10) & 0x07) + 1; + bw = info1 & 3; + sgi = info2 & 1; + + status->rate_idx = mcs; + status->vht_nss = nss; + + if (sgi) + status->flag |= RX_FLAG_SHORT_GI; + + switch (bw) { + /* 20MHZ */ + case 0: + break; + /* 40MHZ */ + case 1: + status->flag |= RX_FLAG_40MHZ; + break; + /* 80MHZ */ + case 2: + status->vht_flag |= RX_VHT_FLAG_80MHZ; + } + + status->flag |= RX_FLAG_VHT; + break; + default: + break; + } +} + +static void ath10k_htt_rx_h_protected(struct ath10k_htt *htt, + struct ieee80211_rx_status *rx_status, + struct sk_buff *skb, + enum htt_rx_mpdu_encrypt_type enctype, + enum rx_msdu_decap_format fmt, + bool dot11frag) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + rx_status->flag &= ~(RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED); + + if (enctype == HTT_RX_MPDU_ENCRYPT_NONE) + return; + + /* + * There's no explicit rx descriptor flag to indicate whether a given + * frame has been decrypted or not. We're forced to use the decap + * format as an implicit indication. However fragmentation rx is always + * raw and it probably never reports undecrypted raws. + * + * This makes sure sniffed frames are reported as-is without stripping + * the protected flag. + */ + if (fmt == RX_MSDU_DECAP_RAW && !dot11frag) + return; + + rx_status->flag |= RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(__le16_to_cpu(hdr->frame_control) & + ~IEEE80211_FCTL_PROTECTED); +} + +static bool ath10k_htt_rx_h_channel(struct ath10k *ar, + struct ieee80211_rx_status *status) +{ + struct ieee80211_channel *ch; + + spin_lock_bh(&ar->data_lock); + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + spin_unlock_bh(&ar->data_lock); + + if (!ch) + return false; + + status->band = ch->band; + status->freq = ch->center_freq; + + return true; +} + +static void ath10k_process_rx(struct ath10k *ar, + struct ieee80211_rx_status *rx_status, + struct sk_buff *skb) +{ + struct ieee80211_rx_status *status; + + status = IEEE80211_SKB_RXCB(skb); + *status = *rx_status; + + ath10k_dbg(ATH10K_DBG_DATA, + "rx skb %p len %u %s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %imic-err %i\n", + skb, + skb->len, + status->flag == 0 ? "legacy" : "", + status->flag & RX_FLAG_HT ? "ht" : "", + status->flag & RX_FLAG_VHT ? "vht" : "", + status->flag & RX_FLAG_40MHZ ? "40" : "", + status->vht_flag & RX_VHT_FLAG_80MHZ ? "80" : "", + status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", + status->rate_idx, + status->vht_nss, + status->freq, + status->band, status->flag, + !!(status->flag & RX_FLAG_FAILED_FCS_CRC), + !!(status->flag & RX_FLAG_MMIC_ERROR)); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ", + skb->data, skb->len); + + ieee80211_rx(ar->hw, skb); +} + +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 ieee80211_rx_status *rx_status, + struct sk_buff *skb_in) { struct htt_rx_desc *rxd; - struct sk_buff *amsdu; + struct sk_buff *skb = skb_in; struct sk_buff *first; - struct ieee80211_hdr *hdr; - struct sk_buff *skb = info->skb; enum rx_msdu_decap_format fmt; enum htt_rx_mpdu_encrypt_type enctype; + struct ieee80211_hdr *hdr; + u8 hdr_buf[64], addr[ETH_ALEN], *qos; unsigned int hdr_len; - int crypto_len; rxd = (void *)skb->data - sizeof(*rxd); - fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), - RX_MSDU_START_INFO1_DECAP_FORMAT); enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); - /* FIXME: No idea what assumptions are safe here. Need logs */ - if ((fmt == RX_MSDU_DECAP_RAW && skb->next) || - (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) { - ath10k_htt_rx_free_msdu_chain(skb->next); - skb->next = NULL; - return -ENOTSUPP; - } - - /* A-MSDU max is a little less than 8K */ - amsdu = dev_alloc_skb(8*1024); - if (!amsdu) { - ath10k_warn("A-MSDU allocation failed\n"); - ath10k_htt_rx_free_msdu_chain(skb->next); - skb->next = NULL; - return -ENOMEM; - } - - if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) { - int hdrlen; - - hdr = (void *)rxd->rx_hdr_status; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen); - } + hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(hdr_buf, hdr, hdr_len); + hdr = (struct ieee80211_hdr *)hdr_buf; first = skb; while (skb) { void *decap_hdr; - int decap_len = 0; + int len; rxd = (void *)skb->data - sizeof(*rxd); fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), - RX_MSDU_START_INFO1_DECAP_FORMAT); + RX_MSDU_START_INFO1_DECAP_FORMAT); decap_hdr = (void *)rxd->rx_hdr_status; - if (skb == first) { - /* We receive linked A-MSDU subframe skbuffs. The - * first one contains the original 802.11 header (and - * possible crypto param) in the RX descriptor. The - * A-MSDU subframe header follows that. Each part is - * aligned to 4 byte boundary. */ - - hdr = (void *)amsdu->data; - hdr_len = ieee80211_hdrlen(hdr->frame_control); - crypto_len = ath10k_htt_rx_crypto_param_len(enctype); - - decap_hdr += roundup(hdr_len, 4); - decap_hdr += roundup(crypto_len, 4); - } - - if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { - /* Ethernet2 decap inserts ethernet header in place of - * A-MSDU subframe header. */ - skb_pull(skb, 6 + 6 + 2); + skb->ip_summed = ath10k_htt_rx_get_csum_state(skb); - /* A-MSDU subframe header length */ - decap_len += 6 + 6 + 2; - - /* Ethernet2 decap also strips the LLC/SNAP so we need - * to re-insert it. The LLC/SNAP follows A-MSDU - * subframe header. */ - /* FIXME: Not all LLCs are 8 bytes long */ - decap_len += 8; - - memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); + /* First frame in an A-MSDU chain has more decapped data. */ + if (skb == first) { + len = round_up(ieee80211_hdrlen(hdr->frame_control), 4); + len += round_up(ath10k_htt_rx_crypto_param_len(enctype), + 4); + decap_hdr += len; } - if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) { - /* Native Wifi decap inserts regular 802.11 header - * in place of A-MSDU subframe header. */ + switch (fmt) { + case RX_MSDU_DECAP_RAW: + /* remove trailing FCS */ + skb_trim(skb, skb->len - FCS_LEN); + break; + case RX_MSDU_DECAP_NATIVE_WIFI: + /* pull decapped header and copy DA */ hdr = (struct ieee80211_hdr *)skb->data; - skb_pull(skb, 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); - /* A-MSDU subframe header length */ - decap_len += 6 + 6 + 2; + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)hdr_buf; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); - memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); - } + /* original A-MSDU header has the bit set but we're + * not including A-MSDU subframe header */ + hdr = (struct ieee80211_hdr *)skb->data; + qos = ieee80211_get_qos_ctl(hdr); + qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - if (fmt == RX_MSDU_DECAP_RAW) - skb_trim(skb, skb->len - 4); /* remove FCS */ + /* original 802.11 header has a different DA */ + memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN); + break; + case RX_MSDU_DECAP_ETHERNET2_DIX: + /* strip ethernet header and insert decapped 802.11 + * header, amsdu subframe header and rfc1042 header */ - memcpy(skb_put(amsdu, skb->len), skb->data, skb->len); + len = 0; + len += sizeof(struct rfc1042_hdr); + len += sizeof(struct amsdu_subframe_hdr); - /* A-MSDU subframes are padded to 4bytes - * but relative to first subframe, not the whole MPDU */ - if (skb->next && ((decap_len + skb->len) & 3)) { - int padlen = 4 - ((decap_len + skb->len) & 3); - memset(skb_put(amsdu, padlen), 0, padlen); + skb_pull(skb, sizeof(struct ethhdr)); + memcpy(skb_push(skb, len), decap_hdr, len); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; + case RX_MSDU_DECAP_8023_SNAP_LLC: + /* insert decapped 802.11 header making a singly + * A-MSDU */ + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; } + skb_in = skb; + ath10k_htt_rx_h_protected(htt, rx_status, skb_in, enctype, fmt, + false); skb = skb->next; - } + skb_in->next = NULL; - info->skb = amsdu; - info->encrypt_type = enctype; + if (skb) + rx_status->flag |= RX_FLAG_AMSDU_MORE; + else + rx_status->flag &= ~RX_FLAG_AMSDU_MORE; - ath10k_htt_rx_free_msdu_chain(first); + ath10k_process_rx(htt->ar, rx_status, skb_in); + } - return 0; + /* FIXME: It might be nice to re-assemble the A-MSDU when there's a + * monitor interface active for sniffing purposes. */ } -static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) +static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, + struct ieee80211_rx_status *rx_status, + struct sk_buff *skb) { - struct sk_buff *skb = info->skb; struct htt_rx_desc *rxd; struct ieee80211_hdr *hdr; enum rx_msdu_decap_format fmt; enum htt_rx_mpdu_encrypt_type enctype; + int hdr_len; + void *rfc1042; /* This shouldn't happen. If it does than it may be a FW bug. */ if (skb->next) { - ath10k_warn("received chained non A-MSDU frame\n"); + ath10k_warn("htt rx received chained non A-MSDU frame\n"); ath10k_htt_rx_free_msdu_chain(skb->next); skb->next = NULL; } @@ -731,77 +977,52 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) RX_MSDU_START_INFO1_DECAP_FORMAT); enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); - hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN; + hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + skb->ip_summed = ath10k_htt_rx_get_csum_state(skb); switch (fmt) { case RX_MSDU_DECAP_RAW: /* remove trailing FCS */ - skb_trim(skb, skb->len - 4); + skb_trim(skb, skb->len - FCS_LEN); break; case RX_MSDU_DECAP_NATIVE_WIFI: - /* nothing to do here */ + /* Pull decapped header */ + hdr = (struct ieee80211_hdr *)skb->data; + hdr_len = ath10k_htt_rx_nwifi_hdrlen(hdr); + skb_pull(skb, hdr_len); + + /* Push original header */ + hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); break; case RX_MSDU_DECAP_ETHERNET2_DIX: - /* macaddr[6] + macaddr[6] + ethertype[2] */ - skb_pull(skb, 6 + 6 + 2); - break; - case RX_MSDU_DECAP_8023_SNAP_LLC: - /* macaddr[6] + macaddr[6] + len[2] */ - /* we don't need this for non-A-MSDU */ - skb_pull(skb, 6 + 6 + 2); - break; - } + /* strip ethernet header and insert decapped 802.11 header and + * rfc1042 header */ - if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { - void *llc; - int llclen; + rfc1042 = hdr; + rfc1042 += roundup(hdr_len, 4); + rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); - llclen = 8; - llc = hdr; - llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4); - llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); + skb_pull(skb, sizeof(struct ethhdr)); + memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)), + rfc1042, sizeof(struct rfc1042_hdr)); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; + case RX_MSDU_DECAP_8023_SNAP_LLC: + /* remove A-MSDU subframe header and insert + * decapped 802.11 header. rfc1042 header is already there */ - skb_push(skb, llclen); - memcpy(skb->data, llc, llclen); + skb_pull(skb, sizeof(struct amsdu_subframe_hdr)); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; } - if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) { - int len = ieee80211_hdrlen(hdr->frame_control); - skb_push(skb, len); - memcpy(skb->data, hdr, len); - } + ath10k_htt_rx_h_protected(htt, rx_status, skb, enctype, fmt, false); - info->skb = skb; - info->encrypt_type = enctype; - return 0; -} - -static bool ath10k_htt_rx_has_decrypt_err(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_DECRYPT_ERR) - return true; - - return false; -} - -static bool ath10k_htt_rx_has_fcs_err(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_FCS_ERR) - return true; - - return false; + ath10k_process_rx(htt->ar, rx_status, skb); } static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb) @@ -835,20 +1056,123 @@ 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 bool ath10k_htt_rx_amsdu_allowed(struct ath10k_htt *htt, + struct sk_buff *head, + enum htt_rx_mpdu_status status, + bool channel_set, + u32 attention) +{ + if (head->len == 0) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx dropping due to zero-len\n"); + return false; + } + + if (attention & RX_ATTENTION_FLAGS_DECRYPT_ERR) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx dropping due to decrypt-err\n"); + return false; + } + + if (!channel_set) { + ath10k_warn("no channel configured; ignoring frame!\n"); + return false; + } + + /* Skip mgmt frames while we handle this in WMI */ + if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL || + attention & RX_ATTENTION_FLAGS_MGMT_TYPE) { + ath10k_dbg(ATH10K_DBG_HTT, "htt rx mgmt ctrl\n"); + return false; + } + + if (status != HTT_RX_IND_MPDU_STATUS_OK && + status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR && + status != HTT_RX_IND_MPDU_STATUS_ERR_INV_PEER && + !htt->ar->monitor_started) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx ignoring frame w/ status %d\n", + status); + return false; + } + + if (test_bit(ATH10K_CAC_RUNNING, &htt->ar->dev_flags)) { + ath10k_dbg(ATH10K_DBG_HTT, + "htt rx CAC running\n"); + return false; + } + + return true; +} + static void ath10k_htt_rx_handler(struct ath10k_htt *htt, struct htt_rx_indication *rx) { - struct htt_rx_info info; + struct ieee80211_rx_status *rx_status = &htt->rx_status; struct htt_rx_indication_mpdu_range *mpdu_ranges; + struct htt_rx_desc *rxd; + enum htt_rx_mpdu_status status; struct ieee80211_hdr *hdr; int num_mpdu_ranges; + u32 attention; int fw_desc_len; u8 *fw_desc; + bool channel_set; int i, j; int ret; - int ip_summed; - memset(&info, 0, sizeof(info)); + lockdep_assert_held(&htt->rx_ring.lock); fw_desc_len = __le16_to_cpu(rx->prefix.fw_rx_desc_bytes); fw_desc = (u8 *)&rx->fw_desc; @@ -857,120 +1181,106 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx); + /* Fill this once, while this is per-ppdu */ + if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_START_VALID) { + memset(rx_status, 0, sizeof(*rx_status)); + rx_status->signal = ATH10K_DEFAULT_NOISE_FLOOR + + rx->ppdu.combined_rssi; + } + + if (rx->ppdu.info0 & HTT_RX_INDICATION_INFO0_END_VALID) { + /* TSF available only in 32-bit */ + rx_status->mactime = __le32_to_cpu(rx->ppdu.tsf) & 0xffffffff; + rx_status->flag |= RX_FLAG_MACTIME_END; + } + + channel_set = ath10k_htt_rx_h_channel(htt->ar, rx_status); + + if (channel_set) { + ath10k_htt_rx_h_rates(htt->ar, rx_status->band, + rx->ppdu.info0, + __le32_to_cpu(rx->ppdu.info1), + __le32_to_cpu(rx->ppdu.info2), + rx_status); + } + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", rx, sizeof(*rx) + (sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges)); for (i = 0; i < num_mpdu_ranges; i++) { - info.status = mpdu_ranges[i].mpdu_range_status; + status = mpdu_ranges[i].mpdu_range_status; for (j = 0; j < mpdu_ranges[i].mpdu_count; j++) { struct sk_buff *msdu_head, *msdu_tail; - enum htt_rx_mpdu_status status; - int msdu_chaining; msdu_head = NULL; msdu_tail = NULL; - msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, - &fw_desc, - &fw_desc_len, - &msdu_head, - &msdu_tail); - - if (!msdu_head) { - ath10k_warn("htt rx no data!\n"); - continue; - } - - if (msdu_head->len == 0) { - ath10k_dbg(ATH10K_DBG_HTT, - "htt rx dropping due to zero-len\n"); + ret = ath10k_htt_rx_amsdu_pop(htt, + &fw_desc, + &fw_desc_len, + &msdu_head, + &msdu_tail); + + if (ret < 0) { + ath10k_warn("failed to pop amsdu from htt rx ring %d\n", + ret); ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } - if (ath10k_htt_rx_has_decrypt_err(msdu_head)) { - ath10k_htt_rx_free_msdu_chain(msdu_head); - continue; - } + rxd = container_of((void *)msdu_head->data, + struct htt_rx_desc, + msdu_payload); + attention = __le32_to_cpu(rxd->attention.flags); - status = info.status; - - /* Skip mgmt frames while we handle this in WMI */ - if (status == HTT_RX_IND_MPDU_STATUS_MGMT_CTRL) { + if (!ath10k_htt_rx_amsdu_allowed(htt, msdu_head, + status, + channel_set, + attention)) { ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } - if (status != HTT_RX_IND_MPDU_STATUS_OK && - status != HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR && - !htt->ar->monitor_enabled) { - ath10k_dbg(ATH10K_DBG_HTT, - "htt rx ignoring frame w/ status %d\n", - status); + if (ret > 0 && + ath10k_unchain_msdu(msdu_head) < 0) { ath10k_htt_rx_free_msdu_chain(msdu_head); continue; } - /* FIXME: we do not support chaining yet. - * this needs investigation */ - if (msdu_chaining) { - ath10k_warn("msdu_chaining is true\n"); - ath10k_htt_rx_free_msdu_chain(msdu_head); - continue; - } - - /* The skb is not yet processed and it may be - * reallocated. Since the offload is in the original - * skb extract the checksum now and assign it later */ - ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); - - info.skb = msdu_head; - info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); - info.signal = ATH10K_DEFAULT_NOISE_FLOOR; - info.signal += rx->ppdu.combined_rssi; + if (attention & RX_ATTENTION_FLAGS_FCS_ERR) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + else + rx_status->flag &= ~RX_FLAG_FAILED_FCS_CRC; - info.rate.info0 = rx->ppdu.info0; - info.rate.info1 = __le32_to_cpu(rx->ppdu.info1); - info.rate.info2 = __le32_to_cpu(rx->ppdu.info2); + if (attention & RX_ATTENTION_FLAGS_TKIP_MIC_ERR) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + else + rx_status->flag &= ~RX_FLAG_MMIC_ERROR; hdr = ath10k_htt_rx_skb_get_hdr(msdu_head); if (ath10k_htt_rx_hdr_is_amsdu(hdr)) - ret = ath10k_htt_rx_amsdu(htt, &info); + ath10k_htt_rx_amsdu(htt, rx_status, msdu_head); else - ret = ath10k_htt_rx_msdu(htt, &info); - - if (ret && !info.fcs_err) { - ath10k_warn("error processing msdus %d\n", ret); - dev_kfree_skb_any(info.skb); - continue; - } - - if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data)) - ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n"); - - info.skb->ip_summed = ip_summed; - - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ", - info.skb->data, info.skb->len); - ath10k_process_rx(htt->ar, &info); + ath10k_htt_rx_msdu(htt, rx_status, msdu_head); } } - ath10k_htt_rx_msdu_buff_replenish(htt); + tasklet_schedule(&htt->rx_replenish_task); } static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, struct htt_rx_fragment_indication *frag) { struct sk_buff *msdu_head, *msdu_tail; + enum htt_rx_mpdu_encrypt_type enctype; struct htt_rx_desc *rxd; enum rx_msdu_decap_format fmt; - struct htt_rx_info info = {}; + struct ieee80211_rx_status *rx_status = &htt->rx_status; struct ieee80211_hdr *hdr; - int msdu_chaining; + int ret; bool tkip_mic_err; bool decrypt_err; u8 *fw_desc; @@ -982,23 +1292,23 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, msdu_head = NULL; msdu_tail = NULL; - msdu_chaining = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, - &msdu_head, &msdu_tail); - ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); + spin_lock_bh(&htt->rx_ring.lock); + ret = ath10k_htt_rx_amsdu_pop(htt, &fw_desc, &fw_desc_len, + &msdu_head, &msdu_tail); + spin_unlock_bh(&htt->rx_ring.lock); - if (!msdu_head) { - ath10k_warn("htt rx frag no data\n"); - return; - } + ath10k_dbg(ATH10K_DBG_HTT_DUMP, "htt rx frag ahead\n"); - if (msdu_chaining || msdu_head != msdu_tail) { - ath10k_warn("aggregation with fragmentation?!\n"); + if (ret) { + ath10k_warn("failed to pop amsdu from httr rx ring for fragmented rx %d\n", + ret); ath10k_htt_rx_free_msdu_chain(msdu_head); return; } /* FIXME: implement signal strength */ + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; hdr = (struct ieee80211_hdr *)msdu_head->data; rxd = (void *)msdu_head->data - sizeof(*rxd); @@ -1015,57 +1325,55 @@ static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, goto end; } - info.skb = msdu_head; - info.status = HTT_RX_IND_MPDU_STATUS_OK; - info.encrypt_type = MS(__le32_to_cpu(rxd->mpdu_start.info0), - RX_MPDU_START_INFO0_ENCRYPT_TYPE); - info.skb->ip_summed = ath10k_htt_rx_get_csum_state(info.skb); + enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), + RX_MPDU_START_INFO0_ENCRYPT_TYPE); + ath10k_htt_rx_h_protected(htt, rx_status, msdu_head, enctype, fmt, + true); + msdu_head->ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); - if (tkip_mic_err) { + if (tkip_mic_err) ath10k_warn("tkip mic error\n"); - info.status = HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR; - } if (decrypt_err) { ath10k_warn("decryption err in fragmented rx\n"); - dev_kfree_skb_any(info.skb); + dev_kfree_skb_any(msdu_head); goto end; } - if (info.encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) { + if (enctype != HTT_RX_MPDU_ENCRYPT_NONE) { hdrlen = ieee80211_hdrlen(hdr->frame_control); - paramlen = ath10k_htt_rx_crypto_param_len(info.encrypt_type); + paramlen = ath10k_htt_rx_crypto_param_len(enctype); /* It is more efficient to move the header than the payload */ - memmove((void *)info.skb->data + paramlen, - (void *)info.skb->data, + memmove((void *)msdu_head->data + paramlen, + (void *)msdu_head->data, hdrlen); - skb_pull(info.skb, paramlen); - hdr = (struct ieee80211_hdr *)info.skb->data; + skb_pull(msdu_head, paramlen); + hdr = (struct ieee80211_hdr *)msdu_head->data; } /* remove trailing FCS */ trim = 4; /* remove crypto trailer */ - trim += ath10k_htt_rx_crypto_tail_len(info.encrypt_type); + trim += ath10k_htt_rx_crypto_tail_len(enctype); /* last fragment of TKIP frags has MIC */ if (!ieee80211_has_morefrags(hdr->frame_control) && - info.encrypt_type == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) + enctype == HTT_RX_MPDU_ENCRYPT_TKIP_WPA) trim += 8; - if (trim > info.skb->len) { + if (trim > msdu_head->len) { ath10k_warn("htt rx fragment: trailer longer than the frame itself? drop\n"); - dev_kfree_skb_any(info.skb); + dev_kfree_skb_any(msdu_head); goto end; } - skb_trim(info.skb, info.skb->len - trim); + skb_trim(msdu_head, msdu_head->len - trim); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt frag mpdu: ", - info.skb->data, info.skb->len); - ath10k_process_rx(htt->ar, &info); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt rx frag mpdu: ", + msdu_head->data, msdu_head->len); + ath10k_process_rx(htt->ar, rx_status, msdu_head); end: if (fw_desc_len > 0) { @@ -1075,6 +1383,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; @@ -1084,7 +1431,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) if (!IS_ALIGNED((unsigned long)skb->data, 4)) ath10k_warn("unaligned htt message, expect trouble\n"); - ath10k_dbg(ATH10K_DBG_HTT, "HTT RX, msg_type: 0x%0X\n", + ath10k_dbg(ATH10K_DBG_HTT, "htt rx, msg_type: 0x%0X\n", resp->hdr.msg_type); switch (resp->hdr.msg_type) { case HTT_T2H_MSG_TYPE_VERSION_CONF: { @@ -1093,10 +1440,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, @@ -1131,44 +1480,17 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } - ath10k_txrx_tx_completed(htt, &tx_done); - 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_completed(htt, &tx_done); - } + 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: + 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; @@ -1190,8 +1512,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) case HTT_T2H_MSG_TYPE_TEST: /* FIX THIS */ break; - case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: case HTT_T2H_MSG_TYPE_STATS_CONF: + trace_ath10k_htt_stats(skb->data, skb->len); + break; + case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: case HTT_T2H_MSG_TYPE_RX_ADDBA: case HTT_T2H_MSG_TYPE_RX_DELBA: case HTT_T2H_MSG_TYPE_RX_FLUSH: @@ -1206,3 +1530,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 656c2546b29..7064354d1f4 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -83,20 +83,17 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) __clear_bit(msdu_id, htt->used_msdu_ids); } -int ath10k_htt_tx_attach(struct ath10k_htt *htt) +int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { - u8 pipe; - spin_lock_init(&htt->tx_lock); init_waitqueue_head(&htt->empty_tx_wq); - /* At the beginning free queue number should hint us the maximum - * queue length */ - pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id; - htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar, - pipe); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features)) + htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; + else + htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC; - ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n", + ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", htt->max_num_pending_tx); htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) * @@ -112,66 +109,50 @@ 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; } -static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) +static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt) { - struct sk_buff *txdesc; + 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; - txdesc = htt->pending_tx[msdu_id]; - if (!txdesc) - continue; - ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id); - if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) - ATH10K_SKB_CB(txdesc)->htt.refcount = 1; + tx_done.discard = 1; + tx_done.msdu_id = msdu_id; - ATH10K_SKB_CB(txdesc)->htt.discard = true; - ath10k_txrx_tx_unref(htt, txdesc); + ath10k_txrx_tx_unref(htt, &tx_done); } + spin_unlock_bh(&htt->tx_lock); } -void ath10k_htt_tx_detach(struct ath10k_htt *htt) +void ath10k_htt_tx_free(struct ath10k_htt *htt) { - ath10k_htt_tx_cleanup_pending(htt); + ath10k_htt_tx_free_pending(htt); kfree(htt->pending_tx); kfree(htt->used_msdu_ids); + dma_pool_destroy(htt->tx_pool); return; } void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) { - struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - struct ath10k_htt *htt = &ar->htt; - - if (skb_cb->htt.is_conf) { - dev_kfree_skb_any(skb); - return; - } - - if (skb_cb->is_aborted) { - skb_cb->htt.discard = true; - - /* if the skbuff is aborted we need to make sure we'll free up - * the tx resources, we can't simply run tx_unref() 2 times - * because if htt tx completion came in earlier we'd access - * unallocated memory */ - if (skb_cb->htt.refcount > 1) - skb_cb->htt.refcount = 1; - } - - ath10k_txrx_tx_unref(htt, skb); + dev_kfree_skb_any(skb); } int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) @@ -192,10 +173,48 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) cmd = (struct htt_cmd *)skb->data; cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ; - ATH10K_SKB_CB(skb)->htt.is_conf = true; + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) +{ + struct htt_stats_req *req; + struct sk_buff *skb; + struct htt_cmd *cmd; + int len = 0, ret; + + len += sizeof(cmd->hdr); + len += sizeof(cmd->stats_req); + + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_cmd *)skb->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_STATS_REQ; + + req = &cmd->stats_req; + + memset(req, 0, sizeof(*req)); + + /* currently we support only max 8 bit masks so no need to worry + * about endian support */ + req->upload_types[0] = mask; + req->reset_types[0] = mask; + req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID; + req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff); + req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { + ath10k_warn("failed to send htt type stats request: %d", ret); dev_kfree_skb_any(skb); return ret; } @@ -279,8 +298,6 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) #undef desc_offset - ATH10K_SKB_CB(skb)->htt.is_conf = true; - ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); @@ -293,10 +310,10 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { struct device *dev = htt->ar->dev; - struct ath10k_skb_cb *skb_cb; struct sk_buff *txdesc = NULL; struct htt_cmd *cmd; - u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + u8 vdev_id = skb_cb->vdev_id; int len = 0; int msdu_id = -1; int res; @@ -304,30 +321,32 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) res = ath10k_htt_tx_inc_pending(htt); if (res) - return res; + goto err; len += sizeof(cmd->hdr); len += sizeof(cmd->mgmt_tx); - txdesc = ath10k_htc_alloc_skb(len); - if (!txdesc) { - res = -ENOMEM; - goto err; - } - spin_lock_bh(&htt->tx_lock); - msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); - if (msdu_id < 0) { + res = ath10k_htt_tx_alloc_msdu_id(htt); + if (res < 0) { spin_unlock_bh(&htt->tx_lock); - res = msdu_id; - goto err; + goto err_tx_dec; } - htt->pending_tx[msdu_id] = txdesc; + msdu_id = res; + htt->pending_tx[msdu_id] = msdu; spin_unlock_bh(&htt->tx_lock); - res = ath10k_skb_map(dev, msdu); + txdesc = ath10k_htc_alloc_skb(len); + if (!txdesc) { + res = -ENOMEM; + goto err_free_msdu_id; + } + + 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; + goto err_free_txdesc; skb_put(txdesc, len); cmd = (struct htt_cmd *)txdesc->data; @@ -339,174 +358,184 @@ 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)); - /* refcount is decremented by HTC and HTT completions until it reaches - * zero and is freed */ - skb_cb = ATH10K_SKB_CB(txdesc); - skb_cb->htt.msdu_id = msdu_id; - skb_cb->htt.refcount = 2; - skb_cb->htt.msdu = msdu; + skb_cb->htt.txbuf = NULL; res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) - goto err; + goto err_unmap_msdu; return 0; -err: - ath10k_skb_unmap(dev, msdu); - - if (txdesc) - dev_kfree_skb_any(txdesc); - if (msdu_id >= 0) { - spin_lock_bh(&htt->tx_lock); - htt->pending_tx[msdu_id] = NULL; - ath10k_htt_tx_free_msdu_id(htt, msdu_id); - spin_unlock_bh(&htt->tx_lock); - } +err_unmap_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: + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); +err_tx_dec: ath10k_htt_tx_dec_pending(htt); +err: return res; } 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; - struct sk_buff *txdesc = NULL; - struct sk_buff *txfrag = NULL; - u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; - u8 tid; - int prefetch_len, desc_len, frag_len; - dma_addr_t frags_paddr; - int msdu_id = -1; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + 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) - return res; - - 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; - frag_len = sizeof(*tx_frags) * 2; - - txdesc = ath10k_htc_alloc_skb(desc_len); - if (!txdesc) { - res = -ENOMEM; - goto err; - } - - txfrag = dev_alloc_skb(frag_len); - if (!txfrag) { - res = -ENOMEM; - goto err; - } - - if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) { - ath10k_warn("htt alignment check failed. dropping packet.\n"); - res = -EIO; goto err; - } spin_lock_bh(&htt->tx_lock); - msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); - if (msdu_id < 0) { + res = ath10k_htt_tx_alloc_msdu_id(htt); + if (res < 0) { spin_unlock_bh(&htt->tx_lock); - res = msdu_id; - goto err; + goto err_tx_dec; } - htt->pending_tx[msdu_id] = txdesc; + msdu_id = res; + htt->pending_tx[msdu_id] = msdu; spin_unlock_bh(&htt->tx_lock); - res = ath10k_skb_map(dev, msdu); + prefetch_len = min(htt->prefetch_len, msdu->len); + prefetch_len = roundup(prefetch_len, 4); + + /* 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); + + 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; + + 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; + goto err_free_txbuf; - /* tx fragment list must be terminated with zero-entry */ - skb_put(txfrag, frag_len); - tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data; - tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr); - tx_frags[0].len = __cpu_to_le32(msdu->len); - tx_frags[1].paddr = __cpu_to_le32(0); - tx_frags[1].len = __cpu_to_le32(0); + if (likely(use_frags)) { + frags = skb_cb->htt.txbuf->frags; - res = ath10k_skb_map(dev, txfrag); - if (res) - goto err; + 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, "txfrag 0x%llx msdu 0x%llx\n", - (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr, - (unsigned long long) ATH10K_SKB_CB(msdu)->paddr); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ", - txfrag->data, frag_len); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "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; - memset(cmd, 0, desc_len); + 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; - flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, - HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); - 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; - frags_paddr = ATH10K_SKB_CB(txfrag)->paddr; - - 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); - cmd->data_tx.id = __cpu_to_le16(msdu_id); - cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr); - cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); - - memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len); - - /* refcount is decremented by HTC and HTT completions until it reaches - * zero and is freed */ - skb_cb = ATH10K_SKB_CB(txdesc); - skb_cb->htt.msdu_id = msdu_id; - skb_cb->htt.refcount = 2; - skb_cb->htt.txfrag = txfrag; - skb_cb->htt.msdu = msdu; + 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; + goto err_unmap_msdu; return 0; -err: - if (txfrag) - ath10k_skb_unmap(dev, txfrag); - if (txdesc) - dev_kfree_skb_any(txdesc); - if (txfrag) - dev_kfree_skb_any(txfrag); - if (msdu_id >= 0) { - spin_lock_bh(&htt->tx_lock); - htt->pending_tx[msdu_id] = NULL; - ath10k_htt_tx_free_msdu_id(htt, msdu_id); - spin_unlock_bh(&htt->tx_lock); - } + +err_unmap_msdu: + 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; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); +err_tx_dec: ath10k_htt_tx_dec_pending(htt); - ath10k_skb_unmap(dev, msdu); +err: return res; } diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 44ed5af0a20..007e855f4ba 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -20,28 +20,38 @@ #include "targaddrs.h" -/* Supported FW version */ -#define SUPPORTED_FW_MAJOR 1 -#define SUPPORTED_FW_MINOR 0 -#define SUPPORTED_FW_RELEASE 0 -#define SUPPORTED_FW_BUILD 629 - -/* QCA988X 1.0 definitions */ -#define QCA988X_HW_1_0_VERSION 0x4000002c -#define QCA988X_HW_1_0_FW_DIR "ath10k/QCA988X/hw1.0" -#define QCA988X_HW_1_0_FW_FILE "firmware.bin" -#define QCA988X_HW_1_0_OTP_FILE "otp.bin" -#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin" -#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234 +/* QCA988X 1.0 definitions (unsupported) */ +#define QCA988X_HW_1_0_CHIP_ID_REV 0x0 /* QCA988X 2.0 definitions */ #define QCA988X_HW_2_0_VERSION 0x4100016c +#define QCA988X_HW_2_0_CHIP_ID_REV 0x2 #define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0" #define QCA988X_HW_2_0_FW_FILE "firmware.bin" +#define QCA988X_HW_2_0_FW_2_FILE "firmware-2.bin" #define QCA988X_HW_2_0_OTP_FILE "otp.bin" #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 +#define ATH10K_FW_API2_FILE "firmware-2.bin" + +/* includes also the null byte */ +#define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" + +struct ath10k_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +enum ath10k_fw_ie_type { + ATH10K_FW_IE_FW_VERSION = 0, + ATH10K_FW_IE_TIMESTAMP = 1, + ATH10K_FW_IE_FEATURES = 2, + ATH10K_FW_IE_FW_IMAGE = 3, + ATH10K_FW_IE_OTP_IMAGE = 4, +}; + /* Known pecularities: * - current FW doesn't support raw rx mode (last tested v599) * - current FW dumps upon raw tx mode (last tested v599) @@ -53,6 +63,9 @@ enum ath10k_hw_txrx_mode { ATH10K_HW_TXRX_RAW = 0, ATH10K_HW_TXRX_NATIVE_WIFI = 1, ATH10K_HW_TXRX_ETHERNET = 2, + + /* Valid for HTT >= 3.0. Used for management frames in TX_FRM. */ + ATH10K_HW_TXRX_MGMT = 3, }; enum ath10k_mcast2ucast_mode { @@ -60,6 +73,7 @@ enum ath10k_mcast2ucast_mode { ATH10K_MCAST2UCAST_ENABLED = 1, }; +/* Target specific defines for MAIN firmware */ #define TARGET_NUM_VDEVS 8 #define TARGET_NUM_PEER_AST 2 #define TARGET_NUM_WDS_ENTRIES 32 @@ -75,7 +89,11 @@ enum ath10k_mcast2ucast_mode { #define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) #define TARGET_RX_TIMEOUT_LO_PRI 100 #define TARGET_RX_TIMEOUT_HI_PRI 40 -#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET + +/* Native Wifi decap mode is used to align IP frames to 4-byte boundaries and + * avoid a very expensive re-alignment in mac80211. */ +#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI + #define TARGET_SCAN_MAX_PENDING_REQS 4 #define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 #define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 @@ -90,6 +108,37 @@ enum ath10k_mcast2ucast_mode { #define TARGET_NUM_MSDU_DESC (1024 + 400) #define TARGET_MAX_FRAG_ENTRIES 0 +/* Target specific defines for 10.X firmware */ +#define TARGET_10X_NUM_VDEVS 16 +#define TARGET_10X_NUM_PEER_AST 2 +#define TARGET_10X_NUM_WDS_ENTRIES 32 +#define TARGET_10X_DMA_BURST_SIZE 0 +#define TARGET_10X_MAC_AGGR_DELIM 0 +#define TARGET_10X_AST_SKID_LIMIT 16 +#define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS)) +#define TARGET_10X_NUM_PEERS_MAX 128 +#define TARGET_10X_NUM_OFFLOAD_PEERS 0 +#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0 +#define TARGET_10X_NUM_PEER_KEYS 2 +#define TARGET_10X_NUM_TIDS 256 +#define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_10X_RX_TIMEOUT_LO_PRI 100 +#define TARGET_10X_RX_TIMEOUT_HI_PRI 40 +#define TARGET_10X_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI +#define TARGET_10X_SCAN_MAX_PENDING_REQS 4 +#define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV 2 +#define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV 2 +#define TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_10X_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_10X_NUM_MCAST_GROUPS 0 +#define TARGET_10X_NUM_MCAST_TABLE_ELEMS 0 +#define TARGET_10X_MCAST2UCAST_MODE ATH10K_MCAST2UCAST_DISABLED +#define TARGET_10X_TX_DBG_LOG_SIZE 1024 +#define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 +#define TARGET_10X_VOW_CONFIG 0 +#define TARGET_10X_NUM_MSDU_DESC (1024 + 400) +#define TARGET_10X_MAX_FRAG_ENTRIES 0 /* Number of Copy Engines supported */ #define CE_COUNT 8 @@ -157,8 +206,11 @@ enum ath10k_mcast2ucast_mode { #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000 #define PCIE_LOCAL_BASE_ADDRESS 0x00080000 +#define SOC_RESET_CONTROL_ADDRESS 0x00000000 #define SOC_RESET_CONTROL_OFFSET 0x00000000 #define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001 +#define SOC_RESET_CONTROL_CE_RST_MASK 0x00040000 +#define SOC_RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040 #define SOC_CPU_CLOCK_OFFSET 0x00000020 #define SOC_CPU_CLOCK_STANDARD_LSB 0 #define SOC_CPU_CLOCK_STANDARD_MASK 0x00000003 @@ -168,6 +220,12 @@ enum ath10k_mcast2ucast_mode { #define SOC_LPO_CAL_OFFSET 0x000000e0 #define SOC_LPO_CAL_ENABLE_LSB 20 #define SOC_LPO_CAL_ENABLE_MASK 0x00100000 +#define SOC_LF_TIMER_CONTROL0_ADDRESS 0x00000050 +#define SOC_LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004 + +#define SOC_CHIP_ID_ADDRESS 0x000000ec +#define SOC_CHIP_ID_REV_LSB 8 +#define SOC_CHIP_ID_REV_MASK 0x00000f00 #define WLAN_RESET_CONTROL_COLD_RST_MASK 0x00000008 #define WLAN_RESET_CONTROL_WARM_RST_MASK 0x00000004 @@ -218,8 +276,10 @@ enum ath10k_mcast2ucast_mode { #define CORE_CTRL_CPU_INTR_MASK 0x00002000 #define CORE_CTRL_ADDRESS 0x0000 #define PCIE_INTR_ENABLE_ADDRESS 0x0008 +#define PCIE_INTR_CAUSE_ADDRESS 0x000c #define PCIE_INTR_CLR_ADDRESS 0x0014 #define SCRATCH_3_ADDRESS 0x0030 +#define CPU_INTR_ADDRESS 0x0010 /* Firmware indications to the Host via SCRATCH_3 register. */ #define FW_INDICATOR_ADDRESS (SOC_CORE_BASE_ADDRESS + SCRATCH_3_ADDRESS) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index cf2ba4d850c..a21080028c5 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -54,7 +54,10 @@ static int ath10k_send_key(struct ath10k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: arg.key_cipher = WMI_CIPHER_AES_CCM; - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; + else + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; break; case WLAN_CIPHER_SUITE_TKIP: arg.key_cipher = WMI_CIPHER_TKIP; @@ -92,7 +95,7 @@ static int ath10k_install_key(struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); - INIT_COMPLETION(ar->install_key_done); + reinit_completion(&ar->install_key_done); ret = ath10k_send_key(arvif, key, cmd, macaddr); if (ret) @@ -165,7 +168,7 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif, first_errno = ret; if (ret) - ath10k_warn("could not remove peer wep key %d (%d)\n", + ath10k_warn("failed to remove peer wep key %d: %d\n", i, ret); peer->keys[i] = NULL; @@ -213,7 +216,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif, first_errno = ret; if (ret) - ath10k_warn("could not remove key for %pM\n", addr); + ath10k_warn("failed to remove key for %pM: %d\n", + addr, ret); } return first_errno; @@ -322,37 +326,95 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) lockdep_assert_held(&ar->conf_mutex); ret = ath10k_wmi_peer_create(ar, vdev_id, addr); - if (ret) + if (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) + if (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); + ar->num_peers++; + spin_unlock_bh(&ar->data_lock); + + return 0; +} + +static int ath10k_mac_set_kickout(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 param; + int ret; + + param = ar->wmi.pdev_param->sta_kickout_th; + ret = ath10k_wmi_pdev_set_param(ar, param, + ATH10K_KICKOUT_THRESHOLD); + if (ret) { + ath10k_warn("failed to set kickout threshold on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = ar->wmi.vdev_param->ap_keepalive_min_idle_inactive_time_secs; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, + ATH10K_KEEPALIVE_MIN_IDLE); + if (ret) { + ath10k_warn("failed to set keepalive minimum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = ar->wmi.vdev_param->ap_keepalive_max_idle_inactive_time_secs; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, + ATH10K_KEEPALIVE_MAX_IDLE); + if (ret) { + ath10k_warn("failed to set keepalive maximum idle time on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + param = ar->wmi.vdev_param->ap_keepalive_max_unresponsive_time_secs; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, param, + ATH10K_KEEPALIVE_MAX_UNRESPONSIVE); + if (ret) { + ath10k_warn("failed to set keepalive maximum unresponsive time on vdev %i: %d\n", + arvif->vdev_id, ret); return ret; + } return 0; } static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) { + struct ath10k *ar = arvif->ar; + u32 vdev_param; + if (value != 0xFFFFFFFF) value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold, ATH10K_RTS_MAX); - return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_RTS_THRESHOLD, - value); + vdev_param = ar->wmi.vdev_param->rts_threshold; + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value) { + struct ath10k *ar = arvif->ar; + u32 vdev_param; + if (value != 0xFFFFFFFF) value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold, ATH10K_FRAGMT_THRESHOLD_MIN, ATH10K_FRAGMT_THRESHOLD_MAX); - return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, - value); + vdev_param = ar->wmi.vdev_param->fragmentation_threshold; + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) @@ -369,6 +431,10 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) if (ret) return ret; + spin_lock_bh(&ar->data_lock); + ar->num_peers--; + spin_unlock_bh(&ar->data_lock); + return 0; } @@ -388,6 +454,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id) list_del(&peer->list); kfree(peer); + ar->num_peers--; } spin_unlock_bh(&ar->data_lock); } @@ -403,6 +470,7 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar) list_del(&peer->list); kfree(peer); } + ar->num_peers = 0; spin_unlock_bh(&ar->data_lock); } @@ -424,175 +492,114 @@ static inline int ath10k_vdev_setup_sync(struct ath10k *ar) return 0; } -static int ath10k_vdev_start(struct ath10k_vif *arvif) +static bool ath10k_monitor_is_enabled(struct ath10k *ar) { - struct ath10k *ar = arvif->ar; - struct ieee80211_conf *conf = &ar->hw->conf; - struct ieee80211_channel *channel = conf->chandef.chan; - struct wmi_vdev_start_request_arg arg = {}; - int ret = 0; - lockdep_assert_held(&ar->conf_mutex); - INIT_COMPLETION(ar->vdev_setup_done); - - arg.vdev_id = arvif->vdev_id; - arg.dtim_period = arvif->dtim_period; - arg.bcn_intval = arvif->beacon_interval; - - arg.channel.freq = channel->center_freq; - - arg.channel.band_center_freq1 = conf->chandef.center_freq1; + ath10k_dbg(ATH10K_DBG_MAC, + "mac monitor refs: promisc %d monitor %d cac %d\n", + ar->promisc, ar->monitor, + test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)); - arg.channel.mode = chan_to_phymode(&conf->chandef); - - arg.channel.min_power = channel->max_power * 3; - arg.channel.max_power = channel->max_power * 4; - arg.channel.max_reg_power = channel->max_reg_power * 4; - arg.channel.max_antenna_gain = channel->max_antenna_gain; - - if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { - arg.ssid = arvif->u.ap.ssid; - arg.ssid_len = arvif->u.ap.ssid_len; - arg.hidden_ssid = arvif->u.ap.hidden_ssid; - } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { - arg.ssid = arvif->vif->bss_conf.ssid; - arg.ssid_len = arvif->vif->bss_conf.ssid_len; - } - - ret = ath10k_wmi_vdev_start(ar, &arg); - if (ret) { - ath10k_warn("WMI vdev start failed: ret %d\n", ret); - return ret; - } - - ret = ath10k_vdev_setup_sync(ar); - if (ret) { - ath10k_warn("vdev setup failed %d\n", ret); - return ret; - } - - return ret; -} - -static int ath10k_vdev_stop(struct ath10k_vif *arvif) -{ - struct ath10k *ar = arvif->ar; - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - INIT_COMPLETION(ar->vdev_setup_done); - - ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); - if (ret) { - ath10k_warn("WMI vdev stop failed: ret %d\n", ret); - return ret; - } - - ret = ath10k_vdev_setup_sync(ar); - if (ret) { - ath10k_warn("vdev setup failed %d\n", ret); - return ret; - } - - return ret; + return ar->promisc || ar->monitor || + test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); } -static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) +static int ath10k_monitor_vdev_start(struct ath10k *ar, int vdev_id) { - struct ieee80211_channel *channel = ar->hw->conf.chandef.chan; + struct cfg80211_chan_def *chandef = &ar->chandef; + struct ieee80211_channel *channel = chandef->chan; struct wmi_vdev_start_request_arg arg = {}; - enum nl80211_channel_type type; int ret = 0; lockdep_assert_held(&ar->conf_mutex); - type = cfg80211_get_chandef_type(&ar->hw->conf.chandef); - arg.vdev_id = vdev_id; arg.channel.freq = channel->center_freq; - arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1; + arg.channel.band_center_freq1 = chandef->center_freq1; /* TODO setup this dynamically, what in case we don't have any vifs? */ - arg.channel.mode = chan_to_phymode(&ar->hw->conf.chandef); + arg.channel.mode = chan_to_phymode(chandef); + arg.channel.chan_radar = + !!(channel->flags & IEEE80211_CHAN_RADAR); - arg.channel.min_power = channel->max_power * 3; - arg.channel.max_power = channel->max_power * 4; - arg.channel.max_reg_power = channel->max_reg_power * 4; - arg.channel.max_antenna_gain = channel->max_antenna_gain; + arg.channel.min_power = 0; + arg.channel.max_power = channel->max_power * 2; + arg.channel.max_reg_power = channel->max_reg_power * 2; + arg.channel.max_antenna_gain = channel->max_antenna_gain * 2; ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { - ath10k_warn("Monitor vdev start failed: ret %d\n", ret); + ath10k_warn("failed to request monitor vdev %i start: %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("failed to synchronize setup for monitor vdev %i: %d\n", + vdev_id, ret); return ret; } ret = ath10k_wmi_vdev_up(ar, vdev_id, 0, ar->mac_addr); if (ret) { - ath10k_warn("Monitor vdev up failed: %d\n", ret); + ath10k_warn("failed to put up monitor vdev %i: %d\n", + vdev_id, ret); goto vdev_stop; } ar->monitor_vdev_id = vdev_id; - ar->monitor_enabled = true; + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i started\n", + ar->monitor_vdev_id); return 0; vdev_stop: ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("Monitor vdev stop failed: %d\n", ret); + ath10k_warn("failed to stop monitor vdev %i after start failure: %d\n", + ar->monitor_vdev_id, ret); return ret; } -static int ath10k_monitor_stop(struct ath10k *ar) +static int ath10k_monitor_vdev_stop(struct ath10k *ar) { int ret = 0; lockdep_assert_held(&ar->conf_mutex); - /* For some reasons, ath10k_wmi_vdev_down() here couse - * often ath10k_wmi_vdev_stop() to fail. Next we could - * not run monitor vdev and driver reload - * required. Don't see such problems we skip - * ath10k_wmi_vdev_down() here. - */ + ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("failed to put down monitor vdev %i: %d\n", + ar->monitor_vdev_id, ret); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) - ath10k_warn("Monitor vdev stop failed: %d\n", ret); + ath10k_warn("failed to to request monitor vdev %i stop: %d\n", + ar->monitor_vdev_id, ret); ret = ath10k_vdev_setup_sync(ar); if (ret) - ath10k_warn("Monitor_down sync failed: %d\n", ret); + ath10k_warn("failed to synchronise monitor vdev %i: %d\n", + ar->monitor_vdev_id, ret); - ar->monitor_enabled = false; + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %i stopped\n", + ar->monitor_vdev_id); return ret; } -static int ath10k_monitor_create(struct ath10k *ar) +static int ath10k_monitor_vdev_create(struct ath10k *ar) { int bit, ret = 0; lockdep_assert_held(&ar->conf_mutex); - if (ar->monitor_present) { - ath10k_warn("Monitor mode already enabled\n"); - return 0; - } - bit = ffs(ar->free_vdev_map); if (bit == 0) { - ath10k_warn("No free VDEV slots\n"); + ath10k_warn("failed to find free vdev id for monitor vdev\n"); return -ENOMEM; } @@ -603,14 +610,14 @@ 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("failed to request monitor vdev %i creation: %d\n", + ar->monitor_vdev_id, ret); goto vdev_fail; } - ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n", ar->monitor_vdev_id); - ar->monitor_present = true; return 0; vdev_fail: @@ -621,26 +628,266 @@ vdev_fail: return ret; } -static int ath10k_monitor_destroy(struct ath10k *ar) +static int ath10k_monitor_vdev_delete(struct ath10k *ar) { int ret = 0; lockdep_assert_held(&ar->conf_mutex); - if (!ar->monitor_present) - return 0; - ret = ath10k_wmi_vdev_delete(ar, ar->monitor_vdev_id); if (ret) { - ath10k_warn("WMI vdev monitor delete failed: %d\n", ret); + ath10k_warn("failed to request wmi monitor vdev %i removal: %d\n", + ar->monitor_vdev_id, ret); return ret; } ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); - ar->monitor_present = false; - ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n", + ar->monitor_vdev_id); + return ret; +} + +static int ath10k_monitor_start(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (!ath10k_monitor_is_enabled(ar)) { + ath10k_warn("trying to start monitor with no references\n"); + return 0; + } + + if (ar->monitor_started) { + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor already started\n"); + return 0; + } + + ret = ath10k_monitor_vdev_create(ar); + if (ret) { + ath10k_warn("failed to create monitor vdev: %d\n", ret); + return ret; + } + + ret = ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id); + if (ret) { + ath10k_warn("failed to start monitor vdev: %d\n", ret); + ath10k_monitor_vdev_delete(ar); + return ret; + } + + ar->monitor_started = true; + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor started\n"); + + return 0; +} + +static void ath10k_monitor_stop(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ath10k_monitor_is_enabled(ar)) { + ath10k_dbg(ATH10K_DBG_MAC, + "mac monitor will be stopped later\n"); + return; + } + + if (!ar->monitor_started) { + ath10k_dbg(ATH10K_DBG_MAC, + "mac monitor probably failed to start earlier\n"); + return; + } + + ret = ath10k_monitor_vdev_stop(ar); + if (ret) + ath10k_warn("failed to stop monitor vdev: %d\n", ret); + + ret = ath10k_monitor_vdev_delete(ar); + if (ret) + ath10k_warn("failed to delete monitor vdev: %d\n", ret); + + ar->monitor_started = false; + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor stopped\n"); +} + +static int ath10k_recalc_rtscts_prot(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 vdev_param, rts_cts = 0; + + lockdep_assert_held(&ar->conf_mutex); + + vdev_param = ar->wmi.vdev_param->enable_rtscts; + + if (arvif->use_cts_prot || arvif->num_legacy_stations > 0) + rts_cts |= SM(WMI_RTSCTS_ENABLED, WMI_RTSCTS_SET); + + if (arvif->num_legacy_stations > 0) + rts_cts |= SM(WMI_RTSCTS_ACROSS_SW_RETRIES, + WMI_RTSCTS_PROFILE); + + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, + rts_cts); +} + +static int ath10k_start_cac(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + set_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); + + ret = ath10k_monitor_start(ar); + if (ret) { + ath10k_warn("failed to start monitor (cac): %d\n", ret); + clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); + return ret; + } + + ath10k_dbg(ATH10K_DBG_MAC, "mac cac start monitor vdev %d\n", ar->monitor_vdev_id); + + return 0; +} + +static int ath10k_stop_cac(struct ath10k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + /* CAC is not running - do nothing */ + if (!test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) + return 0; + + clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); + ath10k_monitor_stop(ar); + + ath10k_dbg(ATH10K_DBG_MAC, "mac cac finished\n"); + + return 0; +} + +static void ath10k_recalc_radar_detection(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath10k_stop_cac(ar); + + if (!ar->radar_enabled) + return; + + if (ar->num_started_vdevs > 0) + return; + + ret = ath10k_start_cac(ar); + if (ret) { + /* + * Not possible to start CAC on current channel so starting + * radiation is not allowed, make this channel DFS_UNAVAILABLE + * by indicating that radar was detected. + */ + ath10k_warn("failed to start CAC: %d\n", ret); + ieee80211_radar_detected(ar->hw); + } +} + +static int ath10k_vdev_start(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct cfg80211_chan_def *chandef = &ar->chandef; + struct wmi_vdev_start_request_arg arg = {}; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_setup_done); + + arg.vdev_id = arvif->vdev_id; + arg.dtim_period = arvif->dtim_period; + arg.bcn_intval = arvif->beacon_interval; + + arg.channel.freq = chandef->chan->center_freq; + arg.channel.band_center_freq1 = chandef->center_freq1; + arg.channel.mode = chan_to_phymode(chandef); + + arg.channel.min_power = 0; + arg.channel.max_power = chandef->chan->max_power * 2; + arg.channel.max_reg_power = chandef->chan->max_reg_power * 2; + arg.channel.max_antenna_gain = chandef->chan->max_antenna_gain * 2; + + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { + arg.ssid = arvif->u.ap.ssid; + arg.ssid_len = arvif->u.ap.ssid_len; + arg.hidden_ssid = arvif->u.ap.hidden_ssid; + + /* For now allow DFS for AP mode */ + arg.channel.chan_radar = + !!(chandef->chan->flags & IEEE80211_CHAN_RADAR); + } else if (arvif->vdev_type == WMI_VDEV_TYPE_IBSS) { + arg.ssid = arvif->vif->bss_conf.ssid; + arg.ssid_len = arvif->vif->bss_conf.ssid_len; + } + + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d start center_freq %d phymode %s\n", + arg.vdev_id, arg.channel.freq, + ath10k_wmi_phymode_str(arg.channel.mode)); + + ret = ath10k_wmi_vdev_start(ar, &arg); + if (ret) { + ath10k_warn("failed to start WMI vdev %i: %d\n", + arg.vdev_id, ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("failed to synchronise setup for vdev %i: %d\n", + arg.vdev_id, ret); + return ret; + } + + ar->num_started_vdevs++; + ath10k_recalc_radar_detection(ar); + + return ret; +} + +static int ath10k_vdev_stop(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_setup_done); + + ret = ath10k_wmi_vdev_stop(ar, arvif->vdev_id); + if (ret) { + ath10k_warn("failed to stop WMI vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + ret = ath10k_vdev_setup_sync(ar); + if (ret) { + ath10k_warn("failed to syncronise setup for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + WARN_ON(ar->num_started_vdevs == 0); + + if (ar->num_started_vdevs != 0) { + ar->num_started_vdevs--; + ath10k_recalc_radar_detection(ar); + } + return ret; } @@ -653,6 +900,22 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, if (!info->enable_beacon) { ath10k_vdev_stop(arvif); + + arvif->is_started = false; + arvif->is_up = false; + + spin_lock_bh(&arvif->ar->data_lock); + if (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; + arvif->beacon_sent = false; + } + spin_unlock_bh(&arvif->ar->data_lock); + return; } @@ -662,19 +925,29 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, if (ret) return; - ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, 0, info->bssid); + arvif->aid = 0; + memcpy(arvif->bssid, info->bssid, ETH_ALEN); + + 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; } - ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id); + + arvif->is_started = true; + arvif->is_up = true; + + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); } static void ath10k_control_ibss(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info, const u8 self_peer[ETH_ALEN]) { + u32 vdev_param; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -682,84 +955,82 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, if (!info->ibss_joined) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer); if (ret) - ath10k_warn("Failed to delete IBSS self peer:%pM for VDEV:%d ret:%d\n", + ath10k_warn("failed to delete IBSS self peer %pM for vdev %d: %d\n", self_peer, arvif->vdev_id, ret); - if (is_zero_ether_addr(arvif->u.ibss.bssid)) + if (is_zero_ether_addr(arvif->bssid)) return; ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, - arvif->u.ibss.bssid); + arvif->bssid); if (ret) { - ath10k_warn("Failed to delete IBSS BSSID peer:%pM for VDEV:%d ret:%d\n", - arvif->u.ibss.bssid, arvif->vdev_id, ret); + ath10k_warn("failed to delete IBSS BSSID peer %pM for vdev %d: %d\n", + arvif->bssid, arvif->vdev_id, ret); return; } - memset(arvif->u.ibss.bssid, 0, ETH_ALEN); + memset(arvif->bssid, 0, ETH_ALEN); return; } ret = ath10k_peer_create(arvif->ar, arvif->vdev_id, self_peer); if (ret) { - ath10k_warn("Failed to create IBSS self peer:%pM for VDEV:%d ret:%d\n", + ath10k_warn("failed to create IBSS self peer %pM for vdev %d: %d\n", self_peer, arvif->vdev_id, ret); return; } - ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_ATIM_WINDOW, + vdev_param = arvif->ar->wmi.vdev_param->atim_window; + ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param, ATH10K_DEFAULT_ATIM); if (ret) - ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n", + ath10k_warn("failed to set IBSS ATIM for vdev %d: %d\n", arvif->vdev_id, ret); } /* * Review this when mac80211 gains per-interface powersave support. */ -static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { - struct ath10k_generic_iter *ar_iter = data; - struct ieee80211_conf *conf = &ar_iter->ar->hw->conf; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + struct ieee80211_conf *conf = &ar->hw->conf; enum wmi_sta_powersave_param param; enum wmi_sta_ps_mode psmode; int ret; lockdep_assert_held(&arvif->ar->conf_mutex); - if (vif->type != NL80211_IFTYPE_STATION) - return; + if (arvif->vif->type != NL80211_IFTYPE_STATION) + return 0; if (conf->flags & IEEE80211_CONF_PS) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; - ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar, - arvif->vdev_id, - param, + 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); - return; + ath10k_warn("failed to set inactivity time for vdev %d: %i\n", + arvif->vdev_id, ret); + return ret; } - - ar_iter->ret = ret; } else { psmode = WMI_STA_PS_MODE_DISABLED; } - ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id, - psmode); - if (ar_iter->ret) - ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n", - psmode, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n", - psmode, arvif->vdev_id); + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n", + arvif->vdev_id, psmode ? "enable" : "disable"); + + ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode); + if (ret) { + ath10k_warn("failed to set PS Mode %d for vdev %d: %d\n", + psmode, arvif->vdev_id, ret); + return ret; + } + + return 0; } /**********************/ @@ -880,7 +1151,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, struct wmi_peer_assoc_complete_arg *arg) { const struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - int smps; int i, n; lockdep_assert_held(&ar->conf_mutex); @@ -926,17 +1196,6 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_flags |= WMI_PEER_STBC; } - smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; - smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; - - if (smps == WLAN_HT_CAP_SM_PS_STATIC) { - arg->peer_flags |= WMI_PEER_SPATIAL_MUX; - arg->peer_flags |= WMI_PEER_STATIC_MIMOPS; - } else if (smps == WLAN_HT_CAP_SM_PS_DYNAMIC) { - arg->peer_flags |= WMI_PEER_SPATIAL_MUX; - arg->peer_flags |= WMI_PEER_DYN_MIMOPS; - } - if (ht_cap->mcs.rx_mask[1] && ht_cap->mcs.rx_mask[2]) arg->peer_rate_caps |= WMI_RC_TS_FLAG; else if (ht_cap->mcs.rx_mask[1]) @@ -946,35 +1205,44 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, if (ht_cap->mcs.rx_mask[i/8] & (1 << i%8)) arg->peer_ht_rates.rates[n++] = i; - arg->peer_ht_rates.num_rates = n; - arg->peer_num_spatial_streams = max((n+7) / 8, 1); + /* + * This is a workaround for HT-enabled STAs which break the spec + * and have no HT capabilities RX mask (no HT RX MCS map). + * + * As per spec, in section 20.3.5 Modulation and coding scheme (MCS), + * MCS 0 through 7 are mandatory in 20MHz with 800 ns GI at all STAs. + * + * Firmware asserts if such situation occurs. + */ + if (n == 0) { + arg->peer_ht_rates.num_rates = 8; + for (i = 0; i < arg->peer_ht_rates.num_rates; i++) + arg->peer_ht_rates.rates[i] = i; + } else { + arg->peer_ht_rates.num_rates = n; + arg->peer_num_spatial_streams = sta->rx_nss; + } - ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", + arg->addr, arg->peer_ht_rates.num_rates, arg->peer_num_spatial_streams); } -static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf, - struct wmi_peer_assoc_complete_arg *arg) +static int ath10k_peer_assoc_qos_ap(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta) { u32 uapsd = 0; u32 max_sp = 0; + int ret = 0; lockdep_assert_held(&ar->conf_mutex); - if (sta->wme) - arg->peer_flags |= WMI_PEER_QOS; - if (sta->wme && sta->uapsd_queues) { - ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", sta->uapsd_queues, sta->max_sp); - arg->peer_flags |= WMI_PEER_APSD; - arg->peer_flags |= WMI_RC_UAPSD_FLAG; - if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | WMI_AP_PS_UAPSD_AC3_TRIGGER_EN; @@ -992,35 +1260,40 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, if (sta->max_sp < MAX_WMI_AP_PS_PEER_PARAM_MAX_SP) max_sp = sta->max_sp; - ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, - sta->addr, - WMI_AP_PS_PEER_PARAM_UAPSD, - uapsd); + ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_UAPSD, + uapsd); + if (ret) { + ath10k_warn("failed to set ap ps peer param uapsd for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } - ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, - sta->addr, - WMI_AP_PS_PEER_PARAM_MAX_SP, - max_sp); + ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, + sta->addr, + WMI_AP_PS_PEER_PARAM_MAX_SP, + max_sp); + if (ret) { + ath10k_warn("failed to set ap ps peer param max sp for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } /* TODO setup this based on STA listen interval and beacon interval. Currently we don't know sta->listen_interval - mac80211 patch required. Currently use 10 seconds */ - ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, - sta->addr, - WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, - 10); + ret = ath10k_wmi_set_ap_ps_param(ar, arvif->vdev_id, sta->addr, + WMI_AP_PS_PEER_PARAM_AGEOUT_TIME, 10); + if (ret) { + ath10k_warn("failed to set ap ps peer param ageout time for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } } -} -static void ath10k_peer_assoc_h_qos_sta(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf, - struct wmi_peer_assoc_complete_arg *arg) -{ - if (bss_conf->qos) - arg->peer_flags |= WMI_PEER_QOS; + return 0; } static void ath10k_peer_assoc_h_vht(struct ath10k *ar, @@ -1028,14 +1301,27 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, struct wmi_peer_assoc_complete_arg *arg) { const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + u8 ampdu_factor; if (!vht_cap->vht_supported) return; arg->peer_flags |= WMI_PEER_VHT; - arg->peer_vht_caps = vht_cap->cap; + + ampdu_factor = (vht_cap->cap & + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to + * zero in VHT IE. Using it would result in degraded throughput. + * arg->peer_max_mpdu at this point contains HT max_mpdu so keep + * it if VHT max_mpdu is smaller. */ + arg->peer_max_mpdu = max(arg->peer_max_mpdu, + (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ampdu_factor)) - 1); + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) arg->peer_flags |= WMI_PEER_80MHZ; @@ -1048,7 +1334,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, arg->peer_vht_rates.tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); - ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n"); + ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", + sta->addr, arg->peer_max_mpdu, arg->peer_flags); } static void ath10k_peer_assoc_h_qos(struct ath10k *ar, @@ -1059,10 +1346,17 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, { switch (arvif->vdev_type) { case WMI_VDEV_TYPE_AP: - ath10k_peer_assoc_h_qos_ap(ar, arvif, sta, bss_conf, arg); + if (sta->wme) + arg->peer_flags |= WMI_PEER_QOS; + + if (sta->wme && sta->uapsd_queues) { + arg->peer_flags |= WMI_PEER_APSD; + arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG; + } break; case WMI_VDEV_TYPE_STA: - ath10k_peer_assoc_h_qos_sta(ar, arvif, sta, bss_conf, arg); + if (bss_conf->qos) + arg->peer_flags |= WMI_PEER_QOS; break; default: break; @@ -1076,8 +1370,6 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, { enum wmi_phy_mode phymode = MODE_UNKNOWN; - /* FIXME: add VHT */ - switch (ar->hw->conf.chandef.chan->band) { case IEEE80211_BAND_2GHZ: if (sta->ht_cap.ht_supported) { @@ -1091,7 +1383,17 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, break; case IEEE80211_BAND_5GHZ: - if (sta->ht_cap.ht_supported) { + /* + * Check VHT first. + */ + if (sta->vht_cap.vht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + phymode = MODE_11AC_VHT80; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AC_VHT40; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) + phymode = MODE_11AC_VHT20; + } else if (sta->ht_cap.ht_supported) { if (sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11NA_HT40; else @@ -1105,30 +1407,59 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, break; } + ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n", + sta->addr, ath10k_wmi_phymode_str(phymode)); + arg->peer_phymode = phymode; WARN_ON(phymode == MODE_UNKNOWN); } -static int ath10k_peer_assoc(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf) +static int ath10k_peer_assoc_prepare(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) { - struct wmi_peer_assoc_complete_arg arg; - lockdep_assert_held(&ar->conf_mutex); - memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); + memset(arg, 0, sizeof(*arg)); - ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); - ath10k_peer_assoc_h_crypto(ar, arvif, &arg); - ath10k_peer_assoc_h_rates(ar, sta, &arg); - ath10k_peer_assoc_h_ht(ar, sta, &arg); - ath10k_peer_assoc_h_vht(ar, sta, &arg); - ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg); - ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg); + ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg); + ath10k_peer_assoc_h_crypto(ar, arvif, arg); + ath10k_peer_assoc_h_rates(ar, sta, arg); + ath10k_peer_assoc_h_ht(ar, sta, arg); + ath10k_peer_assoc_h_vht(ar, sta, arg); + ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg); + ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg); - return ath10k_wmi_peer_assoc(ar, &arg); + return 0; +} + +static const u32 ath10k_smps_map[] = { + [WLAN_HT_CAP_SM_PS_STATIC] = WMI_PEER_SMPS_STATIC, + [WLAN_HT_CAP_SM_PS_DYNAMIC] = WMI_PEER_SMPS_DYNAMIC, + [WLAN_HT_CAP_SM_PS_INVALID] = WMI_PEER_SMPS_PS_NONE, + [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, +}; + +static int ath10k_setup_peer_smps(struct ath10k *ar, struct ath10k_vif *arvif, + const u8 *addr, + const struct ieee80211_sta_ht_cap *ht_cap) +{ + int smps; + + if (!ht_cap->ht_supported) + return 0; + + smps = ht_cap->cap & IEEE80211_HT_CAP_SM_PS; + smps >>= IEEE80211_HT_CAP_SM_PS_SHIFT; + + if (smps >= ARRAY_SIZE(ath10k_smps_map)) + return -EINVAL; + + return ath10k_wmi_peer_set_param(ar, arvif->vdev_id, addr, + WMI_PEER_SMPS_STATE, + ath10k_smps_map[smps]); } /* can be called only in mac80211 callbacks due to `key_count` usage */ @@ -1138,6 +1469,8 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ieee80211_sta_ht_cap ht_cap; + struct wmi_peer_assoc_complete_arg peer_arg; struct ieee80211_sta *ap_sta; int ret; @@ -1147,30 +1480,56 @@ 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 bss %pM vdev %i\n", + bss_conf->bssid, arvif->vdev_id); rcu_read_unlock(); return; } - ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf); + /* ap_sta must be accessed only within rcu section which must be left + * before calling ath10k_setup_peer_smps() which might sleep. */ + ht_cap = ap_sta->ht_cap; + + ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta, + bss_conf, &peer_arg); if (ret) { - ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid); + ath10k_warn("failed to prepare peer assoc for %pM vdev %i: %d\n", + bss_conf->bssid, arvif->vdev_id, ret); rcu_read_unlock(); return; } rcu_read_unlock(); - ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid, - bss_conf->bssid); - if (ret) - ath10k_warn("VDEV: %d up failed: ret %d\n", + ret = ath10k_wmi_peer_assoc(ar, &peer_arg); + if (ret) { + ath10k_warn("failed to run peer assoc for %pM vdev %i: %d\n", + bss_conf->bssid, arvif->vdev_id, ret); + return; + } + + ret = ath10k_setup_peer_smps(ar, arvif, bss_conf->bssid, &ht_cap); + if (ret) { + ath10k_warn("failed to setup peer SMPS for vdev %i: %d\n", arvif->vdev_id, ret); - else - ath10k_dbg(ATH10K_DBG_MAC, - "VDEV: %d associated, BSSID: %pM, AID: %d\n", - arvif->vdev_id, bss_conf->bssid, bss_conf->aid); + return; + } + + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d up (associated) bssid %pM aid %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); + + arvif->aid = bss_conf->aid; + memcpy(arvif->bssid, bss_conf->bssid, ETH_ALEN); + + ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, arvif->aid, arvif->bssid); + if (ret) { + ath10k_warn("failed to set vdev %d up: %d\n", + arvif->vdev_id, ret); + return; + } + + arvif->is_up = true; } /* @@ -1191,10 +1550,11 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, * No idea why this happens, even though VDEV-DOWN is supposed * to be analogous to link down, so just stop the VDEV. */ + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n", + arvif->vdev_id); + + /* FIXME: check return value */ ret = ath10k_vdev_stop(arvif); - if (!ret) - ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n", - arvif->vdev_id); /* * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and @@ -1203,32 +1563,68 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, * interfaces as it expects there is no rx when no interface is * running. */ + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id); + + /* FIXME: why don't we print error if wmi call fails? */ ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) - ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n", - arvif->vdev_id, ret); - ath10k_wmi_flush_tx(ar); + arvif->def_wep_key_idx = 0; - arvif->def_wep_key_index = 0; + arvif->is_started = false; + arvif->is_up = false; } static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, - struct ieee80211_sta *sta) + struct ieee80211_sta *sta, bool reassoc) { + struct wmi_peer_assoc_complete_arg peer_arg; int ret = 0; lockdep_assert_held(&ar->conf_mutex); - ret = ath10k_peer_assoc(ar, arvif, sta, NULL); + ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg); if (ret) { - ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); + ath10k_warn("failed to prepare WMI peer assoc for %pM vdev %i: %i\n", + sta->addr, arvif->vdev_id, ret); return ret; } + peer_arg.peer_reassoc = reassoc; + ret = ath10k_wmi_peer_assoc(ar, &peer_arg); + if (ret) { + ath10k_warn("failed to run peer assoc for STA %pM vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); + return ret; + } + + ret = ath10k_setup_peer_smps(ar, arvif, sta->addr, &sta->ht_cap); + if (ret) { + ath10k_warn("failed to setup peer SMPS for vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + + if (!sta->wme) { + arvif->num_legacy_stations++; + ret = ath10k_recalc_rtscts_prot(arvif); + if (ret) { + ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n", + arvif->vdev_id, 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("failed to install peer wep keys for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + ret = ath10k_peer_assoc_qos_ap(ar, arvif, sta); + if (ret) { + ath10k_warn("failed to set qos params for STA %pM for vdev %i: %d\n", + sta->addr, arvif->vdev_id, ret); return ret; } @@ -1242,9 +1638,20 @@ static int ath10k_station_disassoc(struct ath10k *ar, struct ath10k_vif *arvif, lockdep_assert_held(&ar->conf_mutex); + if (!sta->wme) { + arvif->num_legacy_stations--; + ret = ath10k_recalc_rtscts_prot(arvif); + if (ret) { + ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + } + ret = ath10k_clear_peer_keys(arvif, sta->addr); if (ret) { - ath10k_warn("could not clear all peer wep keys (%d)\n", ret); + ath10k_warn("failed to clear all peer wep keys for vdev %i: %d\n", + arvif->vdev_id, ret); return ret; } @@ -1306,19 +1713,22 @@ static int ath10k_update_channel_list(struct ath10k *ar) ch->allow_vht = true; ch->allow_ibss = - !(channel->flags & IEEE80211_CHAN_NO_IBSS); + !(channel->flags & IEEE80211_CHAN_NO_IR); ch->ht40plus = !(channel->flags & IEEE80211_CHAN_NO_HT40PLUS); - passive = channel->flags & IEEE80211_CHAN_PASSIVE_SCAN; + ch->chan_radar = + !!(channel->flags & IEEE80211_CHAN_RADAR); + + passive = channel->flags & IEEE80211_CHAN_NO_IR; ch->passive = passive; ch->freq = channel->center_freq; - ch->min_power = channel->max_power * 3; - ch->max_power = channel->max_power * 4; - ch->max_reg_power = channel->max_reg_power * 4; - ch->max_antenna_gain = channel->max_antenna_gain; + ch->min_power = 0; + ch->max_power = channel->max_power * 2; + ch->max_reg_power = channel->max_reg_power * 2; + ch->max_antenna_gain = channel->max_antenna_gain * 2; ch->reg_class_id = 0; /* FIXME */ /* FIXME: why use only legacy modes, why not any @@ -1333,8 +1743,8 @@ static int ath10k_update_channel_list(struct ath10k *ar) continue; ath10k_dbg(ATH10K_DBG_WMI, - "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", - __func__, ch - arg.channels, arg.n_channels, + "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", + ch - arg.channels, arg.n_channels, ch->freq, ch->max_power, ch->max_reg_power, ch->max_antenna_gain, ch->mode); @@ -1348,29 +1758,55 @@ static int ath10k_update_channel_list(struct ath10k *ar) return ret; } +static enum wmi_dfs_region +ath10k_mac_get_dfs_region(enum nl80211_dfs_regions dfs_region) +{ + switch (dfs_region) { + case NL80211_DFS_UNSET: + return WMI_UNINIT_DFS_DOMAIN; + case NL80211_DFS_FCC: + return WMI_FCC_DFS_DOMAIN; + case NL80211_DFS_ETSI: + return WMI_ETSI_DFS_DOMAIN; + case NL80211_DFS_JP: + return WMI_MKK4_DFS_DOMAIN; + } + return WMI_UNINIT_DFS_DOMAIN; +} + static void ath10k_regd_update(struct ath10k *ar) { struct reg_dmn_pair_mapping *regpair; int ret; + enum wmi_dfs_region wmi_dfs_reg; + enum nl80211_dfs_regions nl_dfs_reg; lockdep_assert_held(&ar->conf_mutex); ret = ath10k_update_channel_list(ar); if (ret) - ath10k_warn("could not update channel list (%d)\n", ret); + ath10k_warn("failed to update channel list: %d\n", ret); regpair = ar->ath_common.regulatory.regpair; + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) { + nl_dfs_reg = ar->dfs_detector->region; + wmi_dfs_reg = ath10k_mac_get_dfs_region(nl_dfs_reg); + } else { + wmi_dfs_reg = WMI_UNINIT_DFS_DOMAIN; + } + /* Target allows setting up per-band regdomain but ath_common provides * a combined one only */ ret = ath10k_wmi_pdev_set_regdomain(ar, - regpair->regDmnEnum, - regpair->regDmnEnum, /* 2ghz */ - regpair->regDmnEnum, /* 5ghz */ + regpair->reg_domain, + regpair->reg_domain, /* 2ghz */ + regpair->reg_domain, /* 5ghz */ regpair->reg_2ghz_ctl, - regpair->reg_5ghz_ctl); + regpair->reg_5ghz_ctl, + wmi_dfs_reg); if (ret) - ath10k_warn("could not set pdev regdomain (%d)\n", ret); + ath10k_warn("failed to set pdev regdomain: %d\n", ret); } static void ath10k_reg_notifier(struct wiphy *wiphy, @@ -1378,9 +1814,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath10k *ar = hw->priv; + bool result; ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory); + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) { + ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs region 0x%x\n", + request->dfs_region); + result = ar->dfs_detector->set_dfs_domain(ar->dfs_detector, + request->dfs_region); + if (!result) + ath10k_warn("DFS region 0x%X not supported, will trigger radar for every pulse\n", + request->dfs_region); + } + mutex_lock(&ar->conf_mutex); if (ar->state == ATH10K_STATE_ON) ath10k_regd_update(ar); @@ -1391,6 +1838,33 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, /* TX handlers */ /***************/ +static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_mgmt(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_MGMT; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + if (!is_unicast_ether_addr(ieee80211_get_DA(hdr))) + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; +} + +static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, + struct ieee80211_tx_info *info) +{ + if (info->control.vif) + return ath10k_vif_to_arvif(info->control.vif)->vdev_id; + + if (ar->monitor_started) + return ar->monitor_vdev_id; + + ath10k_warn("failed to resolve vdev id\n"); + return 0; +} + /* * Frames sent to the FW have to be in "Native Wifi" format. * Strip the QoS field from the 802.11 header. @@ -1411,6 +1885,40 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, skb_pull(skb, IEEE80211_QOS_CTL_LEN); } +static void ath10k_tx_wep_key_work(struct work_struct *work) +{ + struct ath10k_vif *arvif = container_of(work, struct ath10k_vif, + wep_key_work); + int ret, keyidx = arvif->def_wep_key_newidx; + + mutex_lock(&arvif->ar->conf_mutex); + + if (arvif->ar->state != ATH10K_STATE_ON) + goto unlock; + + if (arvif->def_wep_key_idx == keyidx) + goto unlock; + + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", + arvif->vdev_id, keyidx); + + ret = ath10k_wmi_vdev_set_param(arvif->ar, + arvif->vdev_id, + arvif->ar->wmi.vdev_param->def_keyid, + keyidx); + if (ret) { + ath10k_warn("failed to update wep key index for vdev %d: %d\n", + arvif->vdev_id, + ret); + goto unlock; + } + + arvif->def_wep_key_idx = keyidx; + +unlock: + mutex_unlock(&arvif->ar->conf_mutex); +} + static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1419,11 +1927,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; - int ret; - - /* TODO AP mode should be implemented */ - if (vif->type != NL80211_IFTYPE_STATION) - return; if (!ieee80211_has_protected(hdr->frame_control)) return; @@ -1435,20 +1938,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) key->cipher != WLAN_CIPHER_SUITE_WEP104) return; - if (key->keyidx == arvif->def_wep_key_index) + if (key->keyidx == arvif->def_wep_key_idx) return; - ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx); - - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_DEF_KEYID, - key->keyidx); - if (ret) { - ath10k_warn("could not update wep keyidx (%d)\n", ret); - return; - } - - arvif->def_wep_key_index = key->keyidx; + /* FIXME: Most likely a few frames will be TXed with an old key. Simply + * queueing frames until key index is updated is not an option because + * sk_buff may need more processing to be done, e.g. offchannel */ + arvif->def_wep_key_newidx = key->keyidx; + ieee80211_queue_work(ar->hw, &arvif->wep_key_work); } static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) @@ -1478,21 +1975,44 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int ret; + int ret = 0; - if (ieee80211_is_mgmt(hdr->frame_control)) - ret = ath10k_htt_mgmt_tx(&ar->htt, skb); - else if (ieee80211_is_nullfunc(hdr->frame_control)) + if (ar->htt.target_version_major >= 3) { + /* Since HTT 3.0 there is no separate mgmt tx command */ + ret = ath10k_htt_tx(&ar->htt, skb); + goto exit; + } + + if (ieee80211_is_mgmt(hdr->frame_control)) { + if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, + ar->fw_features)) { + if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >= + ATH10K_MAX_NUM_MGMT_PENDING) { + ath10k_warn("reached WMI management tranmist queue limit\n"); + ret = -EBUSY; + goto exit; + } + + skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb); + ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + } else { + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); + } + } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, + ar->fw_features) && + ieee80211_is_nullfunc(hdr->frame_control)) { /* FW does not report tx status properly for NullFunc frames * unless they are sent through mgmt tx path. mac80211 sends - * those frames when it detects link/beacon loss and depends on - * the tx status to be correct. */ + * those frames when it detects link/beacon loss and depends + * on the tx status to be correct. */ ret = ath10k_htt_mgmt_tx(&ar->htt, skb); - else + } else { ret = ath10k_htt_tx(&ar->htt, skb); + } +exit: if (ret) { - ath10k_warn("tx failed (%d). dropping packet.\n", ret); + ath10k_warn("failed to transmit packet, dropping: %d\n", ret); ieee80211_free_txskb(ar->hw, skb); } } @@ -1534,30 +2054,31 @@ void ath10k_offchan_tx_work(struct work_struct *work) mutex_lock(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n", skb); hdr = (struct ieee80211_hdr *)skb->data; peer_addr = ieee80211_get_DA(hdr); - vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id; + vdev_id = ATH10K_SKB_CB(skb)->vdev_id; spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find(ar, vdev_id, peer_addr); spin_unlock_bh(&ar->data_lock); if (peer) + /* FIXME: should this use ath10k_warn()? */ ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n", peer_addr, vdev_id); if (!peer) { ret = ath10k_peer_create(ar, vdev_id, peer_addr); if (ret) - ath10k_warn("peer %pM on vdev %d not created (%d)\n", + ath10k_warn("failed to create peer %pM on vdev %d: %d\n", peer_addr, vdev_id, ret); } spin_lock_bh(&ar->data_lock); - INIT_COMPLETION(ar->offchan_tx_completed); + reinit_completion(&ar->offchan_tx_completed); ar->offchan_tx_skb = skb; spin_unlock_bh(&ar->data_lock); @@ -1572,7 +2093,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) if (!peer) { ret = ath10k_peer_delete(ar, vdev_id, peer_addr); if (ret) - ath10k_warn("peer %pM on vdev %d not deleted (%d)\n", + ath10k_warn("failed to delete peer %pM on vdev %d: %d\n", peer_addr, vdev_id, ret); } @@ -1580,6 +2101,39 @@ void ath10k_offchan_tx_work(struct work_struct *work) } } +void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar) +{ + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->wmi_mgmt_tx_queue); + if (!skb) + break; + + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work); + struct sk_buff *skb; + int ret; + + for (;;) { + skb = skb_dequeue(&ar->wmi_mgmt_tx_queue); + if (!skb) + break; + + ret = ath10k_wmi_mgmt_tx(ar, skb); + if (ret) { + ath10k_warn("failed to transmit management frame via WMI: %d\n", + ret); + ieee80211_free_txskb(ar->hw, skb); + } + } +} + /************/ /* Scanning */ /************/ @@ -1599,7 +2153,7 @@ void ath10k_reset_scan(unsigned long ptr) return; } - ath10k_warn("scan timeout. resetting. fw issue?\n"); + ath10k_warn("scan timed out, firmware problem?\n"); if (ar->scan.is_roc) ieee80211_remain_on_channel_expired(ar->hw); @@ -1635,7 +2189,7 @@ static int ath10k_abort_scan(struct ath10k *ar) ret = ath10k_wmi_stop_scan(ar, &arg); if (ret) { - ath10k_warn("could not submit wmi stop scan (%d)\n", ret); + ath10k_warn("failed to stop wmi scan: %d\n", ret); spin_lock_bh(&ar->data_lock); ar->scan.in_progress = false; ath10k_offchan_tx_purge(ar); @@ -1643,8 +2197,6 @@ static int ath10k_abort_scan(struct ath10k *ar) return -EIO; } - ath10k_wmi_flush_tx(ar); - ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ); if (ret == 0) ath10k_warn("timed out while waiting for scan to stop\n"); @@ -1657,7 +2209,7 @@ static int ath10k_abort_scan(struct ath10k *ar) spin_lock_bh(&ar->data_lock); if (ar->scan.in_progress) { - ath10k_warn("could not stop scan. its still in progress\n"); + ath10k_warn("failed to stop scan, it's still in progress\n"); ar->scan.in_progress = false; ath10k_offchan_tx_purge(ar); ret = -ETIMEDOUT; @@ -1678,10 +2230,6 @@ static int ath10k_start_scan(struct ath10k *ar, if (ret) return ret; - /* make sure we submit the command so the completion - * timeout makes sense */ - ath10k_wmi_flush_tx(ar); - ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ); if (ret == 0) { ath10k_abort_scan(ar); @@ -1709,16 +2257,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif = NULL; - u32 vdev_id = 0; - u8 tid; - - if (info->control.vif) { - arvif = ath10k_vif_to_arvif(info->control.vif); - vdev_id = arvif->vdev_id; - } else if (ar->monitor_enabled) { - vdev_id = ar->monitor_vdev_id; - } + u8 tid, vdev_id; /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) @@ -1726,12 +2265,8 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* we must calculate tid before we apply qos workaround * as we'd lose the qos control field */ - tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; - if (ieee80211_is_data_qos(hdr->frame_control) && - is_unicast_ether_addr(ieee80211_get_DA(hdr))) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - } + tid = ath10k_tx_h_get_tid(hdr); + vdev_id = ath10k_tx_h_get_vdev_id(ar, info); /* it makes no sense to process injected frames like that */ if (info->control.vif && @@ -1742,14 +2277,14 @@ static void ath10k_tx(struct ieee80211_hw *hw, ath10k_tx_h_seq_no(skb); } - memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); - ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; + ATH10K_SKB_CB(skb)->vdev_id = vdev_id; + ATH10K_SKB_CB(skb)->htt.is_offchan = false; ATH10K_SKB_CB(skb)->htt.tid = tid; if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { spin_lock_bh(&ar->data_lock); ATH10K_SKB_CB(skb)->htt.is_offchan = true; - ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id; + ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; spin_unlock_bh(&ar->data_lock); ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); @@ -1762,117 +2297,330 @@ static void ath10k_tx(struct ieee80211_hw *hw, ath10k_tx_htt(ar, skb); } -/* - * Initialize various parameters with default vaules. - */ +/* Must not be called with conf_mutex held as workers can use that also. */ +static void ath10k_drain_tx(struct ath10k *ar) +{ + /* make sure rcu-protected mac80211 tx path itself is drained */ + synchronize_net(); + + ath10k_offchan_tx_purge(ar); + ath10k_mgmt_over_wmi_tx_purge(ar); + + cancel_work_sync(&ar->offchan_tx_work); + cancel_work_sync(&ar->wmi_mgmt_tx_work); +} + void ath10k_halt(struct ath10k *ar) { + struct ath10k_vif *arvif; + lockdep_assert_held(&ar->conf_mutex); + if (ath10k_monitor_is_enabled(ar)) { + clear_bit(ATH10K_CAC_RUNNING, &ar->dev_flags); + ar->promisc = false; + ar->monitor = false; + ath10k_monitor_stop(ar); + } + del_timer_sync(&ar->scan.timeout); - ath10k_offchan_tx_purge(ar); + ath10k_reset_scan((unsigned long)ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); spin_lock_bh(&ar->data_lock); - if (ar->scan.in_progress) { - del_timer(&ar->scan.timeout); - ar->scan.in_progress = false; - ieee80211_scan_completed(ar->hw, true); + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->beacon) + continue; + + 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; } spin_unlock_bh(&ar->data_lock); } +static int ath10k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) +{ + struct ath10k *ar = hw->priv; + + mutex_lock(&ar->conf_mutex); + + if (ar->cfg_tx_chainmask) { + *tx_ant = ar->cfg_tx_chainmask; + *rx_ant = ar->cfg_rx_chainmask; + } else { + *tx_ant = ar->supp_tx_chainmask; + *rx_ant = ar->supp_rx_chainmask; + } + + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int __ath10k_set_antenna(struct ath10k *ar, u32 tx_ant, u32 rx_ant) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ar->cfg_tx_chainmask = tx_ant; + ar->cfg_rx_chainmask = rx_ant; + + if ((ar->state != ATH10K_STATE_ON) && + (ar->state != ATH10K_STATE_RESTARTED)) + return 0; + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->tx_chain_mask, + tx_ant); + if (ret) { + ath10k_warn("failed to set tx-chainmask: %d, req 0x%x\n", + ret, tx_ant); + return ret; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->rx_chain_mask, + rx_ant); + if (ret) { + ath10k_warn("failed to set rx-chainmask: %d, req 0x%x\n", + ret, rx_ant); + return ret; + } + + return 0; +} + +static int ath10k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) +{ + struct ath10k *ar = hw->priv; + int ret; + + mutex_lock(&ar->conf_mutex); + ret = __ath10k_set_antenna(ar, tx_ant, rx_ant); + mutex_unlock(&ar->conf_mutex); + return ret; +} + static int ath10k_start(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; int ret = 0; + /* + * This makes sense only when restarting hw. It is harmless to call + * uncoditionally. This is necessary to make sure no HTT/WMI tx + * commands will be submitted while restarting. + */ + ath10k_drain_tx(ar); + mutex_lock(&ar->conf_mutex); - if (ar->state != ATH10K_STATE_OFF && - ar->state != ATH10K_STATE_RESTARTING) { + switch (ar->state) { + case ATH10K_STATE_OFF: + ar->state = ATH10K_STATE_ON; + break; + case ATH10K_STATE_RESTARTING: + ath10k_halt(ar); + ar->state = ATH10K_STATE_RESTARTED; + break; + case ATH10K_STATE_ON: + case ATH10K_STATE_RESTARTED: + case ATH10K_STATE_WEDGED: + WARN_ON(1); ret = -EINVAL; - goto exit; + goto err; } ret = ath10k_hif_power_up(ar); if (ret) { - ath10k_err("could not init hif (%d)\n", ret); - ar->state = ATH10K_STATE_OFF; - goto exit; + ath10k_err("Could not init hif: %d\n", ret); + goto err_off; } ret = ath10k_core_start(ar); if (ret) { - ath10k_err("could not init core (%d)\n", ret); - ath10k_hif_power_down(ar); - ar->state = ATH10K_STATE_OFF; - goto exit; + ath10k_err("Could not init core: %d\n", ret); + goto err_power_down; } - if (ar->state == ATH10K_STATE_OFF) - ar->state = ATH10K_STATE_ON; - else if (ar->state == ATH10K_STATE_RESTARTING) - ar->state = ATH10K_STATE_RESTARTED; + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); + if (ret) { + ath10k_warn("failed to enable PMF QOS: %d\n", ret); + goto err_core_stop; + } - ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); - if (ret) - ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", - ret); + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 1); + if (ret) { + ath10k_warn("failed to enable dynamic BW: %d\n", ret); + goto err_core_stop; + } - ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0); - if (ret) - ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", + if (ar->cfg_tx_chainmask) + __ath10k_set_antenna(ar, ar->cfg_tx_chainmask, + ar->cfg_rx_chainmask); + + /* + * By default FW set ARP frames ac to voice (6). In that case ARP + * exchange is not working properly for UAPSD enabled AP. ARP requests + * which arrives with access category 0 are processed by network stack + * and send back with access category 0, but FW changes access category + * to 6. Set ARP frames access category to best effort (0) solves + * this problem. + */ + + ret = ath10k_wmi_pdev_set_param(ar, + ar->wmi.pdev_param->arp_ac_override, 0); + if (ret) { + ath10k_warn("failed to set arp ac override parameter: %d\n", ret); + goto err_core_stop; + } + ar->num_started_vdevs = 0; ath10k_regd_update(ar); -exit: mutex_unlock(&ar->conf_mutex); return 0; + +err_core_stop: + ath10k_core_stop(ar); + +err_power_down: + ath10k_hif_power_down(ar); + +err_off: + ar->state = ATH10K_STATE_OFF; + +err: + mutex_unlock(&ar->conf_mutex); + return ret; } static void ath10k_stop(struct ieee80211_hw *hw) { struct ath10k *ar = hw->priv; + ath10k_drain_tx(ar); + mutex_lock(&ar->conf_mutex); - if (ar->state == ATH10K_STATE_ON || - ar->state == ATH10K_STATE_RESTARTED || - ar->state == ATH10K_STATE_WEDGED) + if (ar->state != ATH10K_STATE_OFF) { ath10k_halt(ar); - - ar->state = ATH10K_STATE_OFF; + ar->state = ATH10K_STATE_OFF; + } mutex_unlock(&ar->conf_mutex); - cancel_work_sync(&ar->offchan_tx_work); cancel_work_sync(&ar->restart_work); } -static void ath10k_config_ps(struct ath10k *ar) +static int ath10k_config_ps(struct ath10k *ar) { - struct ath10k_generic_iter ar_iter; + struct ath10k_vif *arvif; + int ret = 0; lockdep_assert_held(&ar->conf_mutex); - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar->state == ATH10K_STATE_RESTARTED) - return; + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath10k_mac_vif_setup_ps(arvif); + if (ret) { + ath10k_warn("failed to setup powersave: %d\n", ret); + break; + } + } - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + return ret; +} - ieee80211_iterate_active_interfaces_atomic( - ar->hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_ps_iter, &ar_iter); +static const char *chandef_get_width(enum nl80211_chan_width width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return "20 (noht)"; + case NL80211_CHAN_WIDTH_20: + return "20"; + case NL80211_CHAN_WIDTH_40: + return "40"; + case NL80211_CHAN_WIDTH_80: + return "80"; + case NL80211_CHAN_WIDTH_80P80: + return "80+80"; + case NL80211_CHAN_WIDTH_160: + return "160"; + case NL80211_CHAN_WIDTH_5: + return "5"; + case NL80211_CHAN_WIDTH_10: + return "10"; + } + return "?"; +} - if (ar_iter.ret) - ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret); +static void ath10k_config_chan(struct ath10k *ar) +{ + struct ath10k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ath10k_dbg(ATH10K_DBG_MAC, + "mac config channel to %dMHz (cf1 %dMHz cf2 %dMHz width %s)\n", + ar->chandef.chan->center_freq, + ar->chandef.center_freq1, + ar->chandef.center_freq2, + chandef_get_width(ar->chandef.width)); + + /* First stop monitor interface. Some FW versions crash if there's a + * lone monitor interface. */ + if (ar->monitor_started) + ath10k_monitor_vdev_stop(ar); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_started) + continue; + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + continue; + + ret = ath10k_vdev_stop(arvif); + if (ret) { + ath10k_warn("failed to stop vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + /* all vdevs are now stopped - now attempt to restart them */ + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_started) + continue; + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + continue; + + ret = ath10k_vdev_start(arvif); + if (ret) { + ath10k_warn("failed to start vdev %d: %d\n", + arvif->vdev_id, ret); + continue; + } + + if (!arvif->is_up) + continue; + + ret = ath10k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, + arvif->bssid); + if (ret) { + ath10k_warn("failed to bring vdev up %d: %d\n", + arvif->vdev_id, ret); + continue; + } + } + + if (ath10k_monitor_is_enabled(ar)) + ath10k_monitor_vdev_start(ar, ar->monitor_vdev_id); } static int ath10k_config(struct ieee80211_hw *hw, u32 changed) @@ -1880,28 +2628,68 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) struct ath10k *ar = hw->priv; struct ieee80211_conf *conf = &hw->conf; int ret = 0; + u32 param; mutex_lock(&ar->conf_mutex); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n", - conf->chandef.chan->center_freq); + ath10k_dbg(ATH10K_DBG_MAC, + "mac config channel %dMHz flags 0x%x radar %d\n", + conf->chandef.chan->center_freq, + conf->chandef.chan->flags, + conf->radar_enabled); + spin_lock_bh(&ar->data_lock); ar->rx_channel = conf->chandef.chan; spin_unlock_bh(&ar->data_lock); + + ar->radar_enabled = conf->radar_enabled; + ath10k_recalc_radar_detection(ar); + + if (!cfg80211_chandef_identical(&ar->chandef, &conf->chandef)) { + ar->chandef = conf->chandef; + ath10k_config_chan(ar); + } + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + ath10k_dbg(ATH10K_DBG_MAC, "mac config power %d\n", + hw->conf.power_level); + + param = ar->wmi.pdev_param->txpower_limit2g; + ret = ath10k_wmi_pdev_set_param(ar, param, + hw->conf.power_level * 2); + if (ret) + ath10k_warn("failed to set 2g txpower %d: %d\n", + hw->conf.power_level, ret); + + param = ar->wmi.pdev_param->txpower_limit5g; + ret = ath10k_wmi_pdev_set_param(ar, param, + hw->conf.power_level * 2); + if (ret) + ath10k_warn("failed to set 5g txpower %d: %d\n", + hw->conf.power_level, ret); } if (changed & IEEE80211_CONF_CHANGE_PS) ath10k_config_ps(ar); if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - if (conf->flags & IEEE80211_CONF_MONITOR) - ret = ath10k_monitor_create(ar); - else - ret = ath10k_monitor_destroy(ar); + if (conf->flags & IEEE80211_CONF_MONITOR && !ar->monitor) { + ar->monitor = true; + ret = ath10k_monitor_start(ar); + if (ret) { + ath10k_warn("failed to start monitor (config): %d\n", + ret); + ar->monitor = false; + } + } else if (!(conf->flags & IEEE80211_CONF_MONITOR) && + ar->monitor) { + ar->monitor = false; + ath10k_monitor_stop(ar); + } } - ath10k_wmi_flush_tx(ar); mutex_unlock(&ar->conf_mutex); return ret; } @@ -1922,6 +2710,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, int ret = 0; u32 value; int bit; + u32 vdev_param; mutex_lock(&ar->conf_mutex); @@ -1930,21 +2719,17 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->ar = ar; arvif->vif = vif; - if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) { - ath10k_warn("Only one monitor interface allowed\n"); - ret = -EBUSY; - goto exit; - } + INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work); + INIT_LIST_HEAD(&arvif->list); bit = ffs(ar->free_vdev_map); if (bit == 0) { ret = -EBUSY; - goto exit; + goto err; } arvif->vdev_id = bit - 1; arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; - ar->free_vdev_map &= ~(1 << arvif->vdev_id); if (ar->p2p) arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; @@ -1973,32 +2758,52 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, break; } - ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n", arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype); ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, vif->addr); if (ret) { - ath10k_warn("WMI vdev create failed: ret %d\n", ret); - goto exit; + ath10k_warn("failed to create WMI vdev %i: %d\n", + arvif->vdev_id, ret); + goto err; } - ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID, - arvif->def_wep_key_index); - if (ret) - ath10k_warn("Failed to set default keyid: %d\n", ret); + ar->free_vdev_map &= ~BIT(arvif->vdev_id); + list_add(&arvif->list, &ar->arvifs); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_TX_ENCAP_TYPE, + vdev_param = ar->wmi.vdev_param->def_keyid; + ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, + arvif->def_wep_key_idx); + if (ret) { + ath10k_warn("failed to set vdev %i default key id: %d\n", + arvif->vdev_id, ret); + goto err_vdev_delete; + } + + vdev_param = ar->wmi.vdev_param->tx_encap_type; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ATH10K_HW_TXRX_NATIVE_WIFI); - if (ret) - ath10k_warn("Failed to set TX encap: %d\n", ret); + /* 10.X firmware does not support this VDEV parameter. Do not warn */ + if (ret && ret != -EOPNOTSUPP) { + ath10k_warn("failed to set vdev %i TX encapsulation: %d\n", + 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); - goto exit; + 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 vdev %i kickout parameters: %d\n", + arvif->vdev_id, ret); + goto err_peer_delete; } } @@ -2007,39 +2812,62 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; 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); + if (ret) { + ath10k_warn("failed to set vdev %i RX wake policy: %d\n", + arvif->vdev_id, ret); + goto err_peer_delete; + } param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; 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); + if (ret) { + ath10k_warn("failed to set vdev %i TX wake thresh: %d\n", + arvif->vdev_id, ret); + goto err_peer_delete; + } param = WMI_STA_PS_PARAM_PSPOLL_COUNT; value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; 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); + if (ret) { + ath10k_warn("failed to set vdev %i PSPOLL count: %d\n", + arvif->vdev_id, ret); + goto err_peer_delete; + } } ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); - if (ret) - ath10k_warn("failed to set rts threshold for vdev %d (%d)\n", + if (ret) { + ath10k_warn("failed to set rts threshold for vdev %d: %d\n", arvif->vdev_id, ret); + goto err_peer_delete; + } ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold); - if (ret) - ath10k_warn("failed to set frag threshold for vdev %d (%d)\n", + if (ret) { + ath10k_warn("failed to set frag threshold for vdev %d: %d\n", arvif->vdev_id, ret); + goto err_peer_delete; + } - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) - ar->monitor_present = true; + mutex_unlock(&ar->conf_mutex); + return 0; -exit: +err_peer_delete: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) + ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr); + +err_vdev_delete: + ath10k_wmi_vdev_delete(ar, arvif->vdev_id); + ar->free_vdev_map &= ~BIT(arvif->vdev_id); + list_del(&arvif->list); + +err: mutex_unlock(&ar->conf_mutex); + return ret; } @@ -2052,24 +2880,37 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id); + cancel_work_sync(&arvif->wep_key_work); + + spin_lock_bh(&ar->data_lock); + if (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; + } + spin_unlock_bh(&ar->data_lock); ar->free_vdev_map |= 1 << (arvif->vdev_id); + list_del(&arvif->list); if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); if (ret) - ath10k_warn("Failed to remove peer for AP: %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 %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); - - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) - ar->monitor_present = false; + ath10k_warn("failed to delete WMI vdev %i: %d\n", + arvif->vdev_id, ret); ath10k_peer_cleanup(ar, arvif->vdev_id); @@ -2103,20 +2944,17 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw, *total_flags &= SUPPORTED_FILTERS; ar->filter_flags = *total_flags; - if ((ar->filter_flags & FIF_PROMISC_IN_BSS) && - !ar->monitor_enabled) { - ret = ath10k_monitor_start(ar, ar->monitor_vdev_id); - if (ret) - ath10k_warn("Unable to start monitor mode\n"); - else - ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n"); - } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && - ar->monitor_enabled) { - ret = ath10k_monitor_stop(ar); - if (ret) - ath10k_warn("Unable to stop monitor mode\n"); - else - ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n"); + if (ar->filter_flags & FIF_PROMISC_IN_BSS && !ar->promisc) { + ar->promisc = true; + ret = ath10k_monitor_start(ar); + if (ret) { + ath10k_warn("failed to start monitor (promisc): %d\n", + ret); + ar->promisc = false; + } + } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && ar->promisc) { + ar->promisc = false; + ath10k_monitor_stop(ar); } mutex_unlock(&ar->conf_mutex); @@ -2130,6 +2968,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret = 0; + u32 vdev_param, pdev_param; mutex_lock(&ar->conf_mutex); @@ -2138,44 +2977,44 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_INT) { arvif->beacon_interval = info->beacon_int; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_BEACON_INTERVAL, + vdev_param = ar->wmi.vdev_param->beacon_interval; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->beacon_interval); + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d beacon_interval %d\n", + arvif->vdev_id, arvif->beacon_interval); + if (ret) - ath10k_warn("Failed to set beacon interval for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Beacon interval: %d set for VDEV: %d\n", - arvif->beacon_interval, arvif->vdev_id); + ath10k_warn("failed to set beacon interval for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_BEACON) { - ret = ath10k_wmi_pdev_set_param(ar, - WMI_PDEV_PARAM_BEACON_TX_MODE, + ath10k_dbg(ATH10K_DBG_MAC, + "vdev %d set beacon tx mode to staggered\n", + arvif->vdev_id); + + pdev_param = ar->wmi.pdev_param->beacon_tx_mode; + 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); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set staggered 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) { arvif->dtim_period = info->dtim_period; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_DTIM_PERIOD, + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d dtim_period %d\n", + arvif->vdev_id, arvif->dtim_period); + + vdev_param = ar->wmi.vdev_param->dtim_period; + 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); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set dtim period: %d for VDEV: %d\n", - arvif->dtim_period, arvif->vdev_id); + ath10k_warn("failed to set dtim period for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_SSID && @@ -2186,32 +3025,42 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, arvif->u.ap.hidden_ssid = info->hidden_ssid; } - if (changed & BSS_CHANGED_BSSID) { + /* + * Firmware manages AP self-peer internally so make sure to not create + * it in driver. Otherwise AP self-peer deletion may timeout later. + */ + if (changed & BSS_CHANGED_BSSID && + vif->type != NL80211_IFTYPE_AP) { if (!is_zero_ether_addr(info->bssid)) { + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d create peer %pM\n", + arvif->vdev_id, info->bssid); + ret = ath10k_peer_create(ar, arvif->vdev_id, info->bssid); if (ret) - ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", - info->bssid, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Added peer: %pM for VDEV: %d\n", - info->bssid, arvif->vdev_id); - + 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) { /* * this is never erased as we it for crypto key * clearing; this is FW requirement */ - memcpy(arvif->u.sta.bssid, info->bssid, - ETH_ALEN); + memcpy(arvif->bssid, info->bssid, ETH_ALEN); + + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d start %pM\n", + arvif->vdev_id, info->bssid); ret = ath10k_vdev_start(arvif); - if (!ret) - ath10k_dbg(ATH10K_DBG_MAC, - "VDEV: %d started with BSSID: %pM\n", - arvif->vdev_id, info->bssid); + if (ret) { + ath10k_warn("failed to start vdev %i: %d\n", + arvif->vdev_id, ret); + goto exit; + } + + arvif->is_started = true; } /* @@ -2220,7 +3069,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, * IBSS in order to remove BSSID peer. */ if (vif->type == NL80211_IFTYPE_ADHOC) - memcpy(arvif->u.ibss.bssid, info->bssid, + memcpy(arvif->bssid, info->bssid, ETH_ALEN); } } @@ -2229,22 +3078,14 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_control_beaconing(arvif, info); if (changed & BSS_CHANGED_ERP_CTS_PROT) { - u32 cts_prot; - if (info->use_cts_prot) - cts_prot = 1; - else - cts_prot = 0; + arvif->use_cts_prot = info->use_cts_prot; + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n", + arvif->vdev_id, info->use_cts_prot); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_ENABLE_RTSCTS, - cts_prot); + ret = ath10k_recalc_rtscts_prot(arvif); if (ret) - ath10k_warn("Failed to set CTS prot for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set CTS prot: %d for VDEV: %d\n", - cts_prot, arvif->vdev_id); + ath10k_warn("failed to recalculate rts/cts prot for vdev %d: %d\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_ERP_SLOT) { @@ -2255,16 +3096,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_SLOT_TIME, + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n", + arvif->vdev_id, slottime); + + vdev_param = ar->wmi.vdev_param->slot_time; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, slottime); if (ret) - ath10k_warn("Failed to set erp slot for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set slottime: %d for VDEV: %d\n", - slottime, 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) { @@ -2274,16 +3114,16 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else preamble = WMI_VDEV_PREAMBLE_LONG; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_PREAMBLE, + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d preamble %dn", + arvif->vdev_id, preamble); + + vdev_param = ar->wmi.vdev_param->preamble; + 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); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set preamble: %d for VDEV: %d\n", - preamble, arvif->vdev_id); + ath10k_warn("failed to set preamble for vdev %d: %i\n", + arvif->vdev_id, ret); } if (changed & BSS_CHANGED_ASSOC) { @@ -2291,6 +3131,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_bss_assoc(hw, vif, info); } +exit: mutex_unlock(&ar->conf_mutex); } @@ -2313,8 +3154,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, goto exit; } - INIT_COMPLETION(ar->scan.started); - INIT_COMPLETION(ar->scan.completed); + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); ar->scan.in_progress = true; ar->scan.aborting = false; ar->scan.is_roc = false; @@ -2352,7 +3193,7 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw, ret = ath10k_start_scan(ar, &arg); if (ret) { - ath10k_warn("could not start hw scan (%d)\n", ret); + ath10k_warn("failed to start hw scan: %d\n", ret); spin_lock_bh(&ar->data_lock); ar->scan.in_progress = false; spin_unlock_bh(&ar->data_lock); @@ -2372,13 +3213,50 @@ static void ath10k_cancel_hw_scan(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ret = ath10k_abort_scan(ar); if (ret) { - ath10k_warn("couldn't abort scan (%d). forcefully sending scan completion to mac80211\n", - ret); + ath10k_warn("failed to abort scan: %d\n", ret); ieee80211_scan_completed(hw, 1 /* aborted */); } mutex_unlock(&ar->conf_mutex); } +static void ath10k_set_key_h_def_keyidx(struct ath10k *ar, + struct ath10k_vif *arvif, + enum set_key_cmd cmd, + struct ieee80211_key_conf *key) +{ + u32 vdev_param = arvif->ar->wmi.vdev_param->def_keyid; + int ret; + + /* 10.1 firmware branch requires default key index to be set to group + * key index after installing it. Otherwise FW/HW Txes corrupted + * frames with multi-vif APs. This is not required for main firmware + * branch (e.g. 636). + * + * FIXME: This has been tested only in AP. It remains unknown if this + * is required for multi-vif STA interfaces on 10.1 */ + + if (arvif->vdev_type != WMI_VDEV_TYPE_AP) + return; + + if (key->cipher == WLAN_CIPHER_SUITE_WEP40) + return; + + if (key->cipher == WLAN_CIPHER_SUITE_WEP104) + return; + + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) + return; + + if (cmd != SET_KEY) + return; + + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, + key->keyidx); + if (ret) + ath10k_warn("failed to set vdev %i group key as default key: %d\n", + arvif->vdev_id, ret); +} + static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key) @@ -2413,7 +3291,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (!peer) { if (cmd == SET_KEY) { - ath10k_warn("cannot install key for non-existent peer %pM\n", + ath10k_warn("failed to install key for non-existent peer %pM\n", peer_addr); ret = -EOPNOTSUPP; goto exit; @@ -2436,10 +3314,13 @@ 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("failed to install key for vdev %i peer %pM: %d\n", + arvif->vdev_id, peer_addr, ret); goto exit; } + ath10k_set_key_h_def_keyidx(ar, arvif, cmd, key); + spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find(ar, arvif->vdev_id, peer_addr); if (peer && cmd == SET_KEY) @@ -2448,7 +3329,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, peer->keys[key->keyidx] = NULL; else if (peer == NULL) /* impossible unless FW goes crazy */ - ath10k_warn("peer %pM disappeared!\n", peer_addr); + ath10k_warn("Peer %pM disappeared!\n", peer_addr); spin_unlock_bh(&ar->data_lock); exit: @@ -2456,6 +3337,79 @@ exit: return ret; } +static void ath10k_sta_rc_update_wk(struct work_struct *wk) +{ + struct ath10k *ar; + struct ath10k_vif *arvif; + struct ath10k_sta *arsta; + struct ieee80211_sta *sta; + u32 changed, bw, nss, smps; + int err; + + arsta = container_of(wk, struct ath10k_sta, update_wk); + sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); + arvif = arsta->arvif; + ar = arvif->ar; + + spin_lock_bh(&ar->data_lock); + + changed = arsta->changed; + arsta->changed = 0; + + bw = arsta->bw; + nss = arsta->nss; + smps = arsta->smps; + + spin_unlock_bh(&ar->data_lock); + + mutex_lock(&ar->conf_mutex); + + if (changed & IEEE80211_RC_BW_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM peer bw %d\n", + sta->addr, bw); + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_CHAN_WIDTH, bw); + if (err) + ath10k_warn("failed to update STA %pM peer bw %d: %d\n", + sta->addr, bw, err); + } + + if (changed & IEEE80211_RC_NSS_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM nss %d\n", + sta->addr, nss); + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_NSS, nss); + if (err) + ath10k_warn("failed to update STA %pM nss %d: %d\n", + sta->addr, nss, err); + } + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM smps %d\n", + sta->addr, smps); + + err = ath10k_wmi_peer_set_param(ar, arvif->vdev_id, sta->addr, + WMI_PEER_SMPS_STATE, smps); + if (err) + ath10k_warn("failed to update STA %pM smps %d: %d\n", + sta->addr, smps, err); + } + + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { + ath10k_dbg(ATH10K_DBG_MAC, "mac update sta %pM supp rates\n", + sta->addr); + + err = ath10k_station_assoc(ar, arvif, sta, true); + if (err) + ath10k_warn("failed to reassociate station: %pM\n", + sta->addr); + } + + mutex_unlock(&ar->conf_mutex); +} + static int ath10k_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -2464,8 +3418,22 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + 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)) + cancel_work_sync(&arsta->update_wk); + mutex_lock(&ar->conf_mutex); if (old_state == IEEE80211_STA_NOTEXIST && @@ -2474,27 +3442,38 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New station addition. */ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1; + else + max_num_peers = TARGET_NUM_PEERS; + + if (ar->num_peers >= max_num_peers) { + ath10k_warn("number of peers exceeded: peers number %d (max peers %d)\n", + ar->num_peers, max_num_peers); + ret = -ENOBUFS; + goto exit; + } + + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d peer create %pM (new sta) num_peers %d\n", + arvif->vdev_id, sta->addr, ar->num_peers); + ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); if (ret) - ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Added peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); + ath10k_warn("failed to add peer %pM for vdev %d when adding a new sta: %i\n", + sta->addr, arvif->vdev_id, ret); } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { /* * Existing station deletion. */ + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d peer delete %pM (sta gone)\n", + arvif->vdev_id, sta->addr); ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr); if (ret) - ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Removed 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); @@ -2505,14 +3484,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New association. */ - ret = ath10k_station_assoc(ar, arvif, sta); + ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n", + sta->addr); + + ret = ath10k_station_assoc(ar, arvif, sta, false); if (ret) - ath10k_warn("Failed to associate station: %pM\n", - sta->addr); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Station %pM moved to assoc state\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 || @@ -2520,16 +3498,15 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * Disassociation. */ + ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n", + sta->addr); + ret = ath10k_station_disassoc(ar, arvif, sta); if (ret) - ath10k_warn("Failed to disassociate station: %pM\n", - sta->addr); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Station %pM moved to disassociated state\n", - sta->addr); + ath10k_warn("failed to disassociate station: %pM vdev %i: %i\n", + sta->addr, arvif->vdev_id, ret); } - +exit: mutex_unlock(&ar->conf_mutex); return ret; } @@ -2574,7 +3551,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, WMI_STA_PS_PARAM_UAPSD, arvif->u.sta.uapsd); if (ret) { - ath10k_warn("could not set uapsd params %d\n", ret); + ath10k_warn("failed to set uapsd params: %d\n", ret); goto exit; } @@ -2587,7 +3564,7 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, WMI_STA_PS_PARAM_RX_WAKE_POLICY, value); if (ret) - ath10k_warn("could not set rx wake param %d\n", ret); + ath10k_warn("failed to set rx wake param: %d\n", ret); exit: return ret; @@ -2637,13 +3614,13 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw, /* FIXME: FW accepts wmm params per hw, not per vif */ ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params); if (ret) { - ath10k_warn("could not set wmm params %d\n", ret); + ath10k_warn("failed to set wmm params: %d\n", ret); goto exit; } ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd); if (ret) - ath10k_warn("could not set sta uapsd %d\n", ret); + ath10k_warn("failed to set sta uapsd: %d\n", ret); exit: mutex_unlock(&ar->conf_mutex); @@ -2672,9 +3649,9 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, goto exit; } - INIT_COMPLETION(ar->scan.started); - INIT_COMPLETION(ar->scan.completed); - INIT_COMPLETION(ar->scan.on_channel); + reinit_completion(&ar->scan.started); + reinit_completion(&ar->scan.completed); + reinit_completion(&ar->scan.on_channel); ar->scan.in_progress = true; ar->scan.aborting = false; ar->scan.is_roc = true; @@ -2696,7 +3673,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, ret = ath10k_start_scan(ar, &arg); if (ret) { - ath10k_warn("could not start roc scan (%d)\n", ret); + ath10k_warn("failed to start roc scan: %d\n", ret); spin_lock_bh(&ar->data_lock); ar->scan.in_progress = false; spin_unlock_bh(&ar->data_lock); @@ -2705,7 +3682,7 @@ static int ath10k_remain_on_channel(struct ieee80211_hw *hw, ret = wait_for_completion_timeout(&ar->scan.on_channel, 3*HZ); if (ret == 0) { - ath10k_warn("could not switch to channel for roc scan\n"); + ath10k_warn("failed to switch to channel for roc scan\n"); ath10k_abort_scan(ar); ret = -ETIMEDOUT; goto exit; @@ -2732,91 +3709,55 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) * Both RTS and Fragmentation threshold are interface-specific * in ath10k, but device-specific in mac80211. */ -static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath10k_generic_iter *ar_iter = data; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; - - lockdep_assert_held(&arvif->ar->conf_mutex); - - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) - return; - - ar_iter->ret = ath10k_mac_set_rts(arvif, rts); - if (ar_iter->ret) - ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set RTS threshold: %d for VDEV: %d\n", - rts, arvif->vdev_id); -} static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - struct ath10k_generic_iter ar_iter; struct ath10k *ar = hw->priv; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + struct ath10k_vif *arvif; + int ret = 0; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_set_rts_iter, &ar_iter); - mutex_unlock(&ar->conf_mutex); - - return ar_iter.ret; -} + list_for_each_entry(arvif, &ar->arvifs, list) { + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n", + arvif->vdev_id, value); -static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath10k_generic_iter *ar_iter = data; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; - - lockdep_assert_held(&arvif->ar->conf_mutex); - - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) - return; + ret = ath10k_mac_set_rts(arvif, value); + if (ret) { + ath10k_warn("failed to set rts threshold for vdev %d: %d\n", + arvif->vdev_id, ret); + break; + } + } + mutex_unlock(&ar->conf_mutex); - ar_iter->ret = ath10k_mac_set_frag(arvif, frag); - if (ar_iter->ret) - ath10k_warn("Failed to set frag threshold for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set frag threshold: %d for VDEV: %d\n", - frag, arvif->vdev_id); + return ret; } static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) { - struct ath10k_generic_iter ar_iter; struct ath10k *ar = hw->priv; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + struct ath10k_vif *arvif; + int ret = 0; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_set_frag_iter, &ar_iter); + list_for_each_entry(arvif, &ar->arvifs, list) { + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", + arvif->vdev_id, value); + + ret = ath10k_mac_set_rts(arvif, value); + if (ret) { + ath10k_warn("failed to set fragmentation threshold for vdev %d: %d\n", + arvif->vdev_id, ret); + break; + } + } mutex_unlock(&ar->conf_mutex); - return ar_iter.ret; + return ret; } -static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ath10k *ar = hw->priv; bool skip; @@ -2836,8 +3777,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) bool empty; spin_lock_bh(&ar->htt.tx_lock); - empty = bitmap_empty(ar->htt.used_msdu_ids, - ar->htt.max_num_pending_tx); + empty = (ar->htt.num_pending_tx == 0); spin_unlock_bh(&ar->htt.tx_lock); skip = (ar->state == ATH10K_STATE_WEDGED); @@ -2846,7 +3786,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("failed to flush transmit queue (skip %i ar-state %i): %i\n", + skip, ar->state, ret); skip: mutex_unlock(&ar->conf_mutex); @@ -2868,37 +3809,33 @@ static int ath10k_suspend(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; int ret; - ar->is_target_paused = false; + mutex_lock(&ar->conf_mutex); - ret = ath10k_wmi_pdev_suspend_target(ar); + ret = ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND); if (ret) { - ath10k_warn("could not suspend target (%d)\n", ret); - return 1; - } - - ret = wait_event_interruptible_timeout(ar->event_queue, - ar->is_target_paused == true, - 1 * HZ); - if (ret < 0) { - ath10k_warn("suspend interrupted (%d)\n", ret); - goto resume; - } else if (ret == 0) { - ath10k_warn("suspend timed out - target pause event never came\n"); - goto resume; + if (ret == -ETIMEDOUT) + goto resume; + ret = 1; + goto exit; } ret = ath10k_hif_suspend(ar); if (ret) { - ath10k_warn("could not suspend hif (%d)\n", ret); + ath10k_warn("failed to suspend hif: %d\n", ret); goto resume; } - return 0; + ret = 0; + goto exit; resume: ret = ath10k_wmi_pdev_resume_target(ar); if (ret) - ath10k_warn("could not resume target (%d)\n", ret); - return 1; + ath10k_warn("failed to resume target: %d\n", ret); + + ret = 1; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; } static int ath10k_resume(struct ieee80211_hw *hw) @@ -2906,19 +3843,26 @@ static int ath10k_resume(struct ieee80211_hw *hw) struct ath10k *ar = hw->priv; int ret; + mutex_lock(&ar->conf_mutex); + ret = ath10k_hif_resume(ar); if (ret) { - ath10k_warn("could not resume hif (%d)\n", ret); - return 1; + ath10k_warn("failed to resume hif: %d\n", ret); + ret = 1; + goto exit; } ret = ath10k_wmi_pdev_resume_target(ar); if (ret) { - ath10k_warn("could not resume target (%d)\n", ret); - return 1; + ath10k_warn("failed to resume target: %d\n", ret); + ret = 1; + goto exit; } - return 0; + ret = 0; +exit: + mutex_unlock(&ar->conf_mutex); + return ret; } #endif @@ -2973,6 +3917,419 @@ exit: return ret; } +/* Helper table for legacy fixed_rate/bitrate_mask */ +static const u8 cck_ofdm_rate[] = { + /* CCK */ + 3, /* 1Mbps */ + 2, /* 2Mbps */ + 1, /* 5.5Mbps */ + 0, /* 11Mbps */ + /* OFDM */ + 3, /* 6Mbps */ + 7, /* 9Mbps */ + 2, /* 12Mbps */ + 6, /* 18Mbps */ + 1, /* 24Mbps */ + 5, /* 36Mbps */ + 0, /* 48Mbps */ + 4, /* 54Mbps */ +}; + +/* Check if only one bit set */ +static int ath10k_check_single_mask(u32 mask) +{ + int bit; + + bit = ffs(mask); + if (!bit) + return 0; + + mask &= ~BIT(bit - 1); + if (mask) + return 2; + + return 1; +} + +static bool +ath10k_default_bitrate_mask(struct ath10k *ar, + enum ieee80211_band band, + const struct cfg80211_bitrate_mask *mask) +{ + u32 legacy = 0x00ff; + u8 ht = 0xff, i; + u16 vht = 0x3ff; + + switch (band) { + case IEEE80211_BAND_2GHZ: + legacy = 0x00fff; + vht = 0; + break; + case IEEE80211_BAND_5GHZ: + break; + default: + return false; + } + + if (mask->control[band].legacy != legacy) + return false; + + for (i = 0; i < ar->num_rf_chains; i++) + if (mask->control[band].ht_mcs[i] != ht) + return false; + + for (i = 0; i < ar->num_rf_chains; i++) + if (mask->control[band].vht_mcs[i] != vht) + return false; + + return true; +} + +static bool +ath10k_bitrate_mask_nss(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_nss) +{ + int ht_nss = 0, vht_nss = 0, i; + + /* check legacy */ + if (ath10k_check_single_mask(mask->control[band].legacy)) + return false; + + /* check HT */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + if (mask->control[band].ht_mcs[i] == 0xff) + continue; + else if (mask->control[band].ht_mcs[i] == 0x00) + break; + else + return false; + } + + ht_nss = i; + + /* check VHT */ + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if (mask->control[band].vht_mcs[i] == 0x03ff) + continue; + else if (mask->control[band].vht_mcs[i] == 0x0000) + break; + else + return false; + } + + vht_nss = i; + + if (ht_nss > 0 && vht_nss > 0) + return false; + + if (ht_nss) + *fixed_nss = ht_nss; + else if (vht_nss) + *fixed_nss = vht_nss; + else + return false; + + return true; +} + +static bool +ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + enum wmi_rate_preamble *preamble) +{ + int legacy = 0, ht = 0, vht = 0, i; + + *preamble = WMI_RATE_PREAMBLE_OFDM; + + /* check legacy */ + legacy = ath10k_check_single_mask(mask->control[band].legacy); + if (legacy > 1) + return false; + + /* check HT */ + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + ht += ath10k_check_single_mask(mask->control[band].ht_mcs[i]); + if (ht > 1) + return false; + + /* check VHT */ + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + vht += ath10k_check_single_mask(mask->control[band].vht_mcs[i]); + if (vht > 1) + return false; + + /* Currently we support only one fixed_rate */ + if ((legacy + ht + vht) != 1) + return false; + + if (ht) + *preamble = WMI_RATE_PREAMBLE_HT; + else if (vht) + *preamble = WMI_RATE_PREAMBLE_VHT; + + return true; +} + +static bool +ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_rate, + u8 *fixed_nss) +{ + u8 rate = 0, pream = 0, nss = 0, i; + enum wmi_rate_preamble preamble; + + /* Check if single rate correct */ + if (!ath10k_bitrate_mask_correct(mask, band, &preamble)) + return false; + + pream = preamble; + + switch (preamble) { + case WMI_RATE_PREAMBLE_CCK: + case WMI_RATE_PREAMBLE_OFDM: + i = ffs(mask->control[band].legacy) - 1; + + if (band == IEEE80211_BAND_2GHZ && i < 4) + pream = WMI_RATE_PREAMBLE_CCK; + + if (band == IEEE80211_BAND_5GHZ) + i += 4; + + if (i >= ARRAY_SIZE(cck_ofdm_rate)) + return false; + + rate = cck_ofdm_rate[i]; + break; + case WMI_RATE_PREAMBLE_HT: + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + if (mask->control[band].ht_mcs[i]) + break; + + if (i == IEEE80211_HT_MCS_MASK_LEN) + return false; + + rate = ffs(mask->control[band].ht_mcs[i]) - 1; + nss = i; + break; + case WMI_RATE_PREAMBLE_VHT: + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + if (mask->control[band].vht_mcs[i]) + break; + + if (i == NL80211_VHT_NSS_MAX) + return false; + + rate = ffs(mask->control[band].vht_mcs[i]) - 1; + nss = i; + break; + } + + *fixed_nss = nss + 1; + nss <<= 4; + pream <<= 6; + + ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n", + pream, nss, rate); + + *fixed_rate = pream | nss | rate; + + return true; +} + +static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask, + enum ieee80211_band band, + u8 *fixed_rate, + u8 *fixed_nss) +{ + /* First check full NSS mask, if we can simply limit NSS */ + if (ath10k_bitrate_mask_nss(mask, band, fixed_nss)) + return true; + + /* Next Check single rate is set */ + return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss); +} + +static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif, + u8 fixed_rate, + u8 fixed_nss, + u8 force_sgi) +{ + struct ath10k *ar = arvif->ar; + u32 vdev_param; + int ret = 0; + + mutex_lock(&ar->conf_mutex); + + if (arvif->fixed_rate == fixed_rate && + 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); + if (ret) { + ath10k_warn("failed to set fixed rate param 0x%02x: %d\n", + fixed_rate, ret); + ret = -EINVAL; + goto exit; + } + + arvif->fixed_rate = fixed_rate; + + vdev_param = ar->wmi.vdev_param->nss; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, + vdev_param, fixed_nss); + + if (ret) { + ath10k_warn("failed to set fixed nss param %d: %d\n", + fixed_nss, ret); + ret = -EINVAL; + goto exit; + } + + 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("failed to 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; +} + +static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + 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, + &fixed_rate, + &fixed_nss)) + return -EINVAL; + } + + if (fixed_rate == WMI_FIXED_RATE_NONE && force_sgi) { + ath10k_warn("failed to force SGI usage for default rate settings\n"); + return -EINVAL; + } + + return ath10k_set_fixed_rate_param(arvif, fixed_rate, + fixed_nss, force_sgi); +} + +static void ath10k_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 changed) +{ + struct ath10k *ar = hw->priv; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + u32 bw, smps; + + spin_lock_bh(&ar->data_lock); + + ath10k_dbg(ATH10K_DBG_MAC, + "mac sta rc update for %pM changed %08x bw %d nss %d smps %d\n", + sta->addr, changed, sta->bandwidth, sta->rx_nss, + sta->smps_mode); + + if (changed & IEEE80211_RC_BW_CHANGED) { + bw = WMI_PEER_CHWIDTH_20MHZ; + + switch (sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + ath10k_warn("Invalid bandwith %d in rc update for %pM\n", + sta->bandwidth, sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + arsta->bw = bw; + } + + if (changed & IEEE80211_RC_NSS_CHANGED) + arsta->nss = sta->rx_nss; + + if (changed & IEEE80211_RC_SMPS_CHANGED) { + smps = WMI_PEER_SMPS_PS_NONE; + + switch (sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_OFF: + smps = WMI_PEER_SMPS_PS_NONE; + break; + case IEEE80211_SMPS_STATIC: + smps = WMI_PEER_SMPS_STATIC; + break; + case IEEE80211_SMPS_DYNAMIC: + smps = WMI_PEER_SMPS_DYNAMIC; + break; + case IEEE80211_SMPS_NUM_MODES: + ath10k_warn("Invalid smps %d in sta rc update for %pM\n", + sta->smps_mode, sta->addr); + smps = WMI_PEER_SMPS_PS_NONE; + break; + } + + arsta->smps = smps; + } + + arsta->changed |= changed; + + spin_unlock_bh(&ar->data_lock); + + 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, @@ -2993,8 +4350,13 @@ static const struct ieee80211_ops ath10k_ops = { .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, + .set_antenna = ath10k_set_antenna, + .get_antenna = ath10k_get_antenna, .restart_complete = ath10k_restart_complete, .get_survey = ath10k_get_survey, + .set_bitrate_mask = ath10k_set_bitrate_mask, + .sta_rc_update = ath10k_sta_rc_update, + .get_tsf = ath10k_get_tsf, #ifdef CONFIG_PM .suspend = ath10k_suspend, .resume = ath10k_resume, @@ -3127,12 +4489,37 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { }, }; -static const struct ieee80211_iface_combination ath10k_if_comb = { - .limits = ath10k_if_limits, - .n_limits = ARRAY_SIZE(ath10k_if_limits), - .max_interfaces = 8, - .num_different_channels = 1, - .beacon_int_infra_match = true, +static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = { + { + .max = 8, + .types = BIT(NL80211_IFTYPE_AP) + }, +}; + +static const struct ieee80211_iface_combination ath10k_if_comb[] = { + { + .limits = ath10k_if_limits, + .n_limits = ARRAY_SIZE(ath10k_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .beacon_int_infra_match = true, + }, +}; + +static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = { + { + .limits = ath10k_10x_if_limits, + .n_limits = ARRAY_SIZE(ath10k_10x_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .beacon_int_infra_match = true, +#ifdef CONFIG_ATH10K_DFS_CERTIFIED + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), +#endif + }, }; static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar) @@ -3246,7 +4633,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; } @@ -3311,9 +4698,24 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO); + BIT(NL80211_IFTYPE_AP); + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + /* TODO: Have to deal with 2x2 chips if/when the come out. */ + ar->supp_tx_chainmask = TARGET_10X_TX_CHAIN_MASK; + ar->supp_rx_chainmask = TARGET_10X_RX_CHAIN_MASK; + } else { + ar->supp_tx_chainmask = TARGET_TX_CHAIN_MASK; + ar->supp_rx_chainmask = TARGET_RX_CHAIN_MASK; + } + + ar->hw->wiphy->available_antennas_rx = ar->supp_rx_chainmask; + ar->hw->wiphy->available_antennas_tx = ar->supp_tx_chainmask; + + if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) + ar->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | @@ -3323,8 +4725,12 @@ int ath10k_mac_register(struct ath10k *ar) IEEE80211_HW_REPORTS_TX_ACK_STATUS | 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. */ + ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4; if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS; @@ -3338,11 +4744,12 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->wiphy->max_scan_ie_len = WLAN_SCAN_PARAMS_MAX_IE_LEN; ar->hw->vif_data_size = sizeof(struct ath10k_vif); + ar->hw->sta_data_size = sizeof(struct ath10k_sta); - ar->hw->channel_change_time = 5000; ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; ar->hw->wiphy->max_remain_on_channel_duration = 5000; ar->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; @@ -3352,21 +4759,38 @@ int ath10k_mac_register(struct ath10k *ar) */ ar->hw->queues = 4; - ar->hw->wiphy->iface_combinations = &ath10k_if_comb; - ar->hw->wiphy->n_iface_combinations = 1; + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_10x_if_comb); + } else { + ar->hw->wiphy->iface_combinations = ath10k_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_if_comb); + } ar->hw->netdev_features = NETIF_F_HW_CSUM; + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) { + /* Init ath dfs pattern detector */ + ar->ath_common.debug_mask = ATH_DBG_DFS; + ar->dfs_detector = dfs_pattern_detector_init(&ar->ath_common, + NL80211_DFS_UNSET); + + if (!ar->dfs_detector) + ath10k_warn("failed to initialise DFS pattern detector\n"); + } + ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy, ath10k_reg_notifier); if (ret) { - ath10k_err("Regulatory initialization failed\n"); + ath10k_err("failed to initialise regulatory: %i\n", ret); goto err_free; } ret = ieee80211_register_hw(ar->hw); if (ret) { - ath10k_err("ieee80211 registration failed: %d\n", ret); + ath10k_err("failed to register ieee80211: %d\n", ret); goto err_free; } @@ -3392,6 +4816,9 @@ void ath10k_mac_unregister(struct ath10k *ar) { ieee80211_unregister_hw(ar->hw); + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED) && ar->dfs_detector) + ar->dfs_detector->exit(ar->dfs_detector); + kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels); kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels); diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 6fce9bfb19a..ba1021997b8 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); void ath10k_reset_scan(unsigned long ptr); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); +void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar); +void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work); void ath10k_halt(struct ath10k *ar); static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index e2f9ef50b1b..d0004d59c97 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -19,6 +19,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/bitops.h> #include "core.h" #include "debug.h" @@ -32,15 +33,37 @@ #include "ce.h" #include "pci.h" -static unsigned int ath10k_target_ps; -module_param(ath10k_target_ps, uint, 0644); -MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); +enum ath10k_pci_irq_mode { + ATH10K_PCI_IRQ_AUTO = 0, + ATH10K_PCI_IRQ_LEGACY = 1, + ATH10K_PCI_IRQ_MSI = 2, +}; + +enum ath10k_pci_reset_mode { + ATH10K_PCI_RESET_AUTO = 0, + ATH10K_PCI_RESET_WARM_ONLY = 1, +}; + +static unsigned int ath10k_pci_target_ps; +static unsigned int ath10k_pci_irq_mode = ATH10K_PCI_IRQ_AUTO; +static unsigned int ath10k_pci_reset_mode = ATH10K_PCI_RESET_AUTO; + +module_param_named(target_ps, ath10k_pci_target_ps, uint, 0644); +MODULE_PARM_DESC(target_ps, "Enable ath10k Target (SoC) PS option"); + +module_param_named(irq_mode, ath10k_pci_irq_mode, uint, 0644); +MODULE_PARM_DESC(irq_mode, "0: auto, 1: legacy, 2: msi (default: 0)"); + +module_param_named(reset_mode, ath10k_pci_reset_mode, uint, 0644); +MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)"); + +/* how long wait to wait for target to initialise, in ms */ +#define ATH10K_PCI_TARGET_WAIT 3000 +#define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3 -#define QCA988X_1_0_DEVICE_ID (0xabcd) #define QCA988X_2_0_DEVICE_ID (0x003c) static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { - { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */ { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */ {0} }; @@ -48,58 +71,245 @@ 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 hif_ce_pipe_info *pipe_info, +static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, int num); -static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); -static void ath10k_pci_stop_ce(struct ath10k *ar); -static void ath10k_pci_device_reset(struct ath10k *ar); -static int ath10k_pci_reset_target(struct ath10k *ar); -static int ath10k_pci_start_intr(struct ath10k *ar); -static void ath10k_pci_stop_intr(struct ath10k *ar); +static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info); +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); +static int ath10k_pci_init_irq(struct ath10k *ar); +static int ath10k_pci_deinit_irq(struct ath10k *ar); +static int ath10k_pci_request_irq(struct ath10k *ar); +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 const struct ce_attr host_ce_config_wlan[] = { - /* host->target HTC control and raw streams */ - { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,}, - /* could be moved to share CE3 */ - /* target->host HTT + HTC control */ - { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,}, - /* target->host WMI */ - { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,}, - /* host->target WMI */ - { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,}, - /* host->target HTT */ - { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0, - CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,}, - /* unused */ - { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, - /* Target autonomous hif_memcpy */ - { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, - /* ce_diag, the Diagnostic Window */ - { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,}, + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 512, + .dest_nentries = 512, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 32, + }, + + /* CE3: host->target WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = CE_HTT_H2T_MSG_SRC_NENTRIES, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: unused */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: ce_diag, the Diagnostic Window */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 2, + .src_sz_max = DIAG_TRANSFER_LIMIT, + .dest_nentries = 2, + }, }; /* Target firmware's Copy Engine configuration. */ static const struct ce_pipe_config target_ce_config_wlan[] = { - /* host->target HTC control and raw streams */ - { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,}, - /* target->host HTT + HTC control */ - { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,}, - /* target->host WMI */ - { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, - /* host->target WMI */ - { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, - /* host->target HTT */ - { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,}, + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = 0, + .pipedir = PIPEDIR_OUT, + .nentries = 32, + .nbytes_max = 256, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = 1, + .pipedir = PIPEDIR_IN, + .nentries = 32, + .nbytes_max = 512, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE2: target->host WMI */ + { + .pipenum = 2, + .pipedir = PIPEDIR_IN, + .nentries = 32, + .nbytes_max = 2048, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE3: host->target WMI */ + { + .pipenum = 3, + .pipedir = PIPEDIR_OUT, + .nentries = 32, + .nbytes_max = 2048, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE4: host->target HTT */ + { + .pipenum = 4, + .pipedir = PIPEDIR_OUT, + .nentries = 256, + .nbytes_max = 256, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + /* NB: 50% of src nentries, since tx has 2 frags */ - /* unused */ - { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, - /* Reserved for target autonomous hif_memcpy */ - { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,}, + + /* CE5: unused */ + { + .pipenum = 5, + .pipedir = PIPEDIR_OUT, + .nentries = 32, + .nbytes_max = 2048, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = 6, + .pipedir = PIPEDIR_INOUT, + .nentries = 32, + .nbytes_max = 4096, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + /* CE7 used only by Host */ }; +static bool ath10k_pci_irq_pending(struct ath10k *ar) +{ + u32 cause; + + /* Check if the shared legacy irq is for us */ + cause = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + if (cause & (PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL)) + return true; + + return false; +} + +static void ath10k_pci_disable_and_clear_legacy_irq(struct ath10k *ar) +{ + /* IMPORTANT: INTR_CLR register has to be set after + * INTR_ENABLE is set to 0, otherwise interrupt can not be + * really cleared. */ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + 0); + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_CLR_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + + /* IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. */ + (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS); +} + +static void ath10k_pci_enable_legacy_irq(struct ath10k *ar) +{ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + + /* IMPORTANT: this extra read transaction is required to + * flush the posted write buffer. */ + (void) ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS); +} + +static irqreturn_t ath10k_pci_early_irq_handler(int irq, void *arg) +{ + struct ath10k *ar = arg; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + if (ar_pci->num_msi_intrs == 0) { + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + + ath10k_pci_disable_and_clear_legacy_irq(ar); + } + + tasklet_schedule(&ar_pci->early_irq_tasklet); + + return IRQ_HANDLED; +} + +static int ath10k_pci_request_early_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; + + /* Regardless whether MSI-X/MSI/legacy irqs have been set up the first + * interrupt from irq vector is triggered in all cases for FW + * indication/errors */ + ret = request_irq(ar_pci->pdev->irq, ath10k_pci_early_irq_handler, + IRQF_SHARED, "ath10k_pci (early)", ar); + if (ret) { + ath10k_warn("failed to request early irq: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath10k_pci_free_early_irq(struct ath10k *ar) +{ + free_irq(ath10k_pci_priv(ar)->pdev->irq, ar); +} + /* * Diagnostic read/write access is provided for startup/config/debug usage. * Caller must guarantee proper alignment, when applicable, and single user @@ -114,7 +324,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, unsigned int completed_nbytes, orig_nbytes, remaining_bytes; unsigned int id; unsigned int flags; - struct ce_state *ce_diag; + struct ath10k_ce_pipe *ce_diag; /* Host buffer address in CE space */ u32 ce_data; dma_addr_t ce_data_base = 0; @@ -149,9 +359,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, * 2) Buffer in DMA-able space */ orig_nbytes = nbytes; - data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev, - orig_nbytes, - &ce_data_base); + data_buf = (unsigned char *)dma_alloc_coherent(ar->dev, + orig_nbytes, + &ce_data_base, + GFP_ATOMIC); if (!data_buf) { ret = -ENOMEM; @@ -245,12 +456,12 @@ done: __le32_to_cpu(((__le32 *)data_buf)[i]); } } else - ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", - __func__, address); + ath10k_warn("failed to read diag value at 0x%x: %d\n", + address, ret); if (data_buf) - pci_free_consistent(ar_pci->pdev, orig_nbytes, - data_buf, ce_data_base); + dma_free_coherent(ar->dev, orig_nbytes, data_buf, + ce_data_base); return ret; } @@ -278,7 +489,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, unsigned int completed_nbytes, orig_nbytes, remaining_bytes; unsigned int id; unsigned int flags; - struct ce_state *ce_diag; + struct ath10k_ce_pipe *ce_diag; void *data_buf = NULL; u32 ce_data; /* Host buffer address in CE space */ dma_addr_t ce_data_base = 0; @@ -293,9 +504,10 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, * 2) Buffer in DMA-able space */ orig_nbytes = nbytes; - data_buf = (unsigned char *)pci_alloc_consistent(ar_pci->pdev, - orig_nbytes, - &ce_data_base); + data_buf = (unsigned char *)dma_alloc_coherent(ar->dev, + orig_nbytes, + &ce_data_base, + GFP_ATOMIC); if (!data_buf) { ret = -ENOMEM; goto done; @@ -391,13 +603,13 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, done: if (data_buf) { - pci_free_consistent(ar_pci->pdev, orig_nbytes, data_buf, - ce_data_base); + dma_free_coherent(ar->dev, orig_nbytes, data_buf, + ce_data_base); } if (ret != 0) - ath10k_dbg(ATH10K_DBG_PCI, "%s failure (0x%x)\n", __func__, - address); + ath10k_warn("failed to write diag value at 0x%x: %d\n", + address, ret); return ret; } @@ -426,18 +638,7 @@ static bool ath10k_pci_target_is_awake(struct ath10k *ar) return (RTC_STATE_V_GET(val) == RTC_STATE_V_ON); } -static void ath10k_pci_wait(struct ath10k *ar) -{ - int n = 100; - - while (n-- && !ath10k_pci_target_is_awake(ar)) - msleep(10); - - if (n < 0) - ath10k_warn("Unable to wakeup target\n"); -} - -void ath10k_do_pci_wake(struct ath10k *ar) +int ath10k_do_pci_wake(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *pci_addr = ar_pci->mem; @@ -453,18 +654,19 @@ void ath10k_do_pci_wake(struct ath10k *ar) atomic_inc(&ar_pci->keep_awake_count); if (ar_pci->verified_awake) - return; + return 0; for (;;) { if (ath10k_pci_target_is_awake(ar)) { ar_pci->verified_awake = true; - break; + return 0; } if (tot_delay > PCIE_WAKE_TIMEOUT) { - ath10k_warn("target takes too long to wake up (awake count %d)\n", + ath10k_warn("target took longer %d us to wake up (awake count %d)\n", + PCIE_WAKE_TIMEOUT, atomic_read(&ar_pci->keep_awake_count)); - break; + return -ETIMEDOUT; } udelay(curr_delay); @@ -489,195 +691,146 @@ void ath10k_do_pci_sleep(struct ath10k *ar) } } -/* - * FIXME: Handle OOM properly. - */ -static inline -struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *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 ce_state *ce_state, - void *transfer_context, - u32 ce_data, - unsigned int nbytes, - unsigned int transfer_id) +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 hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; - struct ath10k_pci_compl *compl; - bool process = false; + struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; + void *transfer_context; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; - do { - /* - * For the send completion of an item in sendlist, just - * increment num_sends_allowed. The upper layer callback will - * be triggered when last fragment is done with send. - */ - if (transfer_context == CE_SENDLIST_ITEM_CTXT) { - spin_lock_bh(&pipe_info->pipe_lock); - pipe_info->num_sends_allowed++; - spin_unlock_bh(&pipe_info->pipe_lock); + while (ath10k_ce_completed_send_next(ce_state, &transfer_context, + &ce_data, &nbytes, + &transfer_id) == 0) { + /* no need to call tx completion for NULL pointers */ + if (transfer_context == NULL) continue; - } - - compl = get_free_compl(pipe_info); - if (!compl) - break; - - compl->send_or_recv = HIF_CE_COMPLETE_SEND; - compl->ce_state = ce_state; - compl->pipe_info = pipe_info; - compl->transfer_context = transfer_context; - compl->nbytes = nbytes; - compl->transfer_id = transfer_id; - compl->flags = 0; - - /* - * 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); - - process = true; - } while (ath10k_ce_completed_send_next(ce_state, - &transfer_context, - &ce_data, &nbytes, - &transfer_id) == 0); - /* - * If only some of the items within a sendlist have completed, - * don't invoke completion processing until the entire sendlist - * has been sent. - */ - if (!process) - return; - - ath10k_pci_process_ce(ar); + cb->tx_completion(ar, transfer_context, transfer_id); + } } /* Called by lower (CE) layer when data is received from the Target. */ -static void ath10k_pci_ce_recv_data(struct ce_state *ce_state, - void *transfer_context, u32 ce_data, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags) +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 hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; - struct ath10k_pci_compl *compl; + struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_hif_cb *cb = &ar_pci->msg_callbacks_current; struct sk_buff *skb; - - do { - compl = get_free_compl(pipe_info); - if (!compl) - break; - - compl->send_or_recv = HIF_CE_COMPLETE_RECV; - compl->ce_state = ce_state; - compl->pipe_info = pipe_info; - compl->transfer_context = transfer_context; - compl->nbytes = nbytes; - compl->transfer_id = transfer_id; - compl->flags = flags; + void *transfer_context; + u32 ce_data; + 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) { + 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); - } while (ath10k_ce_completed_recv_next(ce_state, - &transfer_context, - &ce_data, &nbytes, - &transfer_id, - &flags) == 0); + if (unlikely(max_nbytes < nbytes)) { + ath10k_warn("rxed more than expected (nbytes %d, max %d)", + nbytes, max_nbytes); + dev_kfree_skb_any(skb); + continue; + } - ath10k_pci_process_ce(ar); + 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 hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]); - struct ce_state *ce_hdl = pipe_info->ce_hdl; - struct ce_sendlist sendlist; - unsigned int len; - u32 flags = 0; - int ret; - - memset(&sendlist, 0, sizeof(struct ce_sendlist)); + 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; + unsigned int sw_index; + unsigned int write_index; + int err, i = 0; + + spin_lock_bh(&ar_pci->ce_lock); + + nentries_mask = src_ring->nentries_mask; + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + + if (unlikely(CE_RING_DELTA(nentries_mask, + write_index, sw_index - 1) < n_items)) { + err = -ENOBUFS; + goto err; + } - len = min(bytes, nbuf->len); - bytes -= len; + 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); + + 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 err; + } - if (len & 3) - ath10k_warn("skb not aligned to 4-byte boundary (%d)\n", len); + /* `i` is equal to `n_items -1` after for() */ 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); - - ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags); - - /* Make sure we have resources to handle this request */ - spin_lock_bh(&pipe_info->pipe_lock); - if (!pipe_info->num_sends_allowed) { - ath10k_warn("Pipe: %d is full\n", pipe_id); - spin_unlock_bh(&pipe_info->pipe_lock); - return -ENOSR; - } - pipe_info->num_sends_allowed--; - spin_unlock_bh(&pipe_info->pipe_lock); - - ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id); - if (ret) - ath10k_warn("CE send failed: %p\n", nbuf); + "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 err; - return ret; + spin_unlock_bh(&ar_pci->ce_lock); + return 0; + +err: + for (; i > 0; i--) + __ath10k_ce_send_revert(ce_pipe); + + spin_unlock_bh(&ar_pci->ce_lock); + return err; } static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]); - int ret; - spin_lock_bh(&pipe_info->pipe_lock); - ret = pipe_info->num_sends_allowed; - spin_unlock_bh(&pipe_info->pipe_lock); + ath10k_dbg(ATH10K_DBG_PCI, "pci hif get free queue number\n"); - return ret; + return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl); } static void ath10k_pci_hif_dump_area(struct ath10k *ar) @@ -691,14 +844,13 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) ath10k_err("firmware crashed!\n"); ath10k_err("hardware name %s version 0x%x\n", ar->hw_params.name, ar->target_version); - ath10k_err("firmware version: %u.%u.%u.%u\n", ar->fw_version_major, - ar->fw_version_minor, ar->fw_version_release, - ar->fw_version_build); + ath10k_err("firmware version: %s\n", ar->hw->wiphy->fw_version); host_addr = host_interest_item_address(HI_ITEM(hi_failure_state)); - if (ath10k_pci_diag_read_mem(ar, host_addr, - ®_dump_area, sizeof(u32)) != 0) { - ath10k_warn("could not read hi_failure_state\n"); + ret = ath10k_pci_diag_read_mem(ar, host_addr, + ®_dump_area, sizeof(u32)); + if (ret) { + ath10k_err("failed to read FW dump area address: %d\n", ret); return; } @@ -708,7 +860,7 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) ®_dump_values[0], REG_DUMP_COUNT_QCA988X * sizeof(u32)); if (ret != 0) { - ath10k_err("could not dump FW Dump Area\n"); + ath10k_err("failed to read FW dump area: %d\n", ret); return; } @@ -723,12 +875,14 @@ static void ath10k_pci_hif_dump_area(struct ath10k *ar) reg_dump_values[i + 2], reg_dump_values[i + 3]); - ieee80211_queue_work(ar->hw, &ar->restart_work); + queue_work(ar->workqueue, &ar->restart_work); } static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe, int force) { + ath10k_dbg(ATH10K_DBG_PCI, "pci hif send complete check\n"); + if (!force) { int resources; /* @@ -755,211 +909,54 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + ath10k_dbg(ATH10K_DBG_PCI, "pci hif set callbacks\n"); memcpy(&ar_pci->msg_callbacks_current, callbacks, sizeof(ar_pci->msg_callbacks_current)); } -static int ath10k_pci_start_ce(struct ath10k *ar) +static int ath10k_pci_setup_ce_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_diag = ar_pci->ce_diag; const struct ce_attr *attr; - struct hif_ce_pipe_info *pipe_info; - struct ath10k_pci_compl *compl; - int i, pipe_num, completions, disable_interrupts; + struct ath10k_pci_pipe *pipe_info; + int pipe_num, disable_interrupts; - spin_lock_init(&ar_pci->compl_lock); - INIT_LIST_HEAD(&ar_pci->compl_process); - - for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + 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 == ce_diag) + if (pipe_info->ce_hdl == ar_pci->ce_diag) continue; attr = &host_ce_config_wlan[pipe_num]; - completions = 0; if (attr->src_nentries) { disable_interrupts = attr->flags & CE_ATTR_DIS_INTR; ath10k_ce_send_cb_register(pipe_info->ce_hdl, ath10k_pci_ce_send_done, disable_interrupts); - completions += attr->src_nentries; - pipe_info->num_sends_allowed = attr->src_nentries - 1; } - if (attr->dest_nentries) { + if (attr->dest_nentries) ath10k_ce_recv_cb_register(pipe_info->ce_hdl, ath10k_pci_ce_recv_data); - completions += attr->dest_nentries; - } - - if (completions == 0) - continue; - - for (i = 0; i < completions; i++) { - compl = kmalloc(sizeof(struct ath10k_pci_compl), - GFP_KERNEL); - if (!compl) { - ath10k_warn("No memory for completion state\n"); - ath10k_pci_stop_ce(ar); - return -ENOMEM; - } - - compl->send_or_recv = HIF_CE_COMPLETE_FREE; - list_add_tail(&compl->list, &pipe_info->compl_free); - } } return 0; } -static void ath10k_pci_stop_ce(struct ath10k *ar) +static void ath10k_pci_kill_tasklet(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_compl *compl; - struct sk_buff *skb; int i; - ath10k_ce_disable_interrupts(ar); - - /* Cancel the pending tasklet */ tasklet_kill(&ar_pci->intr_tq); + tasklet_kill(&ar_pci->msi_fw_err); + tasklet_kill(&ar_pci->early_irq_tasklet); for (i = 0; i < CE_COUNT; i++) tasklet_kill(&ar_pci->pipe_info[i].intr); - - /* 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 = (struct sk_buff *)compl->transfer_context; - 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 hif_ce_pipe_info *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 = (struct sk_buff *)compl->transfer_context; - 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 < ar_pci->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); - - if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) { - cb->tx_completion(ar, - compl->transfer_context, - compl->transfer_id); - send_done = 1; - } else { - ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1); - if (ret) { - ath10k_warn("Unable to post recv buffer for pipe: %d\n", - compl->pipe_info->pipe_num); - break; - } - - skb = (struct sk_buff *)compl->transfer_context; - 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)); - } - } - - compl->send_or_recv = HIF_CE_COMPLETE_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); - compl->pipe_info->num_sends_allowed += send_done; - 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 */ @@ -970,6 +967,8 @@ static int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, { int ret = 0; + ath10k_dbg(ATH10K_DBG_PCI, "pci hif map service\n"); + /* polling for received messages not supported */ *dl_is_polled = 0; @@ -1029,6 +1028,8 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, { int ul_is_polled, dl_is_polled; + ath10k_dbg(ATH10K_DBG_PCI, "pci hif get default pipe\n"); + (void)ath10k_pci_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_RSVD_CTRL, ul_pipe, @@ -1037,12 +1038,12 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, &dl_is_polled); } -static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, +static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, int num) { struct ath10k *ar = pipe_info->hif_ce_state; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_state = pipe_info->ce_hdl; + struct ath10k_ce_pipe *ce_state = pipe_info->ce_hdl; struct sk_buff *skb; dma_addr_t ce_data; int i, ret = 0; @@ -1053,7 +1054,7 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, for (i = 0; i < num; i++) { skb = dev_alloc_skb(pipe_info->buf_sz); if (!skb) { - ath10k_warn("could not allocate skbuff for pipe %d\n", + ath10k_warn("failed to allocate skbuff for pipe %d\n", num); ret = -ENOMEM; goto err; @@ -1066,7 +1067,7 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, DMA_FROM_DEVICE); if (unlikely(dma_mapping_error(ar->dev, ce_data))) { - ath10k_warn("could not dma map skbuff\n"); + ath10k_warn("failed to DMA map sk_buff\n"); dev_kfree_skb_any(skb); ret = -EIO; goto err; @@ -1081,7 +1082,7 @@ static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, ret = ath10k_ce_recv_buf_enqueue(ce_state, (void *)skb, ce_data); if (ret) { - ath10k_warn("could not enqueue to pipe %d (%d)\n", + ath10k_warn("failed to enqueue to pipe %d: %d\n", num, ret); goto err; } @@ -1097,11 +1098,11 @@ err: static int ath10k_pci_post_rx(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; const struct ce_attr *attr; int pipe_num, ret = 0; - for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { pipe_info = &ar_pci->pipe_info[pipe_num]; attr = &host_ce_config_wlan[pipe_num]; @@ -1111,8 +1112,8 @@ static int ath10k_pci_post_rx(struct ath10k *ar) ret = ath10k_pci_post_rx_pipe(pipe_info, attr->dest_nentries - 1); if (ret) { - ath10k_warn("Unable to replenish recv buffers for pipe: %d\n", - pipe_num); + ath10k_warn("failed to post RX buffer for pipe %d: %d\n", + pipe_num, ret); for (; pipe_num >= 0; pipe_num--) { pipe_info = &ar_pci->pipe_info[pipe_num]; @@ -1128,30 +1129,57 @@ static int ath10k_pci_post_rx(struct ath10k *ar) static int ath10k_pci_hif_start(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; + int ret, ret_early; - ret = ath10k_pci_start_ce(ar); + ath10k_dbg(ATH10K_DBG_BOOT, "boot hif start\n"); + + ath10k_pci_free_early_irq(ar); + ath10k_pci_kill_tasklet(ar); + + ret = ath10k_pci_request_irq(ar); if (ret) { - ath10k_warn("could not start CE (%d)\n", ret); - return ret; + ath10k_warn("failed to post RX buffers for all pipes: %d\n", + ret); + goto err_early_irq; + } + + ret = ath10k_pci_setup_ce_irq(ar); + if (ret) { + ath10k_warn("failed to setup CE interrupts: %d\n", ret); + goto err_stop; } /* Post buffers once to start things off. */ ret = ath10k_pci_post_rx(ar); if (ret) { - ath10k_warn("could not post rx pipes (%d)\n", ret); - return ret; + ath10k_warn("failed to post RX buffers for all pipes: %d\n", + ret); + goto err_stop; } ar_pci->started = 1; return 0; + +err_stop: + ath10k_ce_disable_interrupts(ar); + ath10k_pci_free_irq(ar); + ath10k_pci_kill_tasklet(ar); +err_early_irq: + /* Though there should be no interrupts (device was reset) + * power_down() expects the early IRQ to be installed as per the + * driver lifecycle. */ + ret_early = ath10k_pci_request_early_irq(ar); + if (ret_early) + ath10k_warn("failed to re-enable early irq: %d\n", ret_early); + + return ret; } -static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) { struct ath10k *ar; struct ath10k_pci *ar_pci; - struct ce_state *ce_hdl; + struct ath10k_ce_pipe *ce_hdl; u32 buf_sz; struct sk_buff *netbuf; u32 ce_data; @@ -1179,11 +1207,11 @@ static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) } } -static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) { struct ath10k *ar; struct ath10k_pci *ar_pci; - struct ce_state *ce_hdl; + struct ath10k_ce_pipe *ce_hdl; struct sk_buff *netbuf; u32 ce_data; unsigned int nbytes; @@ -1206,15 +1234,13 @@ static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf, &ce_data, &nbytes, &id) == 0) { - if (netbuf != CE_SENDLIST_ITEM_CTXT) - /* - * Indicate the completion to higer layer to free - * the buffer - */ - ATH10K_SKB_CB(netbuf)->is_aborted = true; - ar_pci->msg_callbacks_current.tx_completion(ar, - netbuf, - id); + /* no need to call tx completion for NULL pointers */ + if (!netbuf) + continue; + + ar_pci->msg_callbacks_current.tx_completion(ar, + netbuf, + id); } } @@ -1231,8 +1257,8 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int pipe_num; - for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { - struct hif_ce_pipe_info *pipe_info; + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { + struct ath10k_pci_pipe *pipe_info; pipe_info = &ar_pci->pipe_info[pipe_num]; ath10k_pci_rx_pipe_cleanup(pipe_info); @@ -1242,49 +1268,46 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar) static void ath10k_pci_ce_deinit(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info; - int pipe_num; - - for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { - pipe_info = &ar_pci->pipe_info[pipe_num]; - if (pipe_info->ce_hdl) { - ath10k_ce_deinit(pipe_info->ce_hdl); - pipe_info->ce_hdl = NULL; - pipe_info->buf_sz = 0; - } - } -} - -static void ath10k_pci_disable_irqs(struct ath10k *ar) -{ - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int i; - for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) - disable_irq(ar_pci->pdev->irq + i); + for (i = 0; i < CE_COUNT; i++) + ath10k_ce_deinit_pipe(ar, i); } static void ath10k_pci_hif_stop(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int ret; - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + ath10k_dbg(ATH10K_DBG_BOOT, "boot hif stop\n"); - /* Irqs are never explicitly re-enabled. They are implicitly re-enabled - * by ath10k_pci_start_intr(). */ - ath10k_pci_disable_irqs(ar); + if (WARN_ON(!ar_pci->started)) + return; - ath10k_pci_stop_ce(ar); + ret = ath10k_ce_disable_interrupts(ar); + if (ret) + ath10k_warn("failed to disable CE interrupts: %d\n", ret); + + ath10k_pci_free_irq(ar); + ath10k_pci_kill_tasklet(ar); + + ret = ath10k_pci_request_early_irq(ar); + if (ret) + ath10k_warn("failed to re-enable early irq: %d\n", ret); /* At this point, asynchronous threads are stopped, the target should * not DMA nor interrupt. We process the leftovers and then free * everything else up. */ - ath10k_pci_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 + * resetting it. The device was fed with PCI CE ringbuffer + * configuration during init. If ringbuffers are freed and the device + * were to access them this could lead to memory corruption on the + * host. */ + ath10k_pci_warm_reset(ar); + ar_pci->started = 0; } @@ -1293,14 +1316,18 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, void *resp, u32 *resp_len) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl; - struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl; + struct ath10k_pci_pipe *pci_tx = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; + struct ath10k_pci_pipe *pci_rx = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; + struct ath10k_ce_pipe *ce_tx = pci_tx->ce_hdl; + struct ath10k_ce_pipe *ce_rx = pci_rx->ce_hdl; dma_addr_t req_paddr = 0; dma_addr_t resp_paddr = 0; struct bmi_xfer xfer = {}; void *treq, *tresp = NULL; int ret = 0; + might_sleep(); + if (resp && !resp_len) return -EINVAL; @@ -1341,14 +1368,12 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, if (ret) goto err_resp; - ret = wait_for_completion_timeout(&xfer.done, - BMI_COMMUNICATION_TIMEOUT_HZ); - if (ret <= 0) { + ret = ath10k_pci_bmi_wait(ce_tx, ce_rx, &xfer); + if (ret) { u32 unused_buffer; unsigned int unused_nbytes; unsigned int unused_id; - ret = -ETIMEDOUT; ath10k_ce_cancel_send_next(ce_tx, NULL, &unused_buffer, &unused_nbytes, &unused_id); } else { @@ -1378,13 +1403,16 @@ err_dma: return ret; } -static void ath10k_pci_bmi_send_done(struct ce_state *ce_state, - void *transfer_context, - u32 data, - unsigned int nbytes, - unsigned int transfer_id) +static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state) { - struct bmi_xfer *xfer = transfer_context; + struct bmi_xfer *xfer; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; + + if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data, + &nbytes, &transfer_id)) + return; if (xfer->wait_for_resp) return; @@ -1392,14 +1420,17 @@ static void ath10k_pci_bmi_send_done(struct ce_state *ce_state, complete(&xfer->done); } -static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state, - void *transfer_context, - u32 data, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags) +static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) { - struct bmi_xfer *xfer = transfer_context; + struct bmi_xfer *xfer; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; + unsigned int flags; + + if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data, + &nbytes, &transfer_id, &flags)) + return; if (!xfer->wait_for_resp) { ath10k_warn("unexpected: BMI data received; ignoring\n"); @@ -1410,6 +1441,25 @@ static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state, complete(&xfer->done); } +static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, + struct ath10k_ce_pipe *rx_pipe, + struct bmi_xfer *xfer) +{ + unsigned long timeout = jiffies + BMI_COMMUNICATION_TIMEOUT_HZ; + + while (time_before_eq(jiffies, timeout)) { + ath10k_pci_bmi_send_done(tx_pipe); + ath10k_pci_bmi_recv_data(rx_pipe); + + if (completion_done(&xfer->done)) + return 0; + + schedule(); + } + + return -ETIMEDOUT; +} + /* * Map from service/endpoint to Copy Engine. * This table is derived from the CE_PCI TABLE, above. @@ -1519,7 +1569,7 @@ static int ath10k_pci_wake_target_cpu(struct ath10k *ar) CORE_CTRL_ADDRESS, &core_ctrl); if (ret) { - ath10k_warn("Unable to read core ctrl\n"); + ath10k_warn("failed to read core_ctrl: %d\n", ret); return ret; } @@ -1529,10 +1579,13 @@ static int ath10k_pci_wake_target_cpu(struct ath10k *ar) ret = ath10k_pci_diag_write_access(ar, SOC_CORE_BASE_ADDRESS | CORE_CTRL_ADDRESS, core_ctrl); - if (ret) - ath10k_warn("Unable to set interrupt mask\n"); + if (ret) { + ath10k_warn("failed to set target CPU interrupt mask: %d\n", + ret); + return ret; + } - return ret; + return 0; } static int ath10k_pci_init_config(struct ath10k *ar) @@ -1674,73 +1727,78 @@ static int ath10k_pci_init_config(struct ath10k *ar) return 0; } +static int ath10k_pci_alloc_ce(struct ath10k *ar) +{ + int i, ret; + for (i = 0; i < CE_COUNT; i++) { + ret = ath10k_ce_alloc_pipe(ar, i, &host_ce_config_wlan[i]); + if (ret) { + ath10k_err("failed to allocate copy engine pipe %d: %d\n", + i, ret); + return ret; + } + } + + return 0; +} + +static void ath10k_pci_free_ce(struct ath10k *ar) +{ + int i; + + for (i = 0; i < CE_COUNT; i++) + ath10k_ce_free_pipe(ar, i); +} static int ath10k_pci_ce_init(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; const struct ce_attr *attr; - int pipe_num; + int pipe_num, ret; - for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { + for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { pipe_info = &ar_pci->pipe_info[pipe_num]; + pipe_info->ce_hdl = &ar_pci->ce_states[pipe_num]; pipe_info->pipe_num = pipe_num; pipe_info->hif_ce_state = ar; attr = &host_ce_config_wlan[pipe_num]; - pipe_info->ce_hdl = ath10k_ce_init(ar, pipe_num, attr); - if (pipe_info->ce_hdl == NULL) { - ath10k_err("Unable to initialize CE for pipe: %d\n", - pipe_num); - - /* It is safe to call it here. It checks if ce_hdl is - * valid for each pipe */ - ath10k_pci_ce_deinit(ar); - return -1; + ret = ath10k_ce_init_pipe(ar, pipe_num, attr); + if (ret) { + ath10k_err("failed to initialize copy engine pipe %d: %d\n", + pipe_num, ret); + return ret; } - if (pipe_num == ar_pci->ce_count - 1) { + if (pipe_num == CE_COUNT - 1) { /* * Reserve the ultimate CE for * diagnostic Window support */ - ar_pci->ce_diag = - ar_pci->pipe_info[ar_pci->ce_count - 1].ce_hdl; + ar_pci->ce_diag = pipe_info->ce_hdl; continue; } pipe_info->buf_sz = (size_t) (attr->src_sz_max); } - /* - * Initially, establish CE completion handlers for use with BMI. - * These are overwritten with generic handlers after we exit BMI phase. - */ - pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; - ath10k_ce_send_cb_register(pipe_info->ce_hdl, - ath10k_pci_bmi_send_done, 0); - - pipe_info = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; - ath10k_ce_recv_cb_register(pipe_info->ce_hdl, - ath10k_pci_bmi_recv_data); - return 0; } static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - u32 fw_indicator_address, fw_indicator; + u32 fw_indicator; ath10k_pci_wake(ar); - fw_indicator_address = ar_pci->fw_indicator_address; - fw_indicator = ath10k_pci_read32(ar, fw_indicator_address); + fw_indicator = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); if (fw_indicator & FW_IND_EVENT_PENDING) { /* ACK: clear Target-side pending event */ - ath10k_pci_write32(ar, fw_indicator_address, + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, fw_indicator & ~FW_IND_EVENT_PENDING); if (ar_pci->started) { @@ -1757,17 +1815,120 @@ static void ath10k_pci_fw_interrupt_handler(struct ath10k *ar) ath10k_pci_sleep(ar); } -static int ath10k_pci_hif_power_up(struct ath10k *ar) +/* this function effectively clears target memory controller assert line */ +static void ath10k_pci_warm_reset_si0(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; + u32 val; + + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_SI0_RST_MASK); + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + + msleep(10); + + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_soc_write32(ar, SOC_RESET_CONTROL_ADDRESS, + val & ~SOC_RESET_CONTROL_SI0_RST_MASK); + val = ath10k_pci_soc_read32(ar, SOC_RESET_CONTROL_ADDRESS); + + msleep(10); +} + +static int ath10k_pci_warm_reset(struct ath10k *ar) +{ + int ret = 0; + u32 val; + + ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset\n"); - ret = ath10k_pci_start_intr(ar); + ret = ath10k_do_pci_wake(ar); if (ret) { - ath10k_err("could not start interrupt handling (%d)\n", ret); - goto err; + ath10k_err("failed to wake up target: %d\n", ret); + return ret; } + /* debug */ + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val); + + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + CPU_INTR_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", + val); + + /* disable pending irqs */ + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_ENABLE_ADDRESS, 0); + + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CLR_ADDRESS, ~0); + + msleep(100); + + /* clear fw indicator */ + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, 0); + + /* clear target LF timer interrupts */ + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_LF_TIMER_CONTROL0_ADDRESS); + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + + SOC_LF_TIMER_CONTROL0_ADDRESS, + val & ~SOC_LF_TIMER_CONTROL0_ENABLE_MASK); + + /* reset CE */ + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_CE_RST_MASK); + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + msleep(10); + + /* unreset CE */ + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS, + val & ~SOC_RESET_CONTROL_CE_RST_MASK); + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + msleep(10); + + ath10k_pci_warm_reset_si0(ar); + + /* debug */ + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + PCIE_INTR_CAUSE_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot host cpu intr cause: 0x%08x\n", val); + + val = ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + + CPU_INTR_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target cpu intr cause: 0x%08x\n", + val); + + /* CPU warm reset */ + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + SOC_RESET_CONTROL_ADDRESS, + val | SOC_RESET_CONTROL_CPU_WARM_RST_MASK); + + val = ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + + SOC_RESET_CONTROL_ADDRESS); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target reset state: 0x%08x\n", val); + + msleep(100); + + ath10k_dbg(ATH10K_DBG_BOOT, "boot warm reset complete\n"); + + ath10k_do_pci_sleep(ar); + return ret; +} + +static int __ath10k_pci_hif_power_up(struct ath10k *ar, bool cold_reset) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + const char *irq_mode; + int ret; + /* * Bring the target up cleanly. * @@ -1778,50 +1939,159 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) * is in an unexpected state. We try to catch that here in order to * reset the Target and retry the probe. */ - ath10k_pci_device_reset(ar); + if (cold_reset) + ret = ath10k_pci_cold_reset(ar); + else + ret = ath10k_pci_warm_reset(ar); - ret = ath10k_pci_reset_target(ar); - if (ret) - goto err_irq; + if (ret) { + ath10k_err("failed to reset target: %d\n", ret); + goto err; + } if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) /* Force AWAKE forever */ ath10k_do_pci_wake(ar); ret = ath10k_pci_ce_init(ar); - if (ret) + if (ret) { + ath10k_err("failed to initialize CE: %d\n", ret); goto err_ps; + } - ret = ath10k_pci_init_config(ar); - if (ret) + ret = ath10k_ce_disable_interrupts(ar); + if (ret) { + ath10k_err("failed to disable CE interrupts: %d\n", ret); goto err_ce; + } - ret = ath10k_pci_wake_target_cpu(ar); + ret = ath10k_pci_init_irq(ar); if (ret) { - ath10k_err("could not wake up target CPU (%d)\n", ret); + ath10k_err("failed to init irqs: %d\n", ret); goto err_ce; } + ret = ath10k_pci_request_early_irq(ar); + if (ret) { + ath10k_err("failed to request early irq: %d\n", ret); + goto err_deinit_irq; + } + + ret = ath10k_pci_wait_for_target_init(ar); + if (ret) { + ath10k_err("failed to wait for target to init: %d\n", ret); + goto err_free_early_irq; + } + + ret = ath10k_pci_init_config(ar); + if (ret) { + ath10k_err("failed to setup init config: %d\n", ret); + goto err_free_early_irq; + } + + ret = ath10k_pci_wake_target_cpu(ar); + if (ret) { + ath10k_err("could not wake up target CPU: %d\n", ret); + goto err_free_early_irq; + } + + if (ar_pci->num_msi_intrs > 1) + irq_mode = "MSI-X"; + else if (ar_pci->num_msi_intrs == 1) + irq_mode = "MSI"; + else + irq_mode = "legacy"; + + if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) + ath10k_info("pci irq %s irq_mode %d reset_mode %d\n", + irq_mode, ath10k_pci_irq_mode, + ath10k_pci_reset_mode); + return 0; +err_free_early_irq: + ath10k_pci_free_early_irq(ar); +err_deinit_irq: + ath10k_pci_deinit_irq(ar); err_ce: ath10k_pci_ce_deinit(ar); + ath10k_pci_warm_reset(ar); err_ps: if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_sleep(ar); -err_irq: - ath10k_pci_stop_intr(ar); err: return ret; } +static int ath10k_pci_hif_power_up_warm(struct ath10k *ar) +{ + int i, ret; + + /* + * Sometime warm reset succeeds after retries. + * + * FIXME: It might be possible to tune ath10k_pci_warm_reset() to work + * at first try. + */ + for (i = 0; i < ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS; i++) { + ret = __ath10k_pci_hif_power_up(ar, false); + if (ret == 0) + break; + + ath10k_warn("failed to warm reset (attempt %d out of %d): %d\n", + i + 1, ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS, ret); + } + + return ret; +} + +static int ath10k_pci_hif_power_up(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power up\n"); + + /* + * Hardware CUS232 version 2 has some issues with cold reset and the + * preferred (and safer) way to perform a device reset is through a + * warm reset. + * + * Warm reset doesn't always work though so fall back to cold reset may + * be necessary. + */ + ret = ath10k_pci_hif_power_up_warm(ar); + if (ret) { + ath10k_warn("failed to power up target using warm reset: %d\n", + ret); + + if (ath10k_pci_reset_mode == ATH10K_PCI_RESET_WARM_ONLY) + return ret; + + ath10k_warn("trying cold reset\n"); + + ret = __ath10k_pci_hif_power_up(ar, true); + if (ret) { + ath10k_err("failed to power up target using cold reset too (%d)\n", + ret); + return ret; + } + } + + return 0; +} + static void ath10k_pci_hif_power_down(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ath10k_pci_stop_intr(ar); + ath10k_dbg(ATH10K_DBG_BOOT, "boot hif power down\n"); + ath10k_pci_free_early_irq(ar); + ath10k_pci_kill_tasklet(ar); + ath10k_pci_deinit_irq(ar); ath10k_pci_ce_deinit(ar); + ath10k_pci_warm_reset(ar); + if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) ath10k_do_pci_sleep(ar); } @@ -1876,7 +2146,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, @@ -1895,7 +2165,7 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { static void ath10k_pci_ce_tasklet(unsigned long ptr) { - struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr; + struct ath10k_pci_pipe *pipe = (struct ath10k_pci_pipe *)ptr; struct ath10k_pci *ar_pci = pipe->ar_pci; ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num); @@ -1955,25 +2225,10 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (ar_pci->num_msi_intrs == 0) { - /* - * IMPORTANT: INTR_CLR regiser has to be set after - * INTR_ENABLE is set to 0, otherwise interrupt can not be - * really cleared. - */ - iowrite32(0, ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_CLR_ADDRESS)); - /* - * IMPORTANT: this extra read transaction is required to - * flush the posted write buffer. - */ - (void) ioread32(ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); + if (!ath10k_pci_irq_pending(ar)) + return IRQ_NONE; + + ath10k_pci_disable_and_clear_legacy_irq(ar); } tasklet_schedule(&ar_pci->intr_tq); @@ -1981,6 +2236,30 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) return IRQ_HANDLED; } +static void ath10k_pci_early_irq_tasklet(unsigned long data) +{ + struct ath10k *ar = (struct ath10k *)data; + u32 fw_ind; + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target in early irq tasklet: %d\n", + ret); + return; + } + + fw_ind = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); + if (fw_ind & FW_IND_EVENT_PENDING) { + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, + fw_ind & ~FW_IND_EVENT_PENDING); + ath10k_pci_hif_dump_area(ar); + } + + ath10k_pci_sleep(ar); + ath10k_pci_enable_legacy_irq(ar); +} + static void ath10k_pci_tasklet(unsigned long data) { struct ath10k *ar = (struct ath10k *)data; @@ -1989,40 +2268,22 @@ static void ath10k_pci_tasklet(unsigned long data) ath10k_pci_fw_interrupt_handler(ar); /* FIXME: Handle FW error */ ath10k_ce_per_engine_service_any(ar); - if (ar_pci->num_msi_intrs == 0) { - /* Enable Legacy PCI line interrupts */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - /* - * IMPORTANT: this extra read transaction is required to - * flush the posted write buffer - */ - (void) ioread32(ar_pci->mem + - (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - } + /* Re-enable legacy irq that was disabled in the irq handler */ + if (ar_pci->num_msi_intrs == 0) + ath10k_pci_enable_legacy_irq(ar); } -static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) +static int ath10k_pci_request_irq_msix(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ret; - int i; - - ret = pci_enable_msi_block(ar_pci->pdev, num); - if (ret) - return ret; + int ret, i; ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ath10k_pci_msi_fw_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("request_irq(%d) failed %d\n", + ath10k_warn("failed to request MSI-X fw irq %d: %d\n", ar_pci->pdev->irq + MSI_ASSIGN_FW, ret); - - pci_disable_msi(ar_pci->pdev); return ret; } @@ -2031,44 +2292,38 @@ static int ath10k_pci_start_intr_msix(struct ath10k *ar, int num) ath10k_pci_per_engine_handler, IRQF_SHARED, "ath10k_pci", ar); if (ret) { - ath10k_warn("request_irq(%d) failed %d\n", + ath10k_warn("failed to request MSI-X ce irq %d: %d\n", ar_pci->pdev->irq + i, ret); for (i--; i >= MSI_ASSIGN_CE_INITIAL; i--) free_irq(ar_pci->pdev->irq + i, ar); free_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW, ar); - pci_disable_msi(ar_pci->pdev); return ret; } } - ath10k_info("MSI-X interrupt handling (%d intrs)\n", num); return 0; } -static int ath10k_pci_start_intr_msi(struct ath10k *ar) +static int ath10k_pci_request_irq_msi(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - ret = pci_enable_msi(ar_pci->pdev); - if (ret < 0) - return ret; - ret = request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret < 0) { - pci_disable_msi(ar_pci->pdev); + if (ret) { + ath10k_warn("failed to request MSI irq %d: %d\n", + ar_pci->pdev->irq, ret); return ret; } - ath10k_info("MSI interrupt handling\n"); return 0; } -static int ath10k_pci_start_intr_legacy(struct ath10k *ar) +static int ath10k_pci_request_irq_legacy(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; @@ -2076,168 +2331,247 @@ static int ath10k_pci_start_intr_legacy(struct ath10k *ar) ret = request_irq(ar_pci->pdev->irq, ath10k_pci_interrupt_handler, IRQF_SHARED, "ath10k_pci", ar); - if (ret < 0) + if (ret) { + ath10k_warn("failed to request legacy irq %d: %d\n", + ar_pci->pdev->irq, ret); return ret; + } - /* - * Make sure to wake the Target before enabling Legacy - * Interrupt. - */ - iowrite32(PCIE_SOC_WAKE_V_MASK, - ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); + return 0; +} - ath10k_pci_wait(ar); +static int ath10k_pci_request_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - /* - * A potential race occurs here: The CORE_BASE write - * depends on target correctly decoding AXI address but - * host won't know when target writes BAR to CORE_CTRL. - * This write might get lost if target has NOT written BAR. - * For now, fix the race by repeating the write in below - * synchronization checking. - */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); - iowrite32(PCIE_SOC_WAKE_RESET, - ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); - - ath10k_info("legacy interrupt handling\n"); - return 0; + switch (ar_pci->num_msi_intrs) { + case 0: + return ath10k_pci_request_irq_legacy(ar); + case 1: + return ath10k_pci_request_irq_msi(ar); + case MSI_NUM_REQUEST: + return ath10k_pci_request_irq_msix(ar); + } + + ath10k_warn("unknown irq configuration upon request\n"); + return -EINVAL; } -static int ath10k_pci_start_intr(struct ath10k *ar) +static void ath10k_pci_free_irq(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int num = MSI_NUM_REQUEST; - int ret; int i; - tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long) ar); + /* There's at least one interrupt irregardless whether its legacy INTR + * or MSI or MSI-X */ + for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) + free_irq(ar_pci->pdev->irq + i, ar); +} + +static void ath10k_pci_init_irq_tasklets(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int i; + + tasklet_init(&ar_pci->intr_tq, ath10k_pci_tasklet, (unsigned long)ar); tasklet_init(&ar_pci->msi_fw_err, ath10k_msi_err_tasklet, - (unsigned long) ar); + (unsigned long)ar); + tasklet_init(&ar_pci->early_irq_tasklet, ath10k_pci_early_irq_tasklet, + (unsigned long)ar); for (i = 0; i < CE_COUNT; i++) { ar_pci->pipe_info[i].ar_pci = ar_pci; - tasklet_init(&ar_pci->pipe_info[i].intr, - ath10k_pci_ce_tasklet, + tasklet_init(&ar_pci->pipe_info[i].intr, ath10k_pci_ce_tasklet, (unsigned long)&ar_pci->pipe_info[i]); } +} - if (!test_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features)) - num = 1; +static int ath10k_pci_init_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + bool msix_supported = test_bit(ATH10K_PCI_FEATURE_MSI_X, + ar_pci->features); + int ret; - if (num > 1) { - ret = ath10k_pci_start_intr_msix(ar, num); - if (ret == 0) - goto exit; + ath10k_pci_init_irq_tasklets(ar); + + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_AUTO && + !test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) + ath10k_info("limiting irq mode to: %d\n", ath10k_pci_irq_mode); + + /* Try MSI-X */ + if (ath10k_pci_irq_mode == ATH10K_PCI_IRQ_AUTO && msix_supported) { + ar_pci->num_msi_intrs = MSI_NUM_REQUEST; + ret = pci_enable_msi_range(ar_pci->pdev, ar_pci->num_msi_intrs, + ar_pci->num_msi_intrs); + if (ret > 0) + return 0; - ath10k_warn("MSI-X didn't succeed (%d), trying MSI\n", ret); - num = 1; + /* fall-through */ } - if (num == 1) { - ret = ath10k_pci_start_intr_msi(ar); + /* Try MSI */ + if (ath10k_pci_irq_mode != ATH10K_PCI_IRQ_LEGACY) { + ar_pci->num_msi_intrs = 1; + ret = pci_enable_msi(ar_pci->pdev); if (ret == 0) - goto exit; + return 0; - ath10k_warn("MSI didn't succeed (%d), trying legacy INTR\n", - ret); - num = 0; + /* fall-through */ } - ret = ath10k_pci_start_intr_legacy(ar); + /* Try legacy irq + * + * A potential race occurs here: The CORE_BASE write + * depends on target correctly decoding AXI address but + * host won't know when target writes BAR to CORE_CTRL. + * This write might get lost if target has NOT written BAR. + * For now, fix the race by repeating the write in below + * synchronization checking. */ + ar_pci->num_msi_intrs = 0; -exit: - ar_pci->num_msi_intrs = num; - ar_pci->ce_count = CE_COUNT; - return ret; + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target: %d\n", ret); + return ret; + } + + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | PCIE_INTR_CE_MASK_ALL); + ath10k_pci_sleep(ar); + + return 0; } -static void ath10k_pci_stop_intr(struct ath10k *ar) +static int ath10k_pci_deinit_irq_legacy(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int i; + int ret; - /* There's at least one interrupt irregardless whether its legacy INTR - * or MSI or MSI-X */ - for (i = 0; i < max(1, ar_pci->num_msi_intrs); i++) - free_irq(ar_pci->pdev->irq + i, ar); + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_warn("failed to wake target: %d\n", ret); + return ret; + } + + ath10k_pci_write32(ar, SOC_CORE_BASE_ADDRESS + PCIE_INTR_ENABLE_ADDRESS, + 0); + ath10k_pci_sleep(ar); - if (ar_pci->num_msi_intrs > 0) + return 0; +} + +static int ath10k_pci_deinit_irq(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + switch (ar_pci->num_msi_intrs) { + case 0: + return ath10k_pci_deinit_irq_legacy(ar); + case 1: + /* fall-through */ + case MSI_NUM_REQUEST: + pci_disable_msi(ar_pci->pdev); + return 0; + default: pci_disable_msi(ar_pci->pdev); + } + + ath10k_warn("unknown irq configuration upon deinit\n"); + return -EINVAL; } -static int ath10k_pci_reset_target(struct ath10k *ar) +static int ath10k_pci_wait_for_target_init(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int wait_limit = 300; /* 3 sec */ + unsigned long timeout; + int ret; + u32 val; + + ath10k_dbg(ATH10K_DBG_BOOT, "boot waiting target to initialise\n"); + + ret = ath10k_pci_wake(ar); + if (ret) { + ath10k_err("failed to wake up target for init: %d\n", ret); + return ret; + } + + timeout = jiffies + msecs_to_jiffies(ATH10K_PCI_TARGET_WAIT); - /* Wait for Target to finish initialization before we proceed. */ - iowrite32(PCIE_SOC_WAKE_V_MASK, - ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); + do { + val = ath10k_pci_read32(ar, FW_INDICATOR_ADDRESS); - ath10k_pci_wait(ar); + ath10k_dbg(ATH10K_DBG_BOOT, "boot target indicator %x\n", val); + + /* target should never return this */ + if (val == 0xffffffff) + continue; + + /* the device has crashed so don't bother trying anymore */ + if (val & FW_IND_EVENT_PENDING) + break; + + if (val & FW_IND_INITIALIZED) + break; - while (wait_limit-- && - !(ioread32(ar_pci->mem + FW_INDICATOR_ADDRESS) & - FW_IND_INITIALIZED)) { if (ar_pci->num_msi_intrs == 0) /* Fix potential race by repeating CORE_BASE writes */ - iowrite32(PCIE_INTR_FIRMWARE_MASK | - PCIE_INTR_CE_MASK_ALL, - ar_pci->mem + (SOC_CORE_BASE_ADDRESS | - PCIE_INTR_ENABLE_ADDRESS)); + ath10k_pci_soc_write32(ar, PCIE_INTR_ENABLE_ADDRESS, + PCIE_INTR_FIRMWARE_MASK | + PCIE_INTR_CE_MASK_ALL); + mdelay(10); + } while (time_before(jiffies, timeout)); + + if (val == 0xffffffff) { + ath10k_err("failed to read device register, device is gone\n"); + ret = -EIO; + goto out; } - if (wait_limit < 0) { - ath10k_err("Target stalled\n"); - iowrite32(PCIE_SOC_WAKE_RESET, - ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); - return -EIO; + if (val & FW_IND_EVENT_PENDING) { + ath10k_warn("device has crashed during init\n"); + ath10k_pci_write32(ar, FW_INDICATOR_ADDRESS, + val & ~FW_IND_EVENT_PENDING); + ath10k_pci_hif_dump_area(ar); + ret = -ECOMM; + goto out; } - iowrite32(PCIE_SOC_WAKE_RESET, - ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + - PCIE_SOC_WAKE_ADDRESS); + if (!(val & FW_IND_INITIALIZED)) { + ath10k_err("failed to receive initialized event from target: %08x\n", + val); + ret = -ETIMEDOUT; + goto out; + } - return 0; + ath10k_dbg(ATH10K_DBG_BOOT, "boot target initialised\n"); + +out: + ath10k_pci_sleep(ar); + return ret; } -static void ath10k_pci_device_reset(struct ath10k *ar) +static int ath10k_pci_cold_reset(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *mem = ar_pci->mem; - int i; + int i, ret; u32 val; - if (!SOC_GLOBAL_RESET_ADDRESS) - return; + ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset\n"); - if (!mem) - return; - - ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, - PCIE_SOC_WAKE_V_MASK); - for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { - if (ath10k_pci_target_is_awake(ar)) - break; - msleep(1); + ret = ath10k_do_pci_wake(ar); + if (ret) { + ath10k_err("failed to wake up target: %d\n", + ret); + return ret; } /* Put Target, including PCIe, into RESET. */ - val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS); + val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS); val |= 1; - ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val); for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { - if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + if (ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) & RTC_STATE_COLD_RESET_MASK) break; msleep(1); @@ -2245,16 +2579,20 @@ static void ath10k_pci_device_reset(struct ath10k *ar) /* Pull Target, including PCIe, out of RESET. */ val &= ~1; - ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val); for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { - if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + if (!(ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) & RTC_STATE_COLD_RESET_MASK)) break; msleep(1); } - ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); + ath10k_do_pci_sleep(ar); + + ath10k_dbg(ATH10K_DBG_BOOT, "boot cold reset complete\n"); + + return 0; } static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) @@ -2267,13 +2605,10 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) switch (i) { case ATH10K_PCI_FEATURE_MSI_X: - ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); - break; - case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND: - ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n"); + ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n"); break; case ATH10K_PCI_FEATURE_SOC_POWER_SAVE: - ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n"); + ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n"); break; } } @@ -2286,9 +2621,9 @@ static int ath10k_pci_probe(struct pci_dev *pdev, int ret = 0; struct ath10k *ar; struct ath10k_pci *ar_pci; - u32 lcr_val; + u32 lcr_val, chip_id; - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + ath10k_dbg(ATH10K_DBG_PCI, "pci probe\n"); ar_pci = kzalloc(sizeof(*ar_pci), GFP_KERNEL); if (ar_pci == NULL) @@ -2298,62 +2633,42 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar_pci->dev = &pdev->dev; switch (pci_dev->device) { - case QCA988X_1_0_DEVICE_ID: - set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features); - break; case QCA988X_2_0_DEVICE_ID: set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); break; default: ret = -ENODEV; - ath10k_err("Unkown device ID: %d\n", pci_dev->device); + ath10k_err("Unknown device ID: %d\n", pci_dev->device); goto err_ar_pci; } - if (ath10k_target_ps) + if (ath10k_pci_target_ps) set_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features); ath10k_pci_dump_features(ar_pci); ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops); if (!ar) { - ath10k_err("ath10k_core_create failed!\n"); + ath10k_err("failed to create driver core\n"); ret = -EINVAL; goto err_ar_pci; } - /* Enable QCA988X_1.0 HW workarounds */ - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) - spin_lock_init(&ar_pci->hw_v1_workaround_lock); - ar_pci->ar = ar; - ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS; atomic_set(&ar_pci->keep_awake_count, 0); pci_set_drvdata(pdev, ar); - /* - * Without any knowledge of the Host, the Target may have been reset or - * power cycled and its Config Space may no longer reflect the PCI - * address space that was assigned earlier by the PCI infrastructure. - * Refresh it now. - */ - ret = pci_assign_resource(pdev, BAR_NUM); - if (ret) { - ath10k_err("cannot assign PCI space: %d\n", ret); - goto err_ar; - } - ret = pci_enable_device(pdev); if (ret) { - ath10k_err("cannot enable PCI device: %d\n", ret); + ath10k_err("failed to enable PCI device: %d\n", ret); goto err_ar; } /* Request MMIO resources */ ret = pci_request_region(pdev, BAR_NUM, "ath"); if (ret) { - ath10k_err("PCI MMIO reservation error: %d\n", ret); + ath10k_err("failed to request MMIO region: %d\n", ret); goto err_device; } @@ -2363,13 +2678,13 @@ static int ath10k_pci_probe(struct pci_dev *pdev, */ ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - ath10k_err("32-bit DMA not available: %d\n", ret); + ath10k_err("failed to set DMA mask to 32-bit: %d\n", ret); goto err_region; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - ath10k_err("cannot enable 32-bit consistent DMA\n"); + ath10k_err("failed to set consistent DMA mask to 32-bit\n"); goto err_region; } @@ -2386,7 +2701,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, /* Arrange for access to Target SoC registers. */ mem = pci_iomap(pdev, BAR_NUM, 0); if (!mem) { - ath10k_err("PCI iomap error\n"); + ath10k_err("failed to perform IOMAP for BAR%d\n", BAR_NUM); ret = -EIO; goto err_master; } @@ -2395,16 +2710,34 @@ static int ath10k_pci_probe(struct pci_dev *pdev, spin_lock_init(&ar_pci->ce_lock); - ar_pci->cacheline_sz = dma_get_cache_alignment(); + ret = ath10k_do_pci_wake(ar); + if (ret) { + ath10k_err("Failed to get chip id: %d\n", ret); + goto err_iomap; + } - ret = ath10k_core_register(ar); + chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); + + ath10k_do_pci_sleep(ar); + + ret = ath10k_pci_alloc_ce(ar); if (ret) { - ath10k_err("could not register driver core (%d)\n", ret); + ath10k_err("failed to allocate copy engine pipes: %d\n", ret); goto err_iomap; } + ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem); + + ret = ath10k_core_register(ar, chip_id); + if (ret) { + ath10k_err("failed to register driver core: %d\n", ret); + goto err_free_ce; + } + return 0; +err_free_ce: + ath10k_pci_free_ce(ar); err_iomap: pci_iounmap(pdev, mem); err_master: @@ -2414,7 +2747,6 @@ err_region: err_device: pci_disable_device(pdev); err_ar: - pci_set_drvdata(pdev, NULL); ath10k_core_destroy(ar); err_ar_pci: /* call HIF PCI free here */ @@ -2428,7 +2760,7 @@ static void ath10k_pci_remove(struct pci_dev *pdev) struct ath10k *ar = pci_get_drvdata(pdev); struct ath10k_pci *ar_pci; - ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); + ath10k_dbg(ATH10K_DBG_PCI, "pci remove\n"); if (!ar) return; @@ -2438,11 +2770,9 @@ static void ath10k_pci_remove(struct pci_dev *pdev) if (!ar_pci) return; - tasklet_kill(&ar_pci->msi_fw_err); - ath10k_core_unregister(ar); + ath10k_pci_free_ce(ar); - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ar_pci->mem); pci_release_region(pdev, BAR_NUM); pci_clear_master(pdev); @@ -2467,7 +2797,7 @@ static int __init ath10k_pci_init(void) ret = pci_register_driver(&ath10k_pci_driver); if (ret) - ath10k_err("pci_register_driver failed [%d]\n", ret); + ath10k_err("failed to register PCI driver: %d\n", ret); return ret; } @@ -2483,9 +2813,5 @@ module_exit(ath10k_pci_exit); MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE); -MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE); -MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE); -MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE); -MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE); +MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_2_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 871bb339d56..dfdebb4157a 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -43,22 +43,6 @@ struct bmi_xfer { u32 resp_len; }; -struct ath10k_pci_compl { - struct list_head list; - int send_or_recv; - struct ce_state *ce_state; - struct hif_ce_pipe_info *pipe_info; - void *transfer_context; - unsigned int nbytes; - unsigned int transfer_id; - unsigned int flags; -}; - -/* compl_state.send_or_recv */ -#define HIF_CE_COMPLETE_FREE 0 -#define HIF_CE_COMPLETE_SEND 1 -#define HIF_CE_COMPLETE_RECV 2 - /* * PCI-specific Target state * @@ -152,17 +136,16 @@ struct service_to_pipe { enum ath10k_pci_features { ATH10K_PCI_FEATURE_MSI_X = 0, - ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1, - ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 2, + ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 1, /* keep last */ ATH10K_PCI_FEATURE_COUNT }; /* Per-pipe state. */ -struct hif_ce_pipe_info { +struct ath10k_pci_pipe { /* Handle of underlying Copy Engine */ - struct ce_state *ce_hdl; + struct ath10k_ce_pipe *ce_hdl; /* Our pipe number; facilitiates use of pipe_info ptrs. */ u8 pipe_num; @@ -175,12 +158,6 @@ struct hif_ce_pipe_info { /* protects compl_free and num_send_allowed */ spinlock_t pipe_lock; - /* List of free CE completion slots */ - struct list_head compl_free; - - /* Limit the number of outstanding send requests. */ - int num_sends_allowed; - struct ath10k_pci *ar_pci; struct tasklet_struct intr; }; @@ -190,7 +167,6 @@ struct ath10k_pci { struct device *dev; struct ath10k *ar; void __iomem *mem; - int cacheline_sz; DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT); @@ -202,41 +178,25 @@ struct ath10k_pci { struct tasklet_struct intr_tq; struct tasklet_struct msi_fw_err; - - /* Number of Copy Engines supported */ - unsigned int ce_count; + struct tasklet_struct early_irq_tasklet; int started; 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 hif_ce_pipe_info pipe_info[CE_COUNT_MAX]; + struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX]; struct ath10k_hif_cb msg_callbacks_current; - /* Target address used to signal a pending firmware event */ - u32 fw_indicator_address; - /* Copy Engine used for Diagnostic Accesses */ - struct ce_state *ce_diag; + struct ath10k_ce_pipe *ce_diag; /* FIXME: document what this really protects */ spinlock_t ce_lock; /* Map CE id to ce_state */ - struct ce_state *ce_id_to_state[CE_COUNT_MAX]; - - /* makes sure that dummy reads are atomic */ - spinlock_t hw_v1_workaround_lock; + struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) @@ -244,14 +204,18 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) return ar->hif.priv; } -static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr) +static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr) { - return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); } -static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val) +static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) { - iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); } #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ @@ -310,23 +274,8 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *addr = ar_pci->mem; - - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { - unsigned long irq_flags; - spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); - - ioread32(addr+offset+4); /* 3rd read prior to write */ - ioread32(addr+offset+4); /* 2nd read prior to write */ - ioread32(addr+offset+4); /* 1st read prior to write */ - iowrite32(value, addr+offset); - - spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock, - irq_flags); - } else { - iowrite32(value, addr+offset); - } + iowrite32(value, ar_pci->mem + offset); } static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) @@ -336,15 +285,27 @@ static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) return ioread32(ar_pci->mem + offset); } -void ath10k_do_pci_wake(struct ath10k *ar); +static inline u32 ath10k_pci_soc_read32(struct ath10k *ar, u32 addr) +{ + return ath10k_pci_read32(ar, RTC_SOC_BASE_ADDRESS + addr); +} + +static inline void ath10k_pci_soc_write32(struct ath10k *ar, u32 addr, u32 val) +{ + ath10k_pci_write32(ar, RTC_SOC_BASE_ADDRESS + addr, val); +} + +int ath10k_do_pci_wake(struct ath10k *ar); void ath10k_do_pci_sleep(struct ath10k *ar); -static inline void ath10k_pci_wake(struct ath10k *ar) +static inline int ath10k_pci_wake(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - ath10k_do_pci_wake(ar); + return ath10k_do_pci_wake(ar); + + return 0; } static inline void ath10k_pci_sleep(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index bfec6c8f2ec..1c584c4b019 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -422,10 +422,30 @@ struct rx_mpdu_end { #define RX_MSDU_START_INFO1_IP_FRAG (1 << 14) #define RX_MSDU_START_INFO1_TCP_ONLY_ACK (1 << 15) +/* The decapped header (rx_hdr_status) contains the following: + * a) 802.11 header + * [padding to 4 bytes] + * b) HW crypto parameter + * - 0 bytes for no security + * - 4 bytes for WEP + * - 8 bytes for TKIP, AES + * [padding to 4 bytes] + * c) A-MSDU subframe header (14 bytes) if appliable + * d) LLC/SNAP (RFC1042, 8 bytes) + * + * In case of A-MSDU only first frame in sequence contains (a) and (b). */ enum rx_msdu_decap_format { - RX_MSDU_DECAP_RAW = 0, - RX_MSDU_DECAP_NATIVE_WIFI = 1, + RX_MSDU_DECAP_RAW = 0, + + /* Note: QoS frames are reported as non-QoS. The rx_hdr_status in + * htt_rx_desc contains the original decapped 802.11 header. */ + RX_MSDU_DECAP_NATIVE_WIFI = 1, + + /* Payload contains an ethernet header (struct ethhdr). */ RX_MSDU_DECAP_ETHERNET2_DIX = 2, + + /* Payload contains two 48-bit addresses and 2-byte length (14 bytes + * total), followed by an RFC1042 header (8 bytes). */ RX_MSDU_DECAP_8023_SNAP_LLC = 3 }; diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 85e806bf725..4eb2ecbc06e 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump, ); TRACE_EVENT(ath10k_wmi_cmd, - TP_PROTO(int id, void *buf, size_t buf_len), + TP_PROTO(int id, void *buf, size_t buf_len, int ret), - TP_ARGS(id, buf, buf_len), + TP_ARGS(id, buf, buf_len, ret), TP_STRUCT__entry( __field(unsigned int, id) __field(size_t, buf_len) __dynamic_array(u8, buf, buf_len) + __field(int, ret) ), TP_fast_assign( __entry->id = id; __entry->buf_len = buf_len; + __entry->ret = ret; memcpy(__get_dynamic_array(buf), buf, buf_len); ), TP_printk( - "id %d len %zu", + "id %d len %zu ret %d", __entry->id, - __entry->buf_len + __entry->buf_len, + __entry->ret ) ); @@ -158,6 +161,48 @@ TRACE_EVENT(ath10k_wmi_event, ) ); +TRACE_EVENT(ath10k_htt_stats, + TP_PROTO(void *buf, size_t buf_len), + + TP_ARGS(buf, buf_len), + + TP_STRUCT__entry( + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "len %zu", + __entry->buf_len + ) +); + +TRACE_EVENT(ath10k_wmi_dbglog, + TP_PROTO(void *buf, size_t buf_len), + + TP_ARGS(buf, buf_len), + + TP_STRUCT__entry( + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "len %zu", + __entry->buf_len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 68b6faefd1d..82669a77e55 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -44,40 +44,41 @@ out: spin_unlock_bh(&ar->data_lock); } -void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done) { struct device *dev = htt->ar->dev; struct ieee80211_tx_info *info; - struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag; - struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu; - int ret; + struct ath10k_skb_cb *skb_cb; + struct sk_buff *msdu; - if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0) - return; + lockdep_assert_held(&htt->tx_lock); - ATH10K_SKB_CB(txdesc)->htt.refcount--; + 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); - if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) + if (tx_done->msdu_id >= htt->max_num_pending_tx) { + ath10k_warn("warning: msdu_id %d too big, ignoring\n", + tx_done->msdu_id); return; + } - if (txfrag) { - ret = ath10k_skb_unmap(dev, txfrag); - if (ret) - ath10k_warn("txfrag unmap failed (%d)\n", ret); + msdu = htt->pending_tx[tx_done->msdu_id]; + skb_cb = ATH10K_SKB_CB(msdu); - dev_kfree_skb_any(txfrag); - } + dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - ret = ath10k_skb_unmap(dev, msdu); - if (ret) - ath10k_warn("data skb unmap failed (%d)\n", ret); + 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); info = IEEE80211_SKB_CB(msdu); memset(&info->status, 0, sizeof(info->status)); - if (ATH10K_SKB_CB(txdesc)->htt.discard) { + if (tx_done->discard) { ieee80211_free_txskb(htt->ar->hw, msdu); goto exit; } @@ -85,216 +86,18 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_ACK; - if (ATH10K_SKB_CB(txdesc)->htt.no_ack) + if (tx_done->no_ack) info->flags &= ~IEEE80211_TX_STAT_ACK; ieee80211_tx_status(htt->ar->hw, msdu); /* we do not own the msdu anymore */ exit: - spin_lock_bh(&htt->tx_lock); - htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL; - ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id); + 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 (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx)) + if (htt->num_pending_tx == 0) wake_up(&htt->empty_tx_wq); - spin_unlock_bh(&htt->tx_lock); - - dev_kfree_skb_any(txdesc); -} - -void ath10k_txrx_tx_completed(struct ath10k_htt *htt, - const struct htt_tx_done *tx_done) -{ - struct sk_buff *txdesc; - - 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); - - if (tx_done->msdu_id >= htt->max_num_pending_tx) { - ath10k_warn("warning: msdu_id %d too big, ignoring\n", - tx_done->msdu_id); - return; - } - - txdesc = htt->pending_tx[tx_done->msdu_id]; - - ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard; - ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack; - - ath10k_txrx_tx_unref(htt, txdesc); -} - -static const u8 rx_legacy_rate_idx[] = { - 3, /* 0x00 - 11Mbps */ - 2, /* 0x01 - 5.5Mbps */ - 1, /* 0x02 - 2Mbps */ - 0, /* 0x03 - 1Mbps */ - 3, /* 0x04 - 11Mbps */ - 2, /* 0x05 - 5.5Mbps */ - 1, /* 0x06 - 2Mbps */ - 0, /* 0x07 - 1Mbps */ - 10, /* 0x08 - 48Mbps */ - 8, /* 0x09 - 24Mbps */ - 6, /* 0x0A - 12Mbps */ - 4, /* 0x0B - 6Mbps */ - 11, /* 0x0C - 54Mbps */ - 9, /* 0x0D - 36Mbps */ - 7, /* 0x0E - 18Mbps */ - 5, /* 0x0F - 9Mbps */ -}; - -static void process_rx_rates(struct ath10k *ar, struct htt_rx_info *info, - enum ieee80211_band band, - struct ieee80211_rx_status *status) -{ - u8 cck, rate, rate_idx, bw, sgi, mcs, nss; - u8 info0 = info->rate.info0; - u32 info1 = info->rate.info1; - u32 info2 = info->rate.info2; - u8 preamble = 0; - - /* Check if valid fields */ - if (!(info0 & HTT_RX_INDICATION_INFO0_START_VALID)) - return; - - preamble = MS(info1, HTT_RX_INDICATION_INFO1_PREAMBLE_TYPE); - - switch (preamble) { - case HTT_RX_LEGACY: - cck = info0 & HTT_RX_INDICATION_INFO0_LEGACY_RATE_CCK; - rate = MS(info0, HTT_RX_INDICATION_INFO0_LEGACY_RATE); - rate_idx = 0; - - if (rate < 0x08 || rate > 0x0F) - break; - - switch (band) { - case IEEE80211_BAND_2GHZ: - if (cck) - rate &= ~BIT(3); - rate_idx = rx_legacy_rate_idx[rate]; - break; - case IEEE80211_BAND_5GHZ: - rate_idx = rx_legacy_rate_idx[rate]; - /* We are using same rate table registering - HW - ath10k_rates[]. In case of 5GHz skip - CCK rates, so -4 here */ - rate_idx -= 4; - break; - default: - break; - } - - status->rate_idx = rate_idx; - break; - case HTT_RX_HT: - case HTT_RX_HT_WITH_TXBF: - /* HT-SIG - Table 20-11 in info1 and info2 */ - mcs = info1 & 0x1F; - nss = mcs >> 3; - bw = (info1 >> 7) & 1; - sgi = (info2 >> 7) & 1; - - status->rate_idx = mcs; - status->flag |= RX_FLAG_HT; - if (sgi) - status->flag |= RX_FLAG_SHORT_GI; - if (bw) - status->flag |= RX_FLAG_40MHZ; - break; - case HTT_RX_VHT: - case HTT_RX_VHT_WITH_TXBF: - /* VHT-SIG-A1 in info 1, VHT-SIG-A2 in info2 - TODO check this */ - mcs = (info2 >> 4) & 0x0F; - nss = (info1 >> 10) & 0x07; - bw = info1 & 3; - sgi = info2 & 1; - - status->rate_idx = mcs; - status->vht_nss = nss; - - if (sgi) - status->flag |= RX_FLAG_SHORT_GI; - - switch (bw) { - /* 20MHZ */ - case 0: - break; - /* 40MHZ */ - case 1: - status->flag |= RX_FLAG_40MHZ; - break; - /* 80MHZ */ - case 2: - status->flag |= RX_FLAG_80MHZ; - } - - status->flag |= RX_FLAG_VHT; - break; - default: - break; - } -} - -void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) -{ - struct ieee80211_rx_status *status; - struct ieee80211_channel *ch; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)info->skb->data; - - status = IEEE80211_SKB_RXCB(info->skb); - memset(status, 0, sizeof(*status)); - - if (info->encrypt_type != HTT_RX_MPDU_ENCRYPT_NONE) { - status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | - RX_FLAG_MMIC_STRIPPED; - hdr->frame_control = __cpu_to_le16( - __le16_to_cpu(hdr->frame_control) & - ~IEEE80211_FCTL_PROTECTED); - } - - if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR) - status->flag |= RX_FLAG_MMIC_ERROR; - - if (info->fcs_err) - status->flag |= RX_FLAG_FAILED_FCS_CRC; - - status->signal = info->signal; - - spin_lock_bh(&ar->data_lock); - ch = ar->scan_channel; - if (!ch) - ch = ar->rx_channel; - spin_unlock_bh(&ar->data_lock); - - if (!ch) { - ath10k_warn("no channel configured; ignoring frame!\n"); - dev_kfree_skb_any(info->skb); - return; - } - - process_rx_rates(ar, info, ch->band, status); - status->band = ch->band; - status->freq = ch->center_freq; - - 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\n", - info->skb, - info->skb->len, - status->flag == 0 ? "legacy" : "", - status->flag & RX_FLAG_HT ? "ht" : "", - status->flag & RX_FLAG_VHT ? "vht" : "", - status->flag & RX_FLAG_40MHZ ? "40" : "", - status->flag & RX_FLAG_80MHZ ? "80" : "", - status->flag & RX_FLAG_SHORT_GI ? "sgi " : "", - status->rate_idx, - status->vht_nss, - status->freq, - status->band); - - ieee80211_rx(ar->hw, info->skb); } struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, @@ -397,7 +200,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/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h index e78632a76df..aee3e20058f 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.h +++ b/drivers/net/wireless/ath/ath10k/txrx.h @@ -19,10 +19,8 @@ #include "htt.h" -void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc); -void ath10k_txrx_tx_completed(struct ath10k_htt *htt, - const struct htt_tx_done *tx_done); -void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info); +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done); struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, const u8 *addr); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 55f90c76186..4b7782a529a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -16,6 +16,7 @@ */ #include <linux/skbuff.h> +#include <linux/ctype.h> #include "core.h" #include "htc.h" @@ -23,29 +24,468 @@ #include "wmi.h" #include "mac.h" -void ath10k_wmi_flush_tx(struct ath10k *ar) -{ - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - if (ar->state == ATH10K_STATE_WEDGED) { - ath10k_warn("wmi flush skipped - device is wedged anyway\n"); - return; - } - - ret = wait_event_timeout(ar->wmi.wq, - atomic_read(&ar->wmi.pending_tx_count) == 0, - 5*HZ); - if (atomic_read(&ar->wmi.pending_tx_count) == 0) - return; - - if (ret == 0) - ret = -ETIMEDOUT; - - if (ret < 0) - ath10k_warn("wmi flush failed (%d)\n", ret); -} +/* MAIN WMI cmd track */ +static struct wmi_cmd_map wmi_cmd_map = { + .init_cmdid = WMI_INIT_CMDID, + .start_scan_cmdid = WMI_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID, + .pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_BCN_TMPL_CMDID, + .bcn_filter_rx_cmdid = WMI_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_PRB_TMPL_CMDID, + .addba_clear_resp_cmdid = WMI_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_ROAM_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + .ap_ps_peer_param_cmdid = WMI_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + .peer_rate_retry_sched_cmdid = WMI_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_SET_ARP_NS_OFFLOAD_CMDID, + .network_list_offload_config_cmdid = + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID, + .gtk_offload_cmdid = WMI_GTK_OFFLOAD_CMDID, + .csa_offload_enable_cmdid = WMI_CSA_OFFLOAD_ENABLE_CMDID, + .csa_offload_chanswitch_cmdid = WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + .chatter_set_mode_cmdid = WMI_CHATTER_SET_MODE_CMDID, + .peer_tid_addba_cmdid = WMI_PEER_TID_ADDBA_CMDID, + .peer_tid_delba_cmdid = WMI_PEER_TID_DELBA_CMDID, + .sta_dtim_ps_method_cmdid = WMI_STA_DTIM_PS_METHOD_CMDID, + .sta_uapsd_auto_trig_cmdid = WMI_STA_UAPSD_AUTO_TRIG_CMDID, + .sta_keepalive_cmd = WMI_STA_KEEPALIVE_CMD, + .echo_cmdid = WMI_ECHO_CMDID, + .pdev_utf_cmdid = WMI_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_PDEV_FTM_INTG_CMDID, + .vdev_set_keepalive_cmdid = WMI_VDEV_SET_KEEPALIVE_CMDID, + .vdev_get_keepalive_cmdid = WMI_VDEV_GET_KEEPALIVE_CMDID, + .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID, + .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, +}; + +/* 10.X WMI cmd track */ +static struct wmi_cmd_map wmi_10x_cmd_map = { + .init_cmdid = WMI_10X_INIT_CMDID, + .start_scan_cmdid = WMI_10X_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_10X_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_10X_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_10X_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_10X_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_10X_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_10X_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_10X_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_10X_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_10X_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_10X_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_10X_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_10X_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_10X_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_10X_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_10X_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_10X_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_10X_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_10X_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_10X_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_10X_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_10X_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_10X_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_10X_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_10X_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_10X_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_10X_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_10X_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_10X_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_10X_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_10X_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_10X_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_10X_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_10X_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_10X_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_10X_OFL_SCAN_ADD_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_10X_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_10X_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_10X_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, + .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_10X_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_10X_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_10X_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_10X_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_10X_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_10X_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_10X_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = + WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED, + .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED, + .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED, + .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED, + .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED, + .echo_cmdid = WMI_10X_ECHO_CMDID, + .pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, + .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, +}; + +/* MAIN WMI VDEV param map */ +static struct wmi_vdev_param_map wmi_vdev_param_map = { + .rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_VDEV_PARAM_WDS, + .atim_window = WMI_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + .bmiss_final_bcnt = WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + .feature_wmm = WMI_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_VDEV_PARAM_SGI, + .ldpc = WMI_VDEV_PARAM_LDPC, + .tx_stbc = WMI_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_VDEV_PARAM_DEF_KEYID, + .nss = WMI_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_VDEV_PARAM_UNSUPPORTED, + .enable_rtscts = WMI_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_TXBF, + .packet_powersave = WMI_VDEV_PARAM_PACKET_POWERSAVE, + .drop_unencry = WMI_VDEV_PARAM_DROP_UNENCRY, + .tx_encap_type = WMI_VDEV_PARAM_TX_ENCAP_TYPE, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_VDEV_PARAM_UNSUPPORTED, +}; + +/* 10.X WMI VDEV param map */ +static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { + .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_10X_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_10X_VDEV_PARAM_WDS, + .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_10X_VDEV_PARAM_SGI, + .ldpc = WMI_10X_VDEV_PARAM_LDPC, + .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID, + .nss = WMI_10X_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_UNSUPPORTED, + .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED, + .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED, + .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + +static struct wmi_pdev_param_map wmi_pdev_param_map = { + .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + .pdev_stats_update_period = WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + .dcs = WMI_PDEV_PARAM_DCS, + .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_PROXY_STA, + .idle_ps_config = WMI_PDEV_PARAM_IDLE_PS_CONFIG, + .power_gating_sleep = WMI_PDEV_PARAM_POWER_GATING_SLEEP, + .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED, + .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED, + .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED, +}; + +static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { + .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED, + .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + .dcs = WMI_10X_PDEV_PARAM_DCS, + .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED, + .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED, + .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED, + .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, +}; int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { @@ -85,18 +525,14 @@ static struct sk_buff *ath10k_wmi_alloc_skb(u32 len) static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) { dev_kfree_skb(skb); - - if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0) - wake_up(&ar->wmi.wq); } -/* WMI command API */ -static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, - enum wmi_cmd_id cmd_id) +static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct wmi_cmd_hdr *cmd_hdr; - int status; + int ret; u32 cmd = 0; if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) @@ -107,25 +543,151 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, cmd_hdr = (struct wmi_cmd_hdr *)skb->data; cmd_hdr->cmd_id = __cpu_to_le32(cmd); - if (atomic_add_return(1, &ar->wmi.pending_tx_count) > - WMI_MAX_PENDING_TX_COUNT) { - /* avoid using up memory when FW hangs */ - atomic_dec(&ar->wmi.pending_tx_count); - return -EBUSY; + memset(skb_cb, 0, sizeof(*skb_cb)); + ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); + trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret); + + if (ret) + goto err_pull; + + return 0; + +err_pull: + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + return ret; +} + +static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) +{ + int ret; + + lockdep_assert_held(&arvif->ar->data_lock); + + if (arvif->beacon == NULL) + return; + + if (arvif->beacon_sent) + return; + + ret = ath10k_wmi_beacon_send_ref_nowait(arvif); + if (ret) + return; + + /* We need to retain the arvif->beacon reference for DMA unmapping and + * freeing the skbuff later. */ + arvif->beacon_sent = true; +} + +static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + ath10k_wmi_tx_beacon_nowait(arvif); +} + +static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar) +{ + spin_lock_bh(&ar->data_lock); + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_wmi_tx_beacons_iter, + NULL); + spin_unlock_bh(&ar->data_lock); +} + +static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar) +{ + /* try to send pending beacons first. they take priority */ + ath10k_wmi_tx_beacons_nowait(ar); + + wake_up(&ar->wmi.tx_credits_wq); +} + +static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + + if (cmd_id == WMI_CMD_UNSUPPORTED) { + ath10k_warn("wmi command %d is not supported by firmware\n", + cmd_id); + return ret; } - memset(skb_cb, 0, sizeof(*skb_cb)); + wait_event_timeout(ar->wmi.tx_credits_wq, ({ + /* try to send pending beacons first. they take priority */ + ath10k_wmi_tx_beacons_nowait(ar); - trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); + ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); + (ret != -EAGAIN); + }), 3*HZ); - status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); - if (status) { + if (ret) dev_kfree_skb_any(skb); - atomic_dec(&ar->wmi.pending_tx_count); - return status; + + return ret; +} + +int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) +{ + int ret = 0; + struct wmi_mgmt_tx_cmd *cmd; + struct ieee80211_hdr *hdr; + struct sk_buff *wmi_skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int len; + u32 buf_len = skb->len; + u16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control))) + return -EINVAL; + + len = sizeof(cmd->hdr) + skb->len; + + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) { + len += IEEE80211_CCMP_MIC_LEN; + buf_len += IEEE80211_CCMP_MIC_LEN; } - return 0; + len = round_up(len, 4); + + wmi_skb = ath10k_wmi_alloc_skb(len); + if (!wmi_skb) + return -ENOMEM; + + cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data; + + cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); + cmd->hdr.tx_rate = 0; + cmd->hdr.tx_power = 0; + cmd->hdr.buf_len = __cpu_to_le32(buf_len); + + memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(cmd->buf, skb->data, skb->len); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", + wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE, + fc & IEEE80211_FCTL_STYPE); + + /* Send the management frame buffer to the target */ + ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid); + if (ret) + return ret; + + /* TODO: report tx status to mac80211 - temporary just ACK */ + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(ar->hw, skb); + + return ret; } static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) @@ -315,8 +877,11 @@ static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band) static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data; + struct wmi_mgmt_rx_event_v1 *ev_v1; + struct wmi_mgmt_rx_event_v2 *ev_v2; + struct wmi_mgmt_rx_hdr_v1 *ev_hdr; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_channel *ch; struct ieee80211_hdr *hdr; u32 rx_status; u32 channel; @@ -325,19 +890,35 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rate; u32 buf_len; u16 fc; + int pull_len; + + if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) { + ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data; + ev_hdr = &ev_v2->hdr.v1; + pull_len = sizeof(*ev_v2); + } else { + ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data; + ev_hdr = &ev_v1->hdr; + pull_len = sizeof(*ev_v1); + } - channel = __le32_to_cpu(event->hdr.channel); - buf_len = __le32_to_cpu(event->hdr.buf_len); - rx_status = __le32_to_cpu(event->hdr.status); - snr = __le32_to_cpu(event->hdr.snr); - phy_mode = __le32_to_cpu(event->hdr.phy_mode); - rate = __le32_to_cpu(event->hdr.rate); + channel = __le32_to_cpu(ev_hdr->channel); + buf_len = __le32_to_cpu(ev_hdr->buf_len); + rx_status = __le32_to_cpu(ev_hdr->status); + snr = __le32_to_cpu(ev_hdr->snr); + phy_mode = __le32_to_cpu(ev_hdr->phy_mode); + rate = __le32_to_cpu(ev_hdr->rate); memset(status, 0, sizeof(*status)); ath10k_dbg(ATH10K_DBG_MGMT, "event mgmt rx status %08x\n", rx_status); + if (test_bit(ATH10K_CAC_RUNNING, &ar->dev_flags)) { + dev_kfree_skb(skb); + return 0; + } + if (rx_status & WMI_RX_STATUS_ERR_DECRYPT) { dev_kfree_skb(skb); return 0; @@ -353,21 +934,49 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) if (rx_status & WMI_RX_STATUS_ERR_MIC) status->flag |= RX_FLAG_MMIC_ERROR; - status->band = phy_mode_to_band(phy_mode); + /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to + * MODE_11B. This means phy_mode is not a reliable source for the band + * of mgmt rx. */ + + ch = ar->scan_channel; + if (!ch) + ch = ar->rx_channel; + + if (ch) { + status->band = ch->band; + + if (phy_mode == MODE_11B && + status->band == IEEE80211_BAND_5GHZ) + ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n"); + } else { + ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n"); + status->band = phy_mode_to_band(phy_mode); + } + status->freq = ieee80211_channel_to_frequency(channel, status->band); status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; status->rate_idx = get_rate_idx(rate, status->band); - skb_pull(skb, sizeof(event->hdr)); + skb_pull(skb, pull_len); hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); - if (fc & IEEE80211_FCTL_PROTECTED) { - status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED | - RX_FLAG_MMIC_STRIPPED; - hdr->frame_control = __cpu_to_le16(fc & + /* FW delivers WEP Shared Auth frame with Protected Bit set and + * encrypted payload. However in case of PMF it delivers decrypted + * frames with Protected Bit set. */ + if (ieee80211_has_protected(hdr->frame_control) && + !ieee80211_is_auth(hdr->frame_control)) { + status->flag |= RX_FLAG_DECRYPTED; + + if (!ieee80211_is_action(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control)) { + status->flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; + hdr->frame_control = __cpu_to_le16(fc & ~IEEE80211_FCTL_PROTECTED); + } } ath10k_dbg(ATH10K_DBG_MGMT, @@ -473,9 +1082,14 @@ static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); } -static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n"); + ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", + skb->len); + + trace_ath10k_wmi_dbglog(skb->data, skb->len); + + return 0; } static void ath10k_wmi_event_update_stats(struct ath10k *ar, @@ -513,7 +1127,27 @@ static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PEER_STA_KICKOUT_EVENTID\n"); + struct wmi_peer_sta_kickout_event *ev; + struct ieee80211_sta *sta; + + ev = (struct wmi_peer_sta_kickout_event *)skb->data; + + ath10k_dbg(ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n", + ev->peer_macaddr.addr); + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL); + if (!sta) { + ath10k_warn("Spurious quick kickout for STA %pM\n", + ev->peer_macaddr.addr); + goto exit; + } + + ieee80211_report_low_ack(sta, 10); + +exit: + rcu_read_unlock(); } /* @@ -614,6 +1248,13 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast); memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); + if (tim->dtim_count == 0) { + ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true; + + if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1) + ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true; + } + ath10k_dbg(ATH10K_DBG_MGMT, "dtim %d/%d mcast %d pvmlen %d\n", tim->dtim_count, tim->dtim_period, tim->bitmap_ctrl, pvm_len); @@ -734,18 +1375,13 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) int i = -1; struct wmi_bcn_info *bcn_info; struct ath10k_vif *arvif; - struct wmi_bcn_tx_arg arg; struct sk_buff *bcn; - int vdev_id = 0; - int ret; - - ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n"); + int ret, vdev_id = 0; ev = (struct wmi_host_swba_event *)skb->data; map = __le32_to_cpu(ev->vdev_map); - ath10k_dbg(ATH10K_DBG_MGMT, "host swba:\n" - "-vdev map 0x%x\n", + ath10k_dbg(ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n", ev->vdev_map); for (; map; map >>= 1, vdev_id++) { @@ -762,12 +1398,7 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) bcn_info = &ev->bcn_info[i]; ath10k_dbg(ATH10K_DBG_MGMT, - "-bcn_info[%d]:\n" - "--tim_len %d\n" - "--tim_mcast %d\n" - "--tim_changed %d\n" - "--tim_num_ps_pending %d\n" - "--tim_bitmap 0x%08x%08x%08x%08x\n", + "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n", i, __le32_to_cpu(bcn_info->tim_info.tim_len), __le32_to_cpu(bcn_info->tim_info.tim_mcast), @@ -784,6 +1415,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) continue; } + /* There are no completions for beacons so wait for next SWBA + * before telling mac80211 to decrement CSA counter + * + * Once CSA counter is completed stop sending beacons until + * actual channel switch is done */ + if (arvif->vif->csa_active && + ieee80211_csa_is_complete(arvif->vif)) { + ieee80211_csa_finish(arvif->vif); + continue; + } + bcn = ieee80211_beacon_get(ar->hw, arvif->vif); if (!bcn) { ath10k_warn("could not get mac80211 beacon\n"); @@ -794,17 +1436,37 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); - arg.vdev_id = arvif->vdev_id; - arg.tx_rate = 0; - arg.tx_power = 0; - arg.bcn = bcn->data; - arg.bcn_len = bcn->len; + spin_lock_bh(&ar->data_lock); - ret = ath10k_wmi_beacon_send(ar, &arg); - if (ret) - ath10k_warn("could not send beacon (%d)\n", ret); + if (arvif->beacon) { + if (!arvif->beacon_sent) + ath10k_warn("SWBA overrun on vdev %d\n", + arvif->vdev_id); - dev_kfree_skb_any(bcn); + 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; + } + + 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); + dev_kfree_skb_any(bcn); + goto skip; + } + + arvif->beacon = bcn; + arvif->beacon_sent = false; + + ath10k_wmi_tx_beacon_nowait(arvif); +skip: + spin_unlock_bh(&ar->data_lock); } } @@ -814,9 +1476,259 @@ static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); } +static void ath10k_dfs_radar_report(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + struct phyerr_radar_report *rr, + u64 tsf) +{ + u32 reg0, reg1, tsf32l; + struct pulse_event pe; + u64 tsf64; + u8 rssi, width; + + reg0 = __le32_to_cpu(rr->reg0); + reg1 = __le32_to_cpu(rr->reg1); + + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi phyerr radar report chirp %d max_width %d agc_total_gain %d pulse_delta_diff %d\n", + MS(reg0, RADAR_REPORT_REG0_PULSE_IS_CHIRP), + MS(reg0, RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH), + MS(reg0, RADAR_REPORT_REG0_AGC_TOTAL_GAIN), + MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_DIFF)); + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi phyerr radar report pulse_delta_pean %d pulse_sidx %d fft_valid %d agc_mb_gain %d subchan_mask %d\n", + MS(reg0, RADAR_REPORT_REG0_PULSE_DELTA_PEAK), + MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX), + MS(reg1, RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID), + MS(reg1, RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN), + MS(reg1, RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK)); + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi phyerr radar report pulse_tsf_offset 0x%X pulse_dur: %d\n", + MS(reg1, RADAR_REPORT_REG1_PULSE_TSF_OFFSET), + MS(reg1, RADAR_REPORT_REG1_PULSE_DUR)); + + if (!ar->dfs_detector) + return; + + /* report event to DFS pattern detector */ + tsf32l = __le32_to_cpu(event->hdr.tsf_timestamp); + tsf64 = tsf & (~0xFFFFFFFFULL); + tsf64 |= tsf32l; + + width = MS(reg1, RADAR_REPORT_REG1_PULSE_DUR); + rssi = event->hdr.rssi_combined; + + /* hardware store this as 8 bit signed value, + * set to zero if negative number + */ + if (rssi & 0x80) + rssi = 0; + + pe.ts = tsf64; + pe.freq = ar->hw->conf.chandef.chan->center_freq; + pe.width = width; + pe.rssi = rssi; + + ath10k_dbg(ATH10K_DBG_REGULATORY, + "dfs add pulse freq: %d, width: %d, rssi %d, tsf: %llX\n", + pe.freq, pe.width, pe.rssi, pe.ts); + + ATH10K_DFS_STAT_INC(ar, pulses_detected); + + if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) { + ath10k_dbg(ATH10K_DBG_REGULATORY, + "dfs no pulse pattern detected, yet\n"); + return; + } + + ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs radar detected\n"); + ATH10K_DFS_STAT_INC(ar, radar_detected); + + /* Control radar events reporting in debugfs file + dfs_block_radar_events */ + if (ar->dfs_block_radar_events) { + ath10k_info("DFS Radar detected, but ignored as requested\n"); + return; + } + + ieee80211_radar_detected(ar->hw); +} + +static int ath10k_dfs_fft_report(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + struct phyerr_fft_report *fftr, + u64 tsf) +{ + u32 reg0, reg1; + u8 rssi, peak_mag; + + reg0 = __le32_to_cpu(fftr->reg0); + reg1 = __le32_to_cpu(fftr->reg1); + rssi = event->hdr.rssi_combined; + + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi phyerr fft report total_gain_db %d base_pwr_db %d fft_chn_idx %d peak_sidx %d\n", + MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB), + MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB), + MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX), + MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX)); + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi phyerr fft report rel_pwr_db %d avgpwr_db %d peak_mag %d num_store_bin %d\n", + MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB), + MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB), + MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG), + MS(reg1, SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB)); + + peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG); + + /* false event detection */ + if (rssi == DFS_RSSI_POSSIBLY_FALSE && + peak_mag < 2 * DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE) { + ath10k_dbg(ATH10K_DBG_REGULATORY, "dfs false pulse detected\n"); + ATH10K_DFS_STAT_INC(ar, pulses_discarded); + return -EINVAL; + } + + return 0; +} + +static void ath10k_wmi_event_dfs(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + u64 tsf) +{ + int buf_len, tlv_len, res, i = 0; + struct phyerr_tlv *tlv; + struct phyerr_radar_report *rr; + struct phyerr_fft_report *fftr; + u8 *tlv_buf; + + buf_len = __le32_to_cpu(event->hdr.buf_len); + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi event dfs err_code %d rssi %d tsfl 0x%X tsf64 0x%llX len %d\n", + event->hdr.phy_err_code, event->hdr.rssi_combined, + __le32_to_cpu(event->hdr.tsf_timestamp), tsf, buf_len); + + /* Skip event if DFS disabled */ + if (!config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) + return; + + ATH10K_DFS_STAT_INC(ar, pulses_total); + + while (i < buf_len) { + if (i + sizeof(*tlv) > buf_len) { + ath10k_warn("too short buf for tlv header (%d)\n", i); + return; + } + + tlv = (struct phyerr_tlv *)&event->bufp[i]; + tlv_len = __le16_to_cpu(tlv->len); + tlv_buf = &event->bufp[i + sizeof(*tlv)]; + ath10k_dbg(ATH10K_DBG_REGULATORY, + "wmi event dfs tlv_len %d tlv_tag 0x%02X tlv_sig 0x%02X\n", + tlv_len, tlv->tag, tlv->sig); + + switch (tlv->tag) { + case PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY: + if (i + sizeof(*tlv) + sizeof(*rr) > buf_len) { + ath10k_warn("too short radar pulse summary (%d)\n", + i); + return; + } + + rr = (struct phyerr_radar_report *)tlv_buf; + ath10k_dfs_radar_report(ar, event, rr, tsf); + break; + case PHYERR_TLV_TAG_SEARCH_FFT_REPORT: + if (i + sizeof(*tlv) + sizeof(*fftr) > buf_len) { + ath10k_warn("too short fft report (%d)\n", i); + return; + } + + fftr = (struct phyerr_fft_report *)tlv_buf; + res = ath10k_dfs_fft_report(ar, event, fftr, tsf); + if (res) + return; + break; + } + + i += sizeof(*tlv) + tlv_len; + } +} + +static void ath10k_wmi_event_spectral_scan(struct ath10k *ar, + struct wmi_single_phyerr_rx_event *event, + u64 tsf) +{ + ath10k_dbg(ATH10K_DBG_WMI, "wmi event spectral scan\n"); +} + static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_PHYERR_EVENTID\n"); + struct wmi_comb_phyerr_rx_event *comb_event; + struct wmi_single_phyerr_rx_event *event; + u32 count, i, buf_len, phy_err_code; + u64 tsf; + int left_len = skb->len; + + ATH10K_DFS_STAT_INC(ar, phy_errors); + + /* Check if combined event available */ + if (left_len < sizeof(*comb_event)) { + ath10k_warn("wmi phyerr combined event wrong len\n"); + return; + } + + left_len -= sizeof(*comb_event); + + /* Check number of included events */ + comb_event = (struct wmi_comb_phyerr_rx_event *)skb->data; + count = __le32_to_cpu(comb_event->hdr.num_phyerr_events); + + tsf = __le32_to_cpu(comb_event->hdr.tsf_u32); + tsf <<= 32; + tsf |= __le32_to_cpu(comb_event->hdr.tsf_l32); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event phyerr count %d tsf64 0x%llX\n", + count, tsf); + + event = (struct wmi_single_phyerr_rx_event *)comb_event->bufp; + for (i = 0; i < count; i++) { + /* Check if we can read event header */ + if (left_len < sizeof(*event)) { + ath10k_warn("single event (%d) wrong head len\n", i); + return; + } + + left_len -= sizeof(*event); + + buf_len = __le32_to_cpu(event->hdr.buf_len); + phy_err_code = event->hdr.phy_err_code; + + if (left_len < buf_len) { + ath10k_warn("single event (%d) wrong buf len\n", i); + return; + } + + left_len -= buf_len; + + switch (phy_err_code) { + case PHY_ERROR_RADAR: + ath10k_wmi_event_dfs(ar, event, tsf); + break; + case PHY_ERROR_SPECTRAL_SCAN: + ath10k_wmi_event_spectral_scan(ar, event, tsf); + break; + case PHY_ERROR_FALSE_RADAR_EXT: + ath10k_wmi_event_dfs(ar, event, tsf); + ath10k_wmi_event_spectral_scan(ar, event, tsf); + break; + default: + break; + } + + event += sizeof(*event) + buf_len; + } } static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) @@ -831,9 +1743,37 @@ static void ath10k_wmi_event_profile_match(struct ath10k *ar, } static void ath10k_wmi_event_debug_print(struct ath10k *ar, - struct sk_buff *skb) + struct sk_buff *skb) { - ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n"); + char buf[101], c; + int i; + + for (i = 0; i < sizeof(buf) - 1; i++) { + if (i >= skb->len) + break; + + c = skb->data[i]; + + if (c == '\0') + break; + + if (isascii(c) && isprint(c)) + buf[i] = c; + else + buf[i] = '.'; + } + + if (i == sizeof(buf) - 1) + ath10k_warn("wmi debug print truncated: %d\n", skb->len); + + /* for some reason the debug prints end with \n, remove that */ + if (skb->data[i - 1] == '\n') + i--; + + /* the last byte is always reserved for the null character */ + buf[i] = '\0'; + + ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf); } static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) @@ -919,6 +1859,55 @@ static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); } +static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); +} + +static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) +{ + dma_addr_t paddr; + u32 pool_size; + int idx = ar->wmi.num_mem_chunks; + + pool_size = num_units * round_up(unit_len, 4); + + if (!pool_size) + return -EINVAL; + + ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev, + pool_size, + &paddr, + GFP_ATOMIC); + if (!ar->wmi.mem_chunks[idx].vaddr) { + ath10k_warn("failed to allocate memory chunk\n"); + return -ENOMEM; + } + + memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size); + + ar->wmi.mem_chunks[idx].paddr = paddr; + ar->wmi.mem_chunks[idx].len = pool_size; + ar->wmi.mem_chunks[idx].req_id = req_id; + ar->wmi.num_mem_chunks++; + + return 0; +} + static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { @@ -943,6 +1932,10 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, ar->phy_capability = __le32_to_cpu(ev->phy_capability); ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); + /* only manually set fw features when not using FW IE format */ + if (ar->fw_api == 1 && ar->fw_version_build > 636) + set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features); + if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); @@ -987,6 +1980,108 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, complete(&ar->wmi.service_ready); } +static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, + struct sk_buff *skb) +{ + u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; + int ret; + struct wmi_service_ready_event_10x *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) { + ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + skb->len, sizeof(*ev)); + return; + } + + ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power); + ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power); + ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info); + ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info); + ar->fw_version_major = + (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24; + ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff); + ar->phy_capability = __le32_to_cpu(ev->phy_capability); + ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); + + if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { + ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", + ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); + ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM; + } + + ar->ath_common.regulatory.current_rd = + __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); + + ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, + sizeof(ev->wmi_service_bitmap)); + + if (strlen(ar->hw->wiphy->fw_version) == 0) { + snprintf(ar->hw->wiphy->fw_version, + sizeof(ar->hw->wiphy->fw_version), + "%u.%u", + ar->fw_version_major, + ar->fw_version_minor); + } + + num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs); + + if (num_mem_reqs > ATH10K_MAX_MEM_REQS) { + ath10k_warn("requested memory chunks number (%d) exceeds the limit\n", + num_mem_reqs); + return; + } + + if (!num_mem_reqs) + goto exit; + + ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n", + num_mem_reqs); + + for (i = 0; i < num_mem_reqs; ++i) { + req_id = __le32_to_cpu(ev->mem_reqs[i].req_id); + num_units = __le32_to_cpu(ev->mem_reqs[i].num_units); + unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size); + num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info); + + if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) + /* number of units to allocate is number of + * peers, 1 extra for self peer on target */ + /* this needs to be tied, host and target + * can get out of sync */ + num_units = TARGET_10X_NUM_PEERS + 1; + else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) + num_units = TARGET_10X_NUM_VDEVS + 1; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n", + req_id, + __le32_to_cpu(ev->mem_reqs[i].num_units), + num_unit_info, + unit_size, + num_units); + + ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units, + unit_size); + if (ret) + return; + } + +exit: + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->abi_version), + __le32_to_cpu(ev->phy_capability), + __le32_to_cpu(ev->ht_cap_info), + __le32_to_cpu(ev->vht_cap_info), + __le32_to_cpu(ev->vht_supp_mcs), + __le32_to_cpu(ev->sys_cap_info), + __le32_to_cpu(ev->num_mem_reqs), + __le32_to_cpu(ev->num_rf_chains)); + + complete(&ar->wmi.service_ready); +} + static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; @@ -997,17 +2092,17 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) memcpy(ar->mac_addr, ev->mac_addr.addr, ETH_ALEN); ath10k_dbg(ATH10K_DBG_WMI, - "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n", + "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n", __le32_to_cpu(ev->sw_version), __le32_to_cpu(ev->abi_version), ev->mac_addr.addr, - __le32_to_cpu(ev->status)); + __le32_to_cpu(ev->status), skb->len, sizeof(*ev)); complete(&ar->wmi.unified_ready); return 0; } -static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_event_id id; @@ -1126,67 +2221,161 @@ static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } -static void ath10k_wmi_event_work(struct work_struct *work) +static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) { - struct ath10k *ar = container_of(work, struct ath10k, - wmi.wmi_event_work); - struct sk_buff *skb; + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_10x_event_id id; + u16 len; - for (;;) { - skb = skb_dequeue(&ar->wmi.wmi_event_list); - if (!skb) - break; + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); - ath10k_wmi_event_process(ar, skb); - } -} + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; -static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) -{ - struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data; - enum wmi_event_id event_id; + len = skb->len; - event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + trace_ath10k_wmi_event(id, skb->data, skb->len); - /* some events require to be handled ASAP - * thus can't be defered to a worker thread */ - switch (event_id) { - case WMI_HOST_SWBA_EVENTID: - case WMI_MGMT_RX_EVENTID: - ath10k_wmi_event_process(ar, skb); + switch (id) { + case WMI_10X_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ return; + case WMI_10X_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_10X_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_10X_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_10X_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_10X_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_10X_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_10X_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_10X_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_10X_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_10X_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_10X_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_10X_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_10X_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_10X_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_10X_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_10X_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_10X_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_10X_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_10X_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_10X_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_10X_INST_RSSI_STATS_EVENTID: + ath10k_wmi_event_inst_rssi_stats(ar, skb); + break; + case WMI_10X_VDEV_STANDBY_REQ_EVENTID: + ath10k_wmi_event_vdev_standby_req(ar, skb); + break; + case WMI_10X_VDEV_RESUME_REQ_EVENTID: + ath10k_wmi_event_vdev_resume_req(ar, skb); + break; + case WMI_10X_SERVICE_READY_EVENTID: + ath10k_wmi_10x_service_ready_event_rx(ar, skb); + break; + case WMI_10X_READY_EVENTID: + ath10k_wmi_ready_event_rx(ar, skb); + break; default: + ath10k_warn("Unknown eventid: %d\n", id); break; } - skb_queue_tail(&ar->wmi.wmi_event_list, skb); - queue_work(ar->workqueue, &ar->wmi.wmi_event_work); + dev_kfree_skb(skb); +} + + +static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ath10k_wmi_10x_process_rx(ar, skb); + else + ath10k_wmi_main_process_rx(ar, skb); } /* WMI Initialization functions */ int ath10k_wmi_attach(struct ath10k *ar) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ar->wmi.cmd = &wmi_10x_cmd_map; + ar->wmi.vdev_param = &wmi_10x_vdev_param_map; + ar->wmi.pdev_param = &wmi_10x_pdev_param_map; + } else { + ar->wmi.cmd = &wmi_cmd_map; + ar->wmi.vdev_param = &wmi_vdev_param_map; + ar->wmi.pdev_param = &wmi_pdev_param_map; + } + init_completion(&ar->wmi.service_ready); init_completion(&ar->wmi.unified_ready); - init_waitqueue_head(&ar->wmi.wq); - - skb_queue_head_init(&ar->wmi.wmi_event_list); - INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work); + init_waitqueue_head(&ar->wmi.tx_credits_wq); return 0; } void ath10k_wmi_detach(struct ath10k *ar) { - /* HTC should've drained the packets already */ - if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0)) - ath10k_warn("there are still pending packets\n"); + int i; + + /* free the host memory chunks requested by firmware */ + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + dma_free_coherent(ar->dev, + ar->wmi.mem_chunks[i].len, + ar->wmi.mem_chunks[i].vaddr, + ar->wmi.mem_chunks[i].paddr); + } - cancel_work_sync(&ar->wmi.wmi_event_work); - skb_queue_purge(&ar->wmi.wmi_event_list); + ar->wmi.num_mem_chunks = 0; } -int ath10k_wmi_connect_htc_service(struct ath10k *ar) +int ath10k_wmi_connect(struct ath10k *ar) { int status; struct ath10k_htc_svc_conn_req conn_req; @@ -1198,6 +2387,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) /* these fields are the same for all service endpoints */ conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete; conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx; + conn_req.ep_ops.ep_tx_credits = ath10k_wmi_op_ep_tx_credits; /* connect to control service */ conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; @@ -1213,8 +2403,9 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) return 0; } -int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, - u16 rd5g, u16 ctl2g, u16 ctl5g) +static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, + u16 rd2g, u16 rd5g, u16 ctl2g, + u16 ctl5g) { struct wmi_pdev_set_regdomain_cmd *cmd; struct sk_buff *skb; @@ -1234,7 +2425,48 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", rd, rd2g, rd5g, ctl2g, ctl5g); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_regdomain_cmdid); +} + +static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, + u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) +{ + struct wmi_pdev_set_regdomain_cmd_10x *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data; + cmd->reg_domain = __cpu_to_le32(rd); + cmd->reg_domain_2G = __cpu_to_le32(rd2g); + cmd->reg_domain_5G = __cpu_to_le32(rd5g); + cmd->conformance_test_limit_2G = __cpu_to_le32(ctl2g); + cmd->conformance_test_limit_5G = __cpu_to_le32(ctl5g); + cmd->dfs_domain = __cpu_to_le32(dfs_reg); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n", + rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_regdomain_cmdid); +} + +int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) +{ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g, + ctl2g, ctl5g, dfs_reg); + else + return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g, + ctl2g, ctl5g); } int ath10k_wmi_pdev_set_channel(struct ath10k *ar, @@ -1242,6 +2474,7 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, { struct wmi_set_channel_cmd *cmd; struct sk_buff *skb; + u32 ch_flags = 0; if (arg->passive) return -EINVAL; @@ -1250,10 +2483,14 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, if (!skb) return -ENOMEM; + if (arg->chan_radar) + ch_flags |= WMI_CHAN_FLAG_DFS; + cmd = (struct wmi_set_channel_cmd *)skb->data; cmd->chan.mhz = __cpu_to_le32(arg->freq); cmd->chan.band_center_freq1 = __cpu_to_le32(arg->freq); cmd->chan.mode = arg->mode; + cmd->chan.flags |= __cpu_to_le32(ch_flags); cmd->chan.min_power = arg->min_power; cmd->chan.max_power = arg->max_power; cmd->chan.reg_power = arg->max_reg_power; @@ -1264,10 +2501,11 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, "wmi set channel mode %d freq %d\n", arg->mode, arg->freq); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_channel_cmdid); } -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) { struct wmi_pdev_suspend_cmd *cmd; struct sk_buff *skb; @@ -1277,9 +2515,9 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) return -ENOMEM; cmd = (struct wmi_pdev_suspend_cmd *)skb->data; - cmd->suspend_opt = WMI_PDEV_SUSPEND; + cmd->suspend_opt = __cpu_to_le32(suspend_opt); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); } int ath10k_wmi_pdev_resume_target(struct ath10k *ar) @@ -1290,15 +2528,19 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar) if (skb == NULL) return -ENOMEM; - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); } -int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, - u32 value) +int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) { struct wmi_pdev_set_param_cmd *cmd; struct sk_buff *skb; + if (id == WMI_PDEV_PARAM_UNSUPPORTED) { + ath10k_warn("pdev param %d not supported by firmware\n", id); + return -EOPNOTSUPP; + } + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1309,15 +2551,16 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", id, value); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); } -int ath10k_wmi_cmd_init(struct ath10k *ar) +static int ath10k_wmi_main_cmd_init(struct ath10k *ar) { struct wmi_init_cmd *cmd; struct sk_buff *buf; struct wmi_resource_config config = {}; - u32 val; + u32 len, val; + int i; config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); @@ -1370,23 +2613,158 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); - buf = ath10k_wmi_alloc_skb(sizeof(*cmd)); + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(len); if (!buf) return -ENOMEM; cmd = (struct wmi_init_cmd *)buf->data; - cmd->num_host_mem_chunks = 0; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + ar->wmi.num_mem_chunks); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%llx\n", + i, + ar->wmi.mem_chunks[i].len, + (unsigned long long)ar->wmi.mem_chunks[i].paddr); + } +out: memcpy(&cmd->resource_config, &config, sizeof(config)); ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); - return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); +} + +static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) +{ + struct wmi_init_cmd_10x *cmd; + struct sk_buff *buf; + struct wmi_resource_config_10x config = {}; + u32 len, val; + int i; + + config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); + config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE); + + config.scan_max_pending_reqs = + __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS); + + config.bmiss_offload_max_vdev = + __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_vdev = + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_ap_profiles = + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES); + + config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS); + config.num_mcast_table_elems = + __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS); + + config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM); + + val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); + + config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG); + + config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES); + + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(len); + if (!buf) + return -ENOMEM; + + cmd = (struct wmi_init_cmd_10x *)buf->data; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + ar->wmi.num_mem_chunks); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%llx\n", + i, + ar->wmi.mem_chunks[i].len, + (unsigned long long)ar->wmi.mem_chunks[i].paddr); + } +out: + memcpy(&cmd->resource_config, &config, sizeof(config)); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n"); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); } -static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) +int ath10k_wmi_cmd_init(struct ath10k *ar) +{ + int ret; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ret = ath10k_wmi_10x_cmd_init(ar); + else + ret = ath10k_wmi_main_cmd_init(ar); + + return ret; +} + +static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) { int len; - len = sizeof(struct wmi_start_scan_cmd); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + len = sizeof(struct wmi_start_scan_cmd_10x); + else + len = sizeof(struct wmi_start_scan_cmd); if (arg->ie_len) { if (!arg->ie) @@ -1446,7 +2824,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, int len = 0; int i; - len = ath10k_wmi_start_scan_calc_len(arg); + len = ath10k_wmi_start_scan_calc_len(ar, arg); if (len < 0) return len; /* len contains error code here */ @@ -1478,7 +2856,14 @@ int ath10k_wmi_start_scan(struct ath10k *ar, cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags); /* TLV list starts after fields included in the struct */ - off = sizeof(*cmd); + /* There's just one filed that differes the two start_scan + * structures - burst_duration, which we are not using btw, + no point to make the split here, just shift the buffer to fit with + given FW */ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + off = sizeof(struct wmi_start_scan_cmd_10x); + else + off = sizeof(struct wmi_start_scan_cmd); if (arg->n_channels) { channels = (void *)skb->data + off; @@ -1540,7 +2925,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, } ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n"); - return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); } void ath10k_wmi_start_scan_init(struct ath10k *ar, @@ -1556,7 +2941,7 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar, arg->repeat_probe_time = 0; arg->probe_spacing_time = 0; arg->idle_time = 0; - arg->max_scan_time = 5000; + arg->max_scan_time = 20000; arg->probe_delay = 5; arg->notify_scan_events = WMI_SCAN_EVENT_STARTED | WMI_SCAN_EVENT_COMPLETED @@ -1600,7 +2985,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) ath10k_dbg(ATH10K_DBG_WMI, "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", arg->req_id, arg->req_type, arg->u.scan_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); } int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, @@ -1625,7 +3010,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", vdev_id, type, subtype, macaddr); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); } int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) @@ -1643,20 +3028,21 @@ int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); } static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg, - enum wmi_cmd_id cmd_id) + u32 cmd_id) { struct wmi_vdev_start_request_cmd *cmd; struct sk_buff *skb; const char *cmdname; u32 flags = 0; + u32 ch_flags = 0; - if (cmd_id != WMI_VDEV_START_REQUEST_CMDID && - cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID) + if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid && + cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid) return -EINVAL; if (WARN_ON(arg->ssid && arg->ssid_len == 0)) return -EINVAL; @@ -1665,9 +3051,9 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) return -EINVAL; - if (cmd_id == WMI_VDEV_START_REQUEST_CMDID) + if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid) cmdname = "start"; - else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID) + else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid) cmdname = "restart"; else return -EINVAL; /* should not happen, we already check cmd_id */ @@ -1680,6 +3066,8 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, flags |= WMI_VDEV_START_HIDDEN_SSID; if (arg->pmf_enabled) flags |= WMI_VDEV_START_PMF_ENABLED; + if (arg->channel.chan_radar) + ch_flags |= WMI_CHAN_FLAG_DFS; cmd = (struct wmi_vdev_start_request_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); @@ -1701,6 +3089,7 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, __cpu_to_le32(arg->channel.band_center_freq1); cmd->chan.mode = arg->channel.mode; + cmd->chan.flags |= __cpu_to_le32(ch_flags); cmd->chan.min_power = arg->channel.min_power; cmd->chan.max_power = arg->channel.max_power; cmd->chan.reg_power = arg->channel.max_reg_power; @@ -1708,9 +3097,10 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, cmd->chan.antenna_max = arg->channel.max_antenna_gain; ath10k_dbg(ATH10K_DBG_WMI, - "wmi vdev %s id 0x%x freq %d, mode %d, ch_flags: 0x%0X," - "max_power: %d\n", cmdname, arg->vdev_id, arg->channel.freq, - arg->channel.mode, flags, arg->channel.max_power); + "wmi vdev %s id 0x%x flags: 0x%0X, freq %d, mode %d, " + "ch_flags: 0x%0X, max_power: %d\n", cmdname, arg->vdev_id, + flags, arg->channel.freq, arg->channel.mode, + cmd->chan.flags, arg->channel.max_power); return ath10k_wmi_cmd_send(ar, skb, cmd_id); } @@ -1718,15 +3108,17 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, int ath10k_wmi_vdev_start(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg) { - return ath10k_wmi_vdev_start_restart(ar, arg, - WMI_VDEV_START_REQUEST_CMDID); + u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid; + + return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); } int ath10k_wmi_vdev_restart(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg) { - return ath10k_wmi_vdev_start_restart(ar, arg, - WMI_VDEV_RESTART_REQUEST_CMDID); + u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid; + + return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); } int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) @@ -1743,7 +3135,7 @@ int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); } int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) @@ -1758,13 +3150,13 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) cmd = (struct wmi_vdev_up_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->vdev_assoc_id = __cpu_to_le32(aid); - memcpy(&cmd->vdev_bssid.addr, bssid, 6); + memcpy(&cmd->vdev_bssid.addr, bssid, ETH_ALEN); ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", vdev_id, aid, bssid); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); } int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) @@ -1782,15 +3174,22 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt vdev down id 0x%x\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); } int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_param param_id, u32 param_value) + u32 param_id, u32 param_value) { struct wmi_vdev_set_param_cmd *cmd; struct sk_buff *skb; + if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) { + ath10k_dbg(ATH10K_DBG_WMI, + "vdev param %d not supported by firmware\n", + param_id); + return -EOPNOTSUPP; + } + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1804,7 +3203,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, "wmi vdev id 0x%x set param %d value %d\n", vdev_id, param_id, param_value); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); } int ath10k_wmi_vdev_install_key(struct ath10k *ar, @@ -1839,7 +3238,8 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev install key idx %d cipher %d len %d\n", arg->key_idx, arg->key_cipher, arg->key_len); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_install_key_cmdid); } int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, @@ -1859,7 +3259,7 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); } int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, @@ -1879,7 +3279,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer delete vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); } int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, @@ -1900,7 +3300,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", vdev_id, peer_addr, tid_bitmap); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); } int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, @@ -1918,13 +3318,13 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->param_id = __cpu_to_le32(param_id); cmd->param_value = __cpu_to_le32(param_value); - memcpy(&cmd->peer_macaddr.addr, peer_addr, 6); + memcpy(&cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev %d peer 0x%pM set param %d value %d\n", vdev_id, peer_addr, param_id, param_value); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); } int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, @@ -1945,7 +3345,8 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, "wmi set powersave id 0x%x mode %d\n", vdev_id, psmode); - return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_mode_cmdid); } int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, @@ -1967,7 +3368,8 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi sta ps param vdev_id 0x%x param %d value %d\n", vdev_id, param_id, value); - return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_param_cmdid); } int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, @@ -1993,7 +3395,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", vdev_id, param_id, value, mac); - return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->ap_ps_peer_param_cmdid); } int ath10k_wmi_scan_chan_list(struct ath10k *ar, @@ -2031,6 +3434,8 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, flags |= WMI_CHAN_FLAG_ALLOW_VHT; if (ch->ht40plus) flags |= WMI_CHAN_FLAG_HT40_PLUS; + if (ch->chan_radar) + flags |= WMI_CHAN_FLAG_DFS; ci->mhz = __cpu_to_le32(ch->freq); ci->band_center_freq1 = __cpu_to_le32(ch->freq); @@ -2039,14 +3444,13 @@ 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; ci->flags |= __cpu_to_le32(flags); } - return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); } int ath10k_wmi_peer_assoc(struct ath10k *ar, @@ -2103,28 +3507,51 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); ath10k_dbg(ATH10K_DBG_WMI, - "wmi peer assoc vdev %d addr %pM\n", - arg->vdev_id, arg->addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); + "wmi peer assoc vdev %d addr %pM (%s)\n", + arg->vdev_id, arg->addr, + arg->peer_reassoc ? "reassociate" : "new"); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); } -int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg) +/* This function assumes the beacon is already DMA mapped */ +int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) { - struct wmi_bcn_tx_cmd *cmd; + struct wmi_bcn_tx_ref_cmd *cmd; struct sk_buff *skb; + struct sk_buff *beacon = arvif->beacon; + struct ath10k *ar = arvif->ar; + struct ieee80211_hdr *hdr; + int ret; + u16 fc; - skb = ath10k_wmi_alloc_skb(sizeof(*cmd) + arg->bcn_len); + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; - cmd = (struct wmi_bcn_tx_cmd *)skb->data; - cmd->hdr.vdev_id = __cpu_to_le32(arg->vdev_id); - cmd->hdr.tx_rate = __cpu_to_le32(arg->tx_rate); - cmd->hdr.tx_power = __cpu_to_le32(arg->tx_power); - cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len); - memcpy(cmd->bcn, arg->bcn, arg->bcn_len); + hdr = (struct ieee80211_hdr *)beacon->data; + fc = le16_to_cpu(hdr->frame_control); - return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID); + cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); + cmd->data_len = __cpu_to_le32(beacon->len); + cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr); + cmd->msdu_id = 0; + cmd->frame_control = __cpu_to_le32(fc); + cmd->flags = 0; + + if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO); + + if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB); + + ret = ath10k_wmi_cmd_send_nowait(ar, skb, + ar->wmi.cmd->pdev_send_bcn_cmdid); + + if (ret) + dev_kfree_skb(skb); + + return ret; } static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, @@ -2155,7 +3582,8 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_wmm_params_cmdid); } int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) @@ -2171,7 +3599,7 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) cmd->stats_id = __cpu_to_le32(stats_id); ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); } int ath10k_wmi_force_fw_hang(struct ath10k *ar, @@ -2190,5 +3618,42 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", type, delay_ms); - return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); +} + +int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) +{ + struct wmi_dbglog_cfg_cmd *cmd; + struct sk_buff *skb; + u32 cfg; + + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_dbglog_cfg_cmd *)skb->data; + + if (module_enable) { + cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE, + ATH10K_DBGLOG_CFG_LOG_LVL); + } else { + /* set back defaults, all modules with WARN level */ + cfg = SM(ATH10K_DBGLOG_LEVEL_WARN, + ATH10K_DBGLOG_CFG_LOG_LVL); + module_enable = ~0; + } + + cmd->module_enable = __cpu_to_le32(module_enable); + cmd->module_valid = __cpu_to_le32(~0); + cmd->config_enable = __cpu_to_le32(cfg); + cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi dbglog cfg modules %08x %08x config %08x %08x\n", + __le32_to_cpu(cmd->module_enable), + __le32_to_cpu(cmd->module_valid), + __le32_to_cpu(cmd->config_enable), + __le32_to_cpu(cmd->config_valid)); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 2c5a4f8daf2..e93df2c1041 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -198,15 +198,117 @@ struct wmi_mac_addr { } __packed; } __packed; -/* macro to convert MAC address from WMI word format to char array */ -#define WMI_MAC_ADDR_TO_CHAR_ARRAY(pwmi_mac_addr, c_macaddr) do { \ - (c_macaddr)[0] = ((pwmi_mac_addr)->word0) & 0xff; \ - (c_macaddr)[1] = (((pwmi_mac_addr)->word0) >> 8) & 0xff; \ - (c_macaddr)[2] = (((pwmi_mac_addr)->word0) >> 16) & 0xff; \ - (c_macaddr)[3] = (((pwmi_mac_addr)->word0) >> 24) & 0xff; \ - (c_macaddr)[4] = ((pwmi_mac_addr)->word1) & 0xff; \ - (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \ - } while (0) +struct wmi_cmd_map { + u32 init_cmdid; + u32 start_scan_cmdid; + u32 stop_scan_cmdid; + u32 scan_chan_list_cmdid; + u32 scan_sch_prio_tbl_cmdid; + u32 pdev_set_regdomain_cmdid; + u32 pdev_set_channel_cmdid; + u32 pdev_set_param_cmdid; + u32 pdev_pktlog_enable_cmdid; + u32 pdev_pktlog_disable_cmdid; + u32 pdev_set_wmm_params_cmdid; + u32 pdev_set_ht_cap_ie_cmdid; + u32 pdev_set_vht_cap_ie_cmdid; + u32 pdev_set_dscp_tid_map_cmdid; + u32 pdev_set_quiet_mode_cmdid; + u32 pdev_green_ap_ps_enable_cmdid; + u32 pdev_get_tpc_config_cmdid; + u32 pdev_set_base_macaddr_cmdid; + u32 vdev_create_cmdid; + u32 vdev_delete_cmdid; + u32 vdev_start_request_cmdid; + u32 vdev_restart_request_cmdid; + u32 vdev_up_cmdid; + u32 vdev_stop_cmdid; + u32 vdev_down_cmdid; + u32 vdev_set_param_cmdid; + u32 vdev_install_key_cmdid; + u32 peer_create_cmdid; + u32 peer_delete_cmdid; + u32 peer_flush_tids_cmdid; + u32 peer_set_param_cmdid; + u32 peer_assoc_cmdid; + u32 peer_add_wds_entry_cmdid; + u32 peer_remove_wds_entry_cmdid; + u32 peer_mcast_group_cmdid; + u32 bcn_tx_cmdid; + u32 pdev_send_bcn_cmdid; + u32 bcn_tmpl_cmdid; + u32 bcn_filter_rx_cmdid; + u32 prb_req_filter_rx_cmdid; + u32 mgmt_tx_cmdid; + u32 prb_tmpl_cmdid; + u32 addba_clear_resp_cmdid; + u32 addba_send_cmdid; + u32 addba_status_cmdid; + u32 delba_send_cmdid; + u32 addba_set_resp_cmdid; + u32 send_singleamsdu_cmdid; + u32 sta_powersave_mode_cmdid; + u32 sta_powersave_param_cmdid; + u32 sta_mimo_ps_mode_cmdid; + u32 pdev_dfs_enable_cmdid; + u32 pdev_dfs_disable_cmdid; + u32 roam_scan_mode; + u32 roam_scan_rssi_threshold; + u32 roam_scan_period; + u32 roam_scan_rssi_change_threshold; + u32 roam_ap_profile; + u32 ofl_scan_add_ap_profile; + u32 ofl_scan_remove_ap_profile; + u32 ofl_scan_period; + u32 p2p_dev_set_device_info; + u32 p2p_dev_set_discoverability; + u32 p2p_go_set_beacon_ie; + u32 p2p_go_set_probe_resp_ie; + u32 p2p_set_vendor_ie_data_cmdid; + u32 ap_ps_peer_param_cmdid; + u32 ap_ps_peer_uapsd_coex_cmdid; + u32 peer_rate_retry_sched_cmdid; + u32 wlan_profile_trigger_cmdid; + u32 wlan_profile_set_hist_intvl_cmdid; + u32 wlan_profile_get_profile_data_cmdid; + u32 wlan_profile_enable_profile_id_cmdid; + u32 wlan_profile_list_profile_id_cmdid; + u32 pdev_suspend_cmdid; + u32 pdev_resume_cmdid; + u32 add_bcn_filter_cmdid; + u32 rmv_bcn_filter_cmdid; + u32 wow_add_wake_pattern_cmdid; + u32 wow_del_wake_pattern_cmdid; + u32 wow_enable_disable_wake_event_cmdid; + u32 wow_enable_cmdid; + u32 wow_hostwakeup_from_sleep_cmdid; + u32 rtt_measreq_cmdid; + u32 rtt_tsf_cmdid; + u32 vdev_spectral_scan_configure_cmdid; + u32 vdev_spectral_scan_enable_cmdid; + u32 request_stats_cmdid; + u32 set_arp_ns_offload_cmdid; + u32 network_list_offload_config_cmdid; + u32 gtk_offload_cmdid; + u32 csa_offload_enable_cmdid; + u32 csa_offload_chanswitch_cmdid; + u32 chatter_set_mode_cmdid; + u32 peer_tid_addba_cmdid; + u32 peer_tid_delba_cmdid; + u32 sta_dtim_ps_method_cmdid; + u32 sta_uapsd_auto_trig_cmdid; + u32 sta_keepalive_cmd; + u32 echo_cmdid; + u32 pdev_utf_cmdid; + u32 dbglog_cfg_cmdid; + u32 pdev_qvit_cmdid; + u32 pdev_ftm_intg_cmdid; + u32 vdev_set_keepalive_cmdid; + u32 vdev_get_keepalive_cmdid; + u32 force_fw_hang_cmdid; + u32 gpio_config_cmdid; + u32 gpio_output_cmdid; +}; /* * wmi command groups. @@ -247,7 +349,9 @@ enum wmi_cmd_group { #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) -/* Command IDs and commande events. */ +#define WMI_CMD_UNSUPPORTED 0 + +/* Command IDs and command events for MAIN FW. */ enum wmi_cmd_id { WMI_INIT_CMDID = 0x1, @@ -488,6 +592,217 @@ enum wmi_event_id { WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO), }; +/* Command IDs and command events for 10.X firmware */ +enum wmi_10x_cmd_id { + WMI_10X_START_CMDID = 0x9000, + WMI_10X_END_CMDID = 0x9FFF, + + /* initialize the wlan sub system */ + WMI_10X_INIT_CMDID, + + /* Scan specific commands */ + + WMI_10X_START_SCAN_CMDID = WMI_10X_START_CMDID, + WMI_10X_STOP_SCAN_CMDID, + WMI_10X_SCAN_CHAN_LIST_CMDID, + WMI_10X_ECHO_CMDID, + + /* PDEV(physical device) specific commands */ + WMI_10X_PDEV_SET_REGDOMAIN_CMDID, + WMI_10X_PDEV_SET_CHANNEL_CMDID, + WMI_10X_PDEV_SET_PARAM_CMDID, + WMI_10X_PDEV_PKTLOG_ENABLE_CMDID, + WMI_10X_PDEV_PKTLOG_DISABLE_CMDID, + WMI_10X_PDEV_SET_WMM_PARAMS_CMDID, + WMI_10X_PDEV_SET_HT_CAP_IE_CMDID, + WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_10X_PDEV_SET_BASE_MACADDR_CMDID, + WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_10X_PDEV_SET_QUIET_MODE_CMDID, + WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_10X_PDEV_GET_TPC_CONFIG_CMDID, + + /* VDEV(virtual device) specific commands */ + WMI_10X_VDEV_CREATE_CMDID, + WMI_10X_VDEV_DELETE_CMDID, + WMI_10X_VDEV_START_REQUEST_CMDID, + WMI_10X_VDEV_RESTART_REQUEST_CMDID, + WMI_10X_VDEV_UP_CMDID, + WMI_10X_VDEV_STOP_CMDID, + WMI_10X_VDEV_DOWN_CMDID, + WMI_10X_VDEV_STANDBY_RESPONSE_CMDID, + WMI_10X_VDEV_RESUME_RESPONSE_CMDID, + WMI_10X_VDEV_SET_PARAM_CMDID, + WMI_10X_VDEV_INSTALL_KEY_CMDID, + + /* peer specific commands */ + WMI_10X_PEER_CREATE_CMDID, + WMI_10X_PEER_DELETE_CMDID, + WMI_10X_PEER_FLUSH_TIDS_CMDID, + WMI_10X_PEER_SET_PARAM_CMDID, + WMI_10X_PEER_ASSOC_CMDID, + WMI_10X_PEER_ADD_WDS_ENTRY_CMDID, + WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_10X_PEER_MCAST_GROUP_CMDID, + + /* beacon/management specific commands */ + + WMI_10X_BCN_TX_CMDID, + WMI_10X_BCN_PRB_TMPL_CMDID, + WMI_10X_BCN_FILTER_RX_CMDID, + WMI_10X_PRB_REQ_FILTER_RX_CMDID, + WMI_10X_MGMT_TX_CMDID, + + /* commands to directly control ba negotiation directly from host. */ + WMI_10X_ADDBA_CLEAR_RESP_CMDID, + WMI_10X_ADDBA_SEND_CMDID, + WMI_10X_ADDBA_STATUS_CMDID, + WMI_10X_DELBA_SEND_CMDID, + WMI_10X_ADDBA_SET_RESP_CMDID, + WMI_10X_SEND_SINGLEAMSDU_CMDID, + + /* Station power save specific config */ + WMI_10X_STA_POWERSAVE_MODE_CMDID, + WMI_10X_STA_POWERSAVE_PARAM_CMDID, + WMI_10X_STA_MIMO_PS_MODE_CMDID, + + /* set debug log config */ + WMI_10X_DBGLOG_CFG_CMDID, + + /* DFS-specific commands */ + WMI_10X_PDEV_DFS_ENABLE_CMDID, + WMI_10X_PDEV_DFS_DISABLE_CMDID, + + /* QVIT specific command id */ + WMI_10X_PDEV_QVIT_CMDID, + + /* Offload Scan and Roaming related commands */ + WMI_10X_ROAM_SCAN_MODE, + WMI_10X_ROAM_SCAN_RSSI_THRESHOLD, + WMI_10X_ROAM_SCAN_PERIOD, + WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_10X_ROAM_AP_PROFILE, + WMI_10X_OFL_SCAN_ADD_AP_PROFILE, + WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_10X_OFL_SCAN_PERIOD, + + /* P2P specific commands */ + WMI_10X_P2P_DEV_SET_DEVICE_INFO, + WMI_10X_P2P_DEV_SET_DISCOVERABILITY, + WMI_10X_P2P_GO_SET_BEACON_IE, + WMI_10X_P2P_GO_SET_PROBE_RESP_IE, + + /* AP power save specific config */ + WMI_10X_AP_PS_PEER_PARAM_CMDID, + WMI_10X_AP_PS_PEER_UAPSD_COEX_CMDID, + + /* Rate-control specific commands */ + WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, + + /* WLAN Profiling commands. */ + WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, + WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + + /* Suspend resume command Ids */ + WMI_10X_PDEV_SUSPEND_CMDID, + WMI_10X_PDEV_RESUME_CMDID, + + /* Beacon filter commands */ + WMI_10X_ADD_BCN_FILTER_CMDID, + WMI_10X_RMV_BCN_FILTER_CMDID, + + /* WOW Specific WMI commands*/ + WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID, + WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_10X_WOW_ENABLE_CMDID, + WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + + /* RTT measurement related cmd */ + WMI_10X_RTT_MEASREQ_CMDID, + WMI_10X_RTT_TSF_CMDID, + + /* transmit beacon by value */ + WMI_10X_PDEV_SEND_BCN_CMDID, + + /* F/W stats */ + WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_10X_REQUEST_STATS_CMDID, + + /* GPIO Configuration */ + WMI_10X_GPIO_CONFIG_CMDID, + WMI_10X_GPIO_OUTPUT_CMDID, + + WMI_10X_PDEV_UTF_CMDID = WMI_10X_END_CMDID - 1, +}; + +enum wmi_10x_event_id { + WMI_10X_SERVICE_READY_EVENTID = 0x8000, + WMI_10X_READY_EVENTID, + WMI_10X_START_EVENTID = 0x9000, + WMI_10X_END_EVENTID = 0x9FFF, + + /* Scan specific events */ + WMI_10X_SCAN_EVENTID = WMI_10X_START_EVENTID, + WMI_10X_ECHO_EVENTID, + WMI_10X_DEBUG_MESG_EVENTID, + WMI_10X_UPDATE_STATS_EVENTID, + + /* Instantaneous RSSI event */ + WMI_10X_INST_RSSI_STATS_EVENTID, + + /* VDEV specific events */ + WMI_10X_VDEV_START_RESP_EVENTID, + WMI_10X_VDEV_STANDBY_REQ_EVENTID, + WMI_10X_VDEV_RESUME_REQ_EVENTID, + WMI_10X_VDEV_STOPPED_EVENTID, + + /* peer specific events */ + WMI_10X_PEER_STA_KICKOUT_EVENTID, + + /* beacon/mgmt specific events */ + WMI_10X_HOST_SWBA_EVENTID, + WMI_10X_TBTTOFFSET_UPDATE_EVENTID, + WMI_10X_MGMT_RX_EVENTID, + + /* Channel stats event */ + WMI_10X_CHAN_INFO_EVENTID, + + /* PHY Error specific WMI event */ + WMI_10X_PHYERR_EVENTID, + + /* Roam event to trigger roaming on host */ + WMI_10X_ROAM_EVENTID, + + /* matching AP found from list of profiles */ + WMI_10X_PROFILE_MATCH, + + /* debug print message used for tracing FW code while debugging */ + WMI_10X_DEBUG_PRINT_EVENTID, + /* VI spoecific event */ + WMI_10X_PDEV_QVIT_EVENTID, + /* FW code profile data in response to profile request */ + WMI_10X_WLAN_PROFILE_DATA_EVENTID, + + /*RTT related event ID*/ + WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID, + WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_10X_RTT_ERROR_REPORT_EVENTID, + + WMI_10X_WOW_WAKEUP_HOST_EVENTID, + WMI_10X_DCS_INTERFERENCE_EVENTID, + + /* TPC config for the current operating channel */ + WMI_10X_PDEV_TPC_CONFIG_EVENTID, + + WMI_10X_GPIO_INPUT_EVENTID, + WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1, +}; + enum wmi_phy_mode { MODE_11A = 0, /* 11a Mode */ MODE_11G = 1, /* 11b/g Mode */ @@ -508,6 +823,48 @@ enum wmi_phy_mode { MODE_MAX = 14 }; +static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode) +{ + switch (mode) { + case MODE_11A: + return "11a"; + case MODE_11G: + return "11g"; + case MODE_11B: + return "11b"; + case MODE_11GONLY: + return "11gonly"; + case MODE_11NA_HT20: + return "11na-ht20"; + case MODE_11NG_HT20: + return "11ng-ht20"; + case MODE_11NA_HT40: + return "11na-ht40"; + case MODE_11NG_HT40: + return "11ng-ht40"; + case MODE_11AC_VHT20: + return "11ac-vht20"; + case MODE_11AC_VHT40: + return "11ac-vht40"; + case MODE_11AC_VHT80: + return "11ac-vht80"; + case MODE_11AC_VHT20_2G: + return "11ac-vht20-2g"; + case MODE_11AC_VHT40_2G: + return "11ac-vht40-2g"; + case MODE_11AC_VHT80_2G: + return "11ac-vht80-2g"; + case MODE_UNKNOWN: + /* skip */ + break; + + /* no default handler to allow compiler to check that the + * enum is fully handled */ + }; + + return "<unknown>"; +} + #define WMI_CHAN_LIST_TAG 0x1 #define WMI_SSID_LIST_TAG 0x2 #define WMI_BSSID_LIST_TAG 0x3 @@ -526,6 +883,7 @@ struct wmi_channel { union { __le32 reginfo0; struct { + /* note: power unit is 0.5 dBm */ u8 min_power; u8 max_power; u8 reg_power; @@ -548,7 +906,8 @@ struct wmi_channel_arg { bool allow_ht; bool allow_vht; bool ht40plus; - /* note: power unit is 1/4th of dBm */ + bool chan_radar; + /* note: power unit is 0.5 dBm */ u32 min_power; u32 max_power; u32 max_reg_power; @@ -763,13 +1122,45 @@ struct wmi_service_ready_event { struct wlan_host_mem_req mem_reqs[1]; } __packed; -/* - * status consists of upper 16 bits fo int status and lower 16 bits of - * module ID that retuned status - */ -#define WLAN_INIT_STATUS_SUCCESS 0x0 -#define WLAN_GET_INIT_STATUS_REASON(status) ((status) & 0xffff) -#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff) +/* This is the definition from 10.X firmware branch */ +struct wmi_service_ready_event_10x { + __le32 sw_version; + __le32 abi_version; + + /* WMI_PHY_CAPABILITY */ + __le32 phy_capability; + + /* Maximum number of frag table entries that SW will populate less 1 */ + __le32 max_frag_entry; + __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + __le32 num_rf_chains; + + /* + * The following field is only valid for service type + * WMI_SERVICE_11AC + */ + __le32 ht_cap_info; /* WMI HT Capability */ + __le32 vht_cap_info; /* VHT capability info field of 802.11ac */ + __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */ + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + + struct hal_reg_capabilities hal_reg_capabilities; + + __le32 sys_cap_info; + __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */ + + /* + * request to host to allocate a chuck of memory and pss it down to FW + * via WM_INIT. FW uses this as FW extesnsion memory for saving its + * data structures. Only valid for low latency interfaces like PCIE + * where FW can access this memory directly (or) by DMA. + */ + __le32 num_mem_reqs; + + struct wlan_host_mem_req mem_reqs[1]; +} __packed; + #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ) #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ) @@ -864,7 +1255,7 @@ struct wmi_resource_config { */ __le32 rx_decap_mode; - /* what is the maximum scan requests than can be queued */ + /* what is the maximum number of scan requests that can be queued */ __le32 scan_max_pending_reqs; /* maximum VDEV that could use BMISS offload */ @@ -978,6 +1369,192 @@ struct wmi_resource_config { __le32 max_frag_entries; } __packed; +struct wmi_resource_config_10x { + /* number of virtual devices (VAPs) to support */ + __le32 num_vdevs; + + /* number of peer nodes to support */ + __le32 num_peers; + + /* number of keys per peer */ + __le32 num_peer_keys; + + /* total number of TX/RX data TIDs */ + __le32 num_tids; + + /* + * max skid for resolving hash collisions + * + * The address search table is sparse, so that if two MAC addresses + * result in the same hash value, the second of these conflicting + * entries can slide to the next index in the address search table, + * and use it, if it is unoccupied. This ast_skid_limit parameter + * specifies the upper bound on how many subsequent indices to search + * over to find an unoccupied space. + */ + __le32 ast_skid_limit; + + /* + * the nominal chain mask for transmit + * + * The chain mask may be modified dynamically, e.g. to operate AP + * tx with a reduced number of chains if no clients are associated. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of tx chains. + */ + __le32 tx_chain_mask; + + /* + * the nominal chain mask for receive + * + * The chain mask may be modified dynamically, e.g. for a client + * to use a reduced number of chains for receive if the traffic to + * the client is low enough that it doesn't require downlink MIMO + * or antenna diversity. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of rx chains. + */ + __le32 rx_chain_mask; + + /* + * what rx reorder timeout (ms) to use for the AC + * + * Each WMM access class (voice, video, best-effort, background) will + * have its own timeout value to dictate how long to wait for missing + * rx MPDUs to arrive before flushing subsequent MPDUs that have + * already been received. + * This parameter specifies the timeout in milliseconds for each + * class. + */ + __le32 rx_timeout_pri_vi; + __le32 rx_timeout_pri_vo; + __le32 rx_timeout_pri_be; + __le32 rx_timeout_pri_bk; + + /* + * what mode the rx should decap packets to + * + * MAC can decap to RAW (no decap), native wifi or Ethernet types + * THis setting also determines the default TX behavior, however TX + * behavior can be modified on a per VAP basis during VAP init + */ + __le32 rx_decap_mode; + + /* what is the maximum number of scan requests that can be queued */ + __le32 scan_max_pending_reqs; + + /* maximum VDEV that could use BMISS offload */ + __le32 bmiss_offload_max_vdev; + + /* maximum VDEV that could use offload roaming */ + __le32 roam_offload_max_vdev; + + /* maximum AP profiles that would push to offload roaming */ + __le32 roam_offload_max_ap_profiles; + + /* + * how many groups to use for mcast->ucast conversion + * + * The target's WAL maintains a table to hold information regarding + * which peers belong to a given multicast group, so that if + * multicast->unicast conversion is enabled, the target can convert + * multicast tx frames to a series of unicast tx frames, to each + * peer within the multicast group. + This num_mcast_groups configuration parameter tells the target how + * many multicast groups to provide storage for within its multicast + * group membership table. + */ + __le32 num_mcast_groups; + + /* + * size to alloc for the mcast membership table + * + * This num_mcast_table_elems configuration parameter tells the + * target how many peer elements it needs to provide storage for in + * its multicast group membership table. + * These multicast group membership table elements are shared by the + * multicast groups stored within the table. + */ + __le32 num_mcast_table_elems; + + /* + * whether/how to do multicast->unicast conversion + * + * This configuration parameter specifies whether the target should + * perform multicast --> unicast conversion on transmit, and if so, + * what to do if it finds no entries in its multicast group + * membership table for the multicast IP address in the tx frame. + * Configuration value: + * 0 -> Do not perform multicast to unicast conversion. + * 1 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * drop the frame. + * 2 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * transmit the frame as multicast. + */ + __le32 mcast2ucast_mode; + + /* + * how much memory to allocate for a tx PPDU dbg log + * + * This parameter controls how much memory the target will allocate + * to store a log of tx PPDU meta-information (how large the PPDU + * was, when it was sent, whether it was successful, etc.) + */ + __le32 tx_dbg_log_size; + + /* how many AST entries to be allocated for WDS */ + __le32 num_wds_entries; + + /* + * MAC DMA burst size, e.g., For target PCI limit can be + * 0 -default, 1 256B + */ + __le32 dma_burst_size; + + /* + * Fixed delimiters to be inserted after every MPDU to + * account for interface latency to avoid underrun. + */ + __le32 mac_aggr_delim; + + /* + * determine whether target is responsible for detecting duplicate + * non-aggregate MPDU and timing out stale fragments. + * + * A-MPDU reordering is always performed on the target. + * + * 0: target responsible for frag timeout and dup checking + * 1: host responsible for frag timeout and dup checking + */ + __le32 rx_skip_defrag_timeout_dup_detection_check; + + /* + * Configuration for VoW : + * No of Video Nodes to be supported + * and Max no of descriptors for each Video link (node). + */ + __le32 vow_config; + + /* Number of msdu descriptors target should use */ + __le32 num_msdu_desc; + + /* + * Max. number of Tx fragments per MSDU + * This parameter controls the max number of Tx fragments per MSDU. + * This is sent by the target as part of the WMI_SERVICE_READY event + * and is overriden by the OS shim as required. + */ + __le32 max_frag_entries; +} __packed; + + +#define NUM_UNITS_IS_NUM_VDEVS 0x1 +#define NUM_UNITS_IS_NUM_PEERS 0x2 + /* strucutre describing host memory chunk. */ struct host_memory_chunk { /* id of the request that is passed up in service ready */ @@ -999,6 +1576,18 @@ struct wmi_init_cmd { struct host_memory_chunk host_mem_chunks[1]; } __packed; +/* _10x stucture is from 10.X FW API */ +struct wmi_init_cmd_10x { + struct wmi_resource_config_10x resource_config; + __le32 num_host_mem_chunks; + + /* + * variable number of host memory chunks. + * This should be the last element in the structure + */ + struct host_memory_chunk host_mem_chunks[1]; +} __packed; + /* TLV for channel list */ struct wmi_chan_list { __le32 tag; /* WMI_CHAN_LIST_TAG */ @@ -1118,6 +1707,88 @@ struct wmi_start_scan_cmd { */ } __packed; +/* This is the definition from 10.X firmware branch */ +struct wmi_start_scan_cmd_10x { + /* Scan ID */ + __le32 scan_id; + + /* Scan requestor ID */ + __le32 scan_req_id; + + /* VDEV id(interface) that is requesting scan */ + __le32 vdev_id; + + /* Scan Priority, input to scan scheduler */ + __le32 scan_priority; + + /* Scan events subscription */ + __le32 notify_scan_events; + + /* dwell time in msec on active channels */ + __le32 dwell_time_active; + + /* dwell time in msec on passive channels */ + __le32 dwell_time_passive; + + /* + * min time in msec on the BSS channel,only valid if atleast one + * VDEV is active + */ + __le32 min_rest_time; + + /* + * max rest time in msec on the BSS channel,only valid if at least + * one VDEV is active + */ + /* + * the scanner will rest on the bss channel at least min_rest_time + * after min_rest_time the scanner will start checking for tx/rx + * activity on all VDEVs. if there is no activity the scanner will + * switch to off channel. if there is activity the scanner will let + * the radio on the bss channel until max_rest_time expires.at + * max_rest_time scanner will switch to off channel irrespective of + * activity. activity is determined by the idle_time parameter. + */ + __le32 max_rest_time; + + /* + * time before sending next set of probe requests. + * The scanner keeps repeating probe requests transmission with + * period specified by repeat_probe_time. + * The number of probe requests specified depends on the ssid_list + * and bssid_list + */ + __le32 repeat_probe_time; + + /* time in msec between 2 consequetive probe requests with in a set. */ + __le32 probe_spacing_time; + + /* + * data inactivity time in msec on bss channel that will be used by + * scanner for measuring the inactivity. + */ + __le32 idle_time; + + /* maximum time in msec allowed for scan */ + __le32 max_scan_time; + + /* + * delay in msec before sending first probe request after switching + * to a channel + */ + __le32 probe_delay; + + /* Scan control flags */ + __le32 scan_ctrl_flags; + + /* + * TLV (tag length value ) paramerters follow the scan_cmd structure. + * TLV can contain channel list, bssid list, ssid list and + * ie. the TLV tags are defined above; + */ +} __packed; + + struct wmi_ssid_arg { int len; const u8 *ssid; @@ -1268,7 +1939,7 @@ struct wmi_scan_event { * good idea to pass all the fields in the RX status * descriptor up to the host. */ -struct wmi_mgmt_rx_hdr { +struct wmi_mgmt_rx_hdr_v1 { __le32 channel; __le32 snr; __le32 rate; @@ -1277,8 +1948,18 @@ struct wmi_mgmt_rx_hdr { __le32 status; /* %WMI_RX_STATUS_ */ } __packed; -struct wmi_mgmt_rx_event { - struct wmi_mgmt_rx_hdr hdr; +struct wmi_mgmt_rx_hdr_v2 { + struct wmi_mgmt_rx_hdr_v1 v1; + __le32 rssi_ctl[4]; +} __packed; + +struct wmi_mgmt_rx_event_v1 { + struct wmi_mgmt_rx_hdr_v1 hdr; + u8 buf[0]; +} __packed; + +struct wmi_mgmt_rx_event_v2 { + struct wmi_mgmt_rx_hdr_v2 hdr; u8 buf[0]; } __packed; @@ -1288,6 +1969,10 @@ struct wmi_mgmt_rx_event { #define WMI_RX_STATUS_ERR_MIC 0x10 #define WMI_RX_STATUS_ERR_KEY_CACHE_MISS 0x20 +#define PHY_ERROR_SPECTRAL_SCAN 0x26 +#define PHY_ERROR_FALSE_RADAR_EXT 0x24 +#define PHY_ERROR_RADAR 0x05 + struct wmi_single_phyerr_rx_hdr { /* TSF timestamp */ __le32 tsf_timestamp; @@ -1379,6 +2064,87 @@ struct wmi_comb_phyerr_rx_event { u8 bufp[0]; } __packed; +#define PHYERR_TLV_SIG 0xBB +#define PHYERR_TLV_TAG_SEARCH_FFT_REPORT 0xFB +#define PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY 0xF8 + +struct phyerr_radar_report { + __le32 reg0; /* RADAR_REPORT_REG0_* */ + __le32 reg1; /* REDAR_REPORT_REG1_* */ +} __packed; + +#define RADAR_REPORT_REG0_PULSE_IS_CHIRP_MASK 0x80000000 +#define RADAR_REPORT_REG0_PULSE_IS_CHIRP_LSB 31 + +#define RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH_MASK 0x40000000 +#define RADAR_REPORT_REG0_PULSE_IS_MAX_WIDTH_LSB 30 + +#define RADAR_REPORT_REG0_AGC_TOTAL_GAIN_MASK 0x3FF00000 +#define RADAR_REPORT_REG0_AGC_TOTAL_GAIN_LSB 20 + +#define RADAR_REPORT_REG0_PULSE_DELTA_DIFF_MASK 0x000F0000 +#define RADAR_REPORT_REG0_PULSE_DELTA_DIFF_LSB 16 + +#define RADAR_REPORT_REG0_PULSE_DELTA_PEAK_MASK 0x0000FC00 +#define RADAR_REPORT_REG0_PULSE_DELTA_PEAK_LSB 10 + +#define RADAR_REPORT_REG0_PULSE_SIDX_MASK 0x000003FF +#define RADAR_REPORT_REG0_PULSE_SIDX_LSB 0 + +#define RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID_MASK 0x80000000 +#define RADAR_REPORT_REG1_PULSE_SRCH_FFT_VALID_LSB 31 + +#define RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN_MASK 0x7F000000 +#define RADAR_REPORT_REG1_PULSE_AGC_MB_GAIN_LSB 24 + +#define RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK_MASK 0x00FF0000 +#define RADAR_REPORT_REG1_PULSE_SUBCHAN_MASK_LSB 16 + +#define RADAR_REPORT_REG1_PULSE_TSF_OFFSET_MASK 0x0000FF00 +#define RADAR_REPORT_REG1_PULSE_TSF_OFFSET_LSB 8 + +#define RADAR_REPORT_REG1_PULSE_DUR_MASK 0x000000FF +#define RADAR_REPORT_REG1_PULSE_DUR_LSB 0 + +struct phyerr_fft_report { + __le32 reg0; /* SEARCH_FFT_REPORT_REG0_ * */ + __le32 reg1; /* SEARCH_FFT_REPORT_REG1_ * */ +} __packed; + +#define SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB_MASK 0xFF800000 +#define SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB_LSB 23 + +#define SEARCH_FFT_REPORT_REG0_BASE_PWR_DB_MASK 0x007FC000 +#define SEARCH_FFT_REPORT_REG0_BASE_PWR_DB_LSB 14 + +#define SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX_MASK 0x00003000 +#define SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX_LSB 12 + +#define SEARCH_FFT_REPORT_REG0_PEAK_SIDX_MASK 0x00000FFF +#define SEARCH_FFT_REPORT_REG0_PEAK_SIDX_LSB 0 + +#define SEARCH_FFT_REPORT_REG1_RELPWR_DB_MASK 0xFC000000 +#define SEARCH_FFT_REPORT_REG1_RELPWR_DB_LSB 26 + +#define SEARCH_FFT_REPORT_REG1_AVGPWR_DB_MASK 0x03FC0000 +#define SEARCH_FFT_REPORT_REG1_AVGPWR_DB_LSB 18 + +#define SEARCH_FFT_REPORT_REG1_PEAK_MAG_MASK 0x0003FF00 +#define SEARCH_FFT_REPORT_REG1_PEAK_MAG_LSB 8 + +#define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_MASK 0x000000FF +#define SEARCH_FFT_REPORT_REG1_NUM_STR_BINS_IB_LSB 0 + + +struct phyerr_tlv { + __le16 len; + u8 tag; + u8 sig; +} __packed; + +#define DFS_RSSI_POSSIBLY_FALSE 50 +#define DFS_PEAK_MAG_THOLD_POSSIBLY_FALSE 40 + struct wmi_mgmt_tx_hdr { __le32 vdev_id; struct wmi_mac_addr peer_macaddr; @@ -1409,6 +2175,31 @@ struct wmi_pdev_set_regdomain_cmd { __le32 conformance_test_limit_5G; } __packed; +enum wmi_dfs_region { + /* Uninitialized dfs domain */ + WMI_UNINIT_DFS_DOMAIN = 0, + + /* FCC3 dfs domain */ + WMI_FCC_DFS_DOMAIN = 1, + + /* ETSI dfs domain */ + WMI_ETSI_DFS_DOMAIN = 2, + + /*Japan dfs domain */ + WMI_MKK4_DFS_DOMAIN = 3, +}; + +struct wmi_pdev_set_regdomain_cmd_10x { + __le32 reg_domain; + __le32 reg_domain_2G; + __le32 reg_domain_5G; + __le32 conformance_test_limit_2G; + __le32 conformance_test_limit_5G; + + /* dfs domain from wmi_dfs_region */ + __le32 dfs_domain; +} __packed; + /* Command to set/unset chip in quiet mode */ struct wmi_pdev_set_quiet_cmd { /* period in TUs */ @@ -1434,6 +2225,19 @@ enum ath10k_protmode { ATH10K_PROT_RTSCTS = 2, /* RTS-CTS */ }; +enum wmi_rtscts_profile { + WMI_RTSCTS_FOR_NO_RATESERIES = 0, + WMI_RTSCTS_FOR_SECOND_RATESERIES, + WMI_RTSCTS_ACROSS_SW_RETRIES +}; + +#define WMI_RTSCTS_ENABLED 1 +#define WMI_RTSCTS_SET_MASK 0x0f +#define WMI_RTSCTS_SET_LSB 0 + +#define WMI_RTSCTS_PROFILE_MASK 0xf0 +#define WMI_RTSCTS_PROFILE_LSB 4 + enum wmi_beacon_gen_mode { WMI_BEACON_STAGGERED_MODE = 0, WMI_BEACON_BURST_MODE = 1 @@ -1465,10 +2269,63 @@ struct wmi_csa_event { #define VDEV_DEFAULT_STATS_UPDATE_PERIOD 500 #define PEER_DEFAULT_STATS_UPDATE_PERIOD 500 +struct wmi_pdev_param_map { + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 txpower_limit2g; + u32 txpower_limit5g; + u32 txpower_scale; + u32 beacon_gen_mode; + u32 beacon_tx_mode; + u32 resmgr_offchan_mode; + u32 protection_mode; + u32 dynamic_bw; + u32 non_agg_sw_retry_th; + u32 agg_sw_retry_th; + u32 sta_kickout_th; + u32 ac_aggrsize_scaling; + u32 ltr_enable; + u32 ltr_ac_latency_be; + u32 ltr_ac_latency_bk; + u32 ltr_ac_latency_vi; + u32 ltr_ac_latency_vo; + u32 ltr_ac_latency_timeout; + u32 ltr_sleep_override; + u32 ltr_rx_override; + u32 ltr_tx_activity_timeout; + u32 l1ss_enable; + u32 dsleep_enable; + u32 pcielp_txbuf_flush; + u32 pcielp_txbuf_watermark; + u32 pcielp_txbuf_tmo_en; + u32 pcielp_txbuf_tmo_value; + u32 pdev_stats_update_period; + u32 vdev_stats_update_period; + u32 peer_stats_update_period; + u32 bcnflt_stats_update_period; + u32 pmf_qos; + u32 arp_ac_override; + u32 dcs; + u32 ani_enable; + u32 ani_poll_period; + u32 ani_listen_period; + u32 ani_ofdm_level; + u32 ani_cck_level; + u32 dyntxchain; + u32 proxy_sta; + u32 idle_ps_config; + u32 power_gating_sleep; + u32 fast_channel_reset; + u32 burst_dur; + u32 burst_enable; +}; + +#define WMI_PDEV_PARAM_UNSUPPORTED 0 + enum wmi_pdev_param { - /* TX chian mask */ + /* TX chain mask */ WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, - /* RX chian mask */ + /* RX chain mask */ WMI_PDEV_PARAM_RX_CHAIN_MASK, /* TX power limit for 2G Radio */ WMI_PDEV_PARAM_TXPOWER_LIMIT2G, @@ -1490,7 +2347,12 @@ enum wmi_pdev_param { * 0: no protection 1:use CTS-to-self 2: use RTS/CTS */ WMI_PDEV_PARAM_PROTECTION_MODE, - /* Dynamic bandwidth 0: disable 1: enable */ + /* + * Dynamic bandwidth - 0: disable, 1: enable + * + * When enabled HW rate control tries different bandwidths when + * retransmitting frames. + */ WMI_PDEV_PARAM_DYNAMIC_BW, /* Non aggregrate/ 11g sw retry threshold.0-disable */ WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, @@ -1564,6 +2426,97 @@ enum wmi_pdev_param { WMI_PDEV_PARAM_POWER_GATING_SLEEP, }; +enum wmi_10x_pdev_param { + /* TX chian mask */ + WMI_10X_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + /* RX chian mask */ + WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + /* TX power limit for 2G Radio */ + WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + /* TX power limit for 5G Radio */ + WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + /* TX power scale */ + WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + /* Beacon generation mode . 0: host, 1: target */ + WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + /* Beacon generation mode . 0: staggered 1: bursted */ + WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + /* + * Resource manager off chan mode . + * 0: turn off off chan mode. 1: turn on offchan mode + */ + WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + /* + * Protection mode: + * 0: no protection 1:use CTS-to-self 2: use RTS/CTS + */ + WMI_10X_PDEV_PARAM_PROTECTION_MODE, + /* Dynamic bandwidth 0: disable 1: enable */ + WMI_10X_PDEV_PARAM_DYNAMIC_BW, + /* Non aggregrate/ 11g sw retry threshold.0-disable */ + WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + /* aggregrate sw retry threshold. 0-disable*/ + WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + /* Station kickout threshold (non of consecutive failures).0-disable */ + WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + /* Aggerate size scaling configuration per AC */ + WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + /* LTR enable */ + WMI_10X_PDEV_PARAM_LTR_ENABLE, + /* LTR latency for BE, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + /* LTR latency for BK, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + /* LTR latency for VI, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + /* LTR latency for VO, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + /* LTR AC latency timeout, in ms */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + /* LTR platform latency override, in us */ + WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + /* LTR-RX override, in us */ + WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + /* Tx activity timeout for LTR, in us */ + WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + /* L1SS state machine enable */ + WMI_10X_PDEV_PARAM_L1SS_ENABLE, + /* Deep sleep state machine enable */ + WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + /* pdev level stats update period in ms */ + WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + /* vdev level stats update period in ms */ + WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + /* peer level stats update period in ms */ + WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + /* beacon filter status update period */ + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */ + WMI_10X_PDEV_PARAM_PMF_QOS, + /* Access category on which ARP and DHCP frames are sent */ + WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + /* DCS configuration */ + WMI_10X_PDEV_PARAM_DCS, + /* Enable/Disable ANI on target */ + WMI_10X_PDEV_PARAM_ANI_ENABLE, + /* configure the ANI polling period */ + WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + /* configure the ANI listening period */ + WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + /* configure OFDM immunity level */ + WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + /* configure CCK immunity level */ + WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + /* Enable/Disable CDD for 1x1 STAs in rate control module */ + WMI_10X_PDEV_PARAM_DYNTXCHAIN, + /* Enable/Disable Fast channel reset*/ + WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + /* Set Bursting DUR */ + WMI_10X_PDEV_PARAM_BURST_DUR, + /* Set Bursting Enable*/ + WMI_10X_PDEV_PARAM_BURST_ENABLE, +}; + struct wmi_pdev_set_param_cmd { __le32 param_id; __le32 param_value; @@ -1757,6 +2710,9 @@ struct wal_dbg_tx_stats { /* wal pdev resets */ __le32 pdev_resets; + /* frames dropped due to non-availability of stateless TIDs */ + __le32 stateless_tid_alloc_failure; + __le32 phy_underrun; /* MPDU is more than txop limit */ @@ -1813,13 +2769,21 @@ enum wmi_stats_id { WMI_REQUEST_AP_STAT = 0x02 }; +struct wlan_inst_rssi_args { + __le16 cfg_retry_count; + __le16 retry_count; +}; + struct wmi_request_stats_cmd { __le32 stats_id; - /* - * Space to add parameters like - * peer mac addr - */ + __le32 vdev_id; + + /* peer MAC address */ + struct wmi_mac_addr peer_macaddr; + + /* Instantaneous RSSI arguments */ + struct wlan_inst_rssi_args inst_rssi_args; } __packed; /* Suspend option */ @@ -1870,7 +2834,7 @@ struct wmi_stats_event { * PDEV statistics * TODO: add all PDEV stats here */ -struct wmi_pdev_stats { +struct wmi_pdev_stats_old { __le32 chan_nf; /* Channel noise floor */ __le32 tx_frame_count; /* TX frame count */ __le32 rx_frame_count; /* RX frame count */ @@ -1881,6 +2845,23 @@ struct wmi_pdev_stats { struct wal_dbg_stats wal; /* WAL dbg stats */ } __packed; +struct wmi_pdev_stats_10x { + __le32 chan_nf; /* Channel noise floor */ + __le32 tx_frame_count; /* TX frame count */ + __le32 rx_frame_count; /* RX frame count */ + __le32 rx_clear_count; /* rx clear count */ + __le32 cycle_count; /* cycle count */ + __le32 phy_err_count; /* Phy error count */ + __le32 chan_tx_pwr; /* channel tx power */ + struct wal_dbg_stats wal; /* WAL dbg stats */ + __le32 ack_rx_bad; + __le32 rts_bad; + __le32 rts_good; + __le32 fcs_bad; + __le32 no_beacons; + __le32 mib_int_count; +} __packed; + /* * VDEV statistics * TODO: add all VDEV stats here @@ -1893,10 +2874,17 @@ struct wmi_vdev_stats { * peer statistics. * TODO: add more stats */ -struct wmi_peer_stats { +struct wmi_peer_stats_old { + struct wmi_mac_addr peer_macaddr; + __le32 peer_rssi; + __le32 peer_tx_rate; +} __packed; + +struct wmi_peer_stats_10x { struct wmi_mac_addr peer_macaddr; __le32 peer_rssi; __le32 peer_tx_rate; + __le32 peer_rx_rate; } __packed; struct wmi_vdev_create_cmd { @@ -2077,6 +3065,18 @@ struct wmi_vdev_install_key_arg { const void *key_data; }; +/* + * vdev fixed rate format: + * - preamble - b7:b6 - see WMI_RATE_PREMABLE_ + * - nss - b5:b4 - ss number (0 mean 1ss) + * - rate_mcs - b3:b0 - as below + * CCK: 0 - 11Mbps, 1 - 5,5Mbps, 2 - 2Mbps, 3 - 1Mbps, + * 4 - 11Mbps (s), 5 - 5,5Mbps (s), 6 - 2Mbps (s) + * OFDM: 0 - 48Mbps, 1 - 24Mbps, 2 - 12Mbps, 3 - 6Mbps, + * 4 - 54Mbps, 5 - 36Mbps, 6 - 18Mbps, 7 - 9Mbps + * HT/VHT: MCS index + */ + /* Preamble types to be used with VDEV fixed rate configuration */ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_OFDM, @@ -2088,6 +3088,61 @@ enum wmi_rate_preamble { /* Value to disable fixed rate setting */ #define WMI_FIXED_RATE_NONE (0xff) +struct wmi_vdev_param_map { + u32 rts_threshold; + u32 fragmentation_threshold; + u32 beacon_interval; + u32 listen_interval; + u32 multicast_rate; + u32 mgmt_tx_rate; + u32 slot_time; + u32 preamble; + u32 swba_time; + u32 wmi_vdev_stats_update_period; + u32 wmi_vdev_pwrsave_ageout_time; + u32 wmi_vdev_host_swba_interval; + u32 dtim_period; + u32 wmi_vdev_oc_scheduler_air_time_limit; + u32 wds; + u32 atim_window; + u32 bmiss_count_max; + u32 bmiss_first_bcnt; + u32 bmiss_final_bcnt; + u32 feature_wmm; + u32 chwidth; + u32 chextoffset; + u32 disable_htprotection; + u32 sta_quickkickout; + u32 mgmt_rate; + u32 protection_mode; + u32 fixed_rate; + u32 sgi; + u32 ldpc; + u32 tx_stbc; + u32 rx_stbc; + u32 intra_bss_fwd; + u32 def_keyid; + u32 nss; + u32 bcast_data_rate; + u32 mcast_data_rate; + u32 mcast_indicate; + u32 dhcp_indicate; + u32 unknown_dest_indicate; + u32 ap_keepalive_min_idle_inactive_time_secs; + u32 ap_keepalive_max_idle_inactive_time_secs; + u32 ap_keepalive_max_unresponsive_time_secs; + u32 ap_enable_nawds; + u32 mcast2ucast_set; + u32 enable_rtscts; + u32 txbf; + u32 packet_powersave; + u32 drop_unencry; + u32 tx_encap_type; + u32 ap_detect_out_of_sync_sleeping_sta_time_secs; +}; + +#define WMI_VDEV_PARAM_UNSUPPORTED 0 + /* the definition of different VDEV parameters */ enum wmi_vdev_param { /* RTS Threshold */ @@ -2219,6 +3274,121 @@ enum wmi_vdev_param { WMI_VDEV_PARAM_TX_ENCAP_TYPE, }; +/* the definition of different VDEV parameters */ +enum wmi_10x_vdev_param { + /* RTS Threshold */ + WMI_10X_VDEV_PARAM_RTS_THRESHOLD = 0x1, + /* Fragmentation threshold */ + WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + /* beacon interval in TUs */ + WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + /* Listen interval in TUs */ + WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + /* muticast rate in Mbps */ + WMI_10X_VDEV_PARAM_MULTICAST_RATE, + /* management frame rate in Mbps */ + WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + /* slot time (long vs short) */ + WMI_10X_VDEV_PARAM_SLOT_TIME, + /* preamble (long vs short) */ + WMI_10X_VDEV_PARAM_PREAMBLE, + /* SWBA time (time before tbtt in msec) */ + WMI_10X_VDEV_PARAM_SWBA_TIME, + /* time period for updating VDEV stats */ + WMI_10X_VDEV_STATS_UPDATE_PERIOD, + /* age out time in msec for frames queued for station in power save */ + WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + /* + * Host SWBA interval (time in msec before tbtt for SWBA event + * generation). + */ + WMI_10X_VDEV_HOST_SWBA_INTERVAL, + /* DTIM period (specified in units of num beacon intervals) */ + WMI_10X_VDEV_PARAM_DTIM_PERIOD, + /* + * scheduler air time limit for this VDEV. used by off chan + * scheduler. + */ + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + /* enable/dsiable WDS for this VDEV */ + WMI_10X_VDEV_PARAM_WDS, + /* ATIM Window */ + WMI_10X_VDEV_PARAM_ATIM_WINDOW, + /* BMISS max */ + WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + /* WMM enables/disabled */ + WMI_10X_VDEV_PARAM_FEATURE_WMM, + /* Channel width */ + WMI_10X_VDEV_PARAM_CHWIDTH, + /* Channel Offset */ + WMI_10X_VDEV_PARAM_CHEXTOFFSET, + /* Disable HT Protection */ + WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + /* Quick STA Kickout */ + WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + /* Rate to be used with Management frames */ + WMI_10X_VDEV_PARAM_MGMT_RATE, + /* Protection Mode */ + WMI_10X_VDEV_PARAM_PROTECTION_MODE, + /* Fixed rate setting */ + WMI_10X_VDEV_PARAM_FIXED_RATE, + /* Short GI Enable/Disable */ + WMI_10X_VDEV_PARAM_SGI, + /* Enable LDPC */ + WMI_10X_VDEV_PARAM_LDPC, + /* Enable Tx STBC */ + WMI_10X_VDEV_PARAM_TX_STBC, + /* Enable Rx STBC */ + WMI_10X_VDEV_PARAM_RX_STBC, + /* Intra BSS forwarding */ + WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + /* Setting Default xmit key for Vdev */ + WMI_10X_VDEV_PARAM_DEF_KEYID, + /* NSS width */ + WMI_10X_VDEV_PARAM_NSS, + /* Set the custom rate for the broadcast data frames */ + WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + /* Set the custom rate (rate-code) for multicast data frames */ + WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + /* Tx multicast packet indicate Enable/Disable */ + WMI_10X_VDEV_PARAM_MCAST_INDICATE, + /* Tx DHCP packet indicate Enable/Disable */ + WMI_10X_VDEV_PARAM_DHCP_INDICATE, + /* Enable host inspection of Tx unicast packet to unknown destination */ + WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + + /* The minimum amount of time AP begins to consider STA inactive */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered inactive when there is no recent + * TX/RX activity and no downlink frames are buffered for it. Once a + * STA exceeds the maximum idle inactive time, the AP will send an + * 802.11 data-null as a keep alive to verify the STA is still + * associated. If the STA does ACK the data-null, or if the data-null + * is buffered and the STA does not retrieve it, the STA will be + * considered unresponsive + * (see WMI_10X_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS). + */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered unresponsive if there is no recent + * TX/RX activity and downlink frames are buffered for it. Once a STA + * exceeds the maximum unresponsive time, the AP will send a + * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + + /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ + WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + + WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + /* Enable/Disable RTS-CTS */ + WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + /* slot time long */ #define WMI_VDEV_SLOT_TIME_LONG 0x1 /* slot time short */ @@ -2295,6 +3465,24 @@ struct wmi_bcn_tx_arg { const void *bcn; }; +enum wmi_bcn_tx_ref_flags { + WMI_BCN_TX_REF_FLAG_DTIM_ZERO = 0x1, + WMI_BCN_TX_REF_FLAG_DELIVER_CAB = 0x2, +}; + +struct wmi_bcn_tx_ref_cmd { + __le32 vdev_id; + __le32 data_len; + /* physical address of the frame - dma pointer */ + __le32 data_ptr; + /* id for host to track */ + __le32 msdu_id; + /* frame ctrl to setup PPDU desc */ + __le32 frame_control; + /* to control CABQ traffic: WMI_BCN_TX_REF_FLAG_ */ + __le32 flags; +} __packed; + /* Beacon filter */ #define WMI_BCN_FILTER_ALL 0 /* Filter all beacons */ #define WMI_BCN_FILTER_NONE 1 /* Pass all beacons */ @@ -2751,6 +3939,12 @@ enum wmi_peer_smps_state { WMI_PEER_SMPS_DYNAMIC = 0x2 }; +enum wmi_peer_chwidth { + WMI_PEER_CHWIDTH_20MHZ = 0, + WMI_PEER_CHWIDTH_40MHZ = 1, + WMI_PEER_CHWIDTH_80MHZ = 2, +}; + enum wmi_peer_param { WMI_PEER_SMPS_STATE = 0x1, /* see %wmi_peer_smps_state */ WMI_PEER_AMPDU = 0x2, @@ -2931,6 +4125,10 @@ struct wmi_chan_info_event { __le32 cycle_count; } __packed; +struct wmi_peer_sta_kickout_event { + struct wmi_mac_addr peer_macaddr; +} __packed; + #define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0) /* FIXME: empirically extrapolated */ @@ -2994,13 +4192,60 @@ struct wmi_force_fw_hang_cmd { __le32 delay_ms; } __packed; +enum ath10k_dbglog_level { + ATH10K_DBGLOG_LEVEL_VERBOSE = 0, + ATH10K_DBGLOG_LEVEL_INFO = 1, + ATH10K_DBGLOG_LEVEL_WARN = 2, + ATH10K_DBGLOG_LEVEL_ERR = 3, +}; + +/* VAP ids to enable dbglog */ +#define ATH10K_DBGLOG_CFG_VAP_LOG_LSB 0 +#define ATH10K_DBGLOG_CFG_VAP_LOG_MASK 0x0000ffff + +/* to enable dbglog in the firmware */ +#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_LSB 16 +#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_MASK 0x00010000 + +/* timestamp resolution */ +#define ATH10K_DBGLOG_CFG_RESOLUTION_LSB 17 +#define ATH10K_DBGLOG_CFG_RESOLUTION_MASK 0x000E0000 + +/* number of queued messages before sending them to the host */ +#define ATH10K_DBGLOG_CFG_REPORT_SIZE_LSB 20 +#define ATH10K_DBGLOG_CFG_REPORT_SIZE_MASK 0x0ff00000 + +/* + * Log levels to enable. This defines the minimum level to enable, this is + * not a bitmask. See enum ath10k_dbglog_level for the values. + */ +#define ATH10K_DBGLOG_CFG_LOG_LVL_LSB 28 +#define ATH10K_DBGLOG_CFG_LOG_LVL_MASK 0x70000000 + +/* + * Note: this is a cleaned up version of a struct firmware uses. For + * example, config_valid was hidden inside an array. + */ +struct wmi_dbglog_cfg_cmd { + /* bitmask to hold mod id config*/ + __le32 module_enable; + + /* see ATH10K_DBGLOG_CFG_ */ + __le32 config_enable; + + /* mask of module id bits to be changed */ + __le32 module_valid; + + /* mask of config bits to be changed, see ATH10K_DBGLOG_CFG_ */ + __le32 config_valid; +} __packed; + #define ATH10K_RTS_MAX 2347 #define ATH10K_FRAGMT_THRESHOLD_MIN 540 #define ATH10K_FRAGMT_THRESHOLD_MAX 2346 #define WMI_MAX_EVENT 0x1000 /* Maximum number of pending TXed WMI packets */ -#define WMI_MAX_PENDING_TX_COUNT 128 #define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr) /* By default disable power save for IBSS */ @@ -3013,17 +4258,16 @@ int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); -void ath10k_wmi_flush_tx(struct ath10k *ar); -int ath10k_wmi_connect_htc_service(struct ath10k *ar); +int ath10k_wmi_connect(struct ath10k *ar); int ath10k_wmi_pdev_set_channel(struct ath10k *ar, const struct wmi_channel_arg *); -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar); +int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt); int ath10k_wmi_pdev_resume_target(struct ath10k *ar); int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, - u16 rd5g, u16 ctl2g, u16 ctl5g); -int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, - u32 value); + u16 rd5g, u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg); +int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value); int ath10k_wmi_cmd_init(struct ath10k *ar); int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); @@ -3043,7 +4287,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid); int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_param param_id, u32 param_value); + u32 param_id, u32 param_value); int ath10k_wmi_vdev_install_key(struct ath10k *ar, const struct wmi_vdev_install_key_arg *arg); int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, @@ -3066,11 +4310,13 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, enum wmi_ap_ps_peer_param param_id, u32 value); int ath10k_wmi_scan_chan_list(struct ath10k *ar, const struct wmi_scan_chan_list_arg *arg); -int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); +int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif); int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, const struct wmi_pdev_set_wmm_params_arg *arg); int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); int ath10k_wmi_force_fw_hang(struct ath10k *ar, enum wmi_force_fw_hang_type type, u32 delay_ms); +int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable); #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index e9bc9e616b6..79bffe165ca 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -37,12 +37,9 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath5k_hw *ah = common->priv; struct platform_device *pdev = to_platform_device(ah->dev); - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); u16 *eeprom, *eeprom_end; - - - bcfg = pdev->dev.platform_data; eeprom = (u16 *) bcfg->radio; eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ; @@ -57,7 +54,7 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) int ath5k_hw_read_srev(struct ath5k_hw *ah) { struct platform_device *pdev = to_platform_device(ah->dev); - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); ah->ah_mac_srev = bcfg->devid; return 0; } @@ -65,7 +62,7 @@ int ath5k_hw_read_srev(struct ath5k_hw *ah) static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) { struct platform_device *pdev = to_platform_device(ah->dev); - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); u8 *cfg_mac; if (to_platform_device(ah->dev)->id == 0) @@ -87,7 +84,7 @@ static const struct ath_bus_ops ath_ahb_bus_ops = { /*Initialization*/ static int ath_ahb_probe(struct platform_device *pdev) { - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); struct ath5k_hw *ah; struct ieee80211_hw *hw; struct resource *res; @@ -96,7 +93,7 @@ static int ath_ahb_probe(struct platform_device *pdev) int ret = 0; u32 reg; - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data specified\n"); ret = -EINVAL; goto err_out; @@ -193,7 +190,7 @@ static int ath_ahb_probe(struct platform_device *pdev) static int ath_ahb_remove(struct platform_device *pdev) { - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); struct ieee80211_hw *hw = platform_get_drvdata(pdev); struct ath5k_hw *ah; u32 reg; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 48161edec8d..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)); @@ -1238,14 +1241,11 @@ static void ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, struct ieee80211_rx_status *rxs) { - struct ath_common *common = ath5k_hw_common(ah); u64 tsf, bc_tstamp; u32 hw_tu; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - if (ieee80211_is_beacon(mgmt->frame_control) && - le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && - ether_addr_equal(mgmt->bssid, common->curbssid)) { + if (le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS) { /* * Received an IBSS beacon with the same BSSID. Hardware *must* * have updated the local TSF. We have to work around various @@ -1301,23 +1301,6 @@ ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, } } -static void -ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi) -{ - struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; - struct ath_common *common = ath5k_hw_common(ah); - - /* only beacons from our BSSID */ - if (!ieee80211_is_beacon(mgmt->frame_control) || - !ether_addr_equal(mgmt->bssid, common->curbssid)) - return; - - ewma_add(&ah->ah_beacon_rssi_avg, rssi); - - /* in IBSS mode we should keep RSSI statistics per neighbour */ - /* le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS */ -} - /* * Compute padding position. skb must contain an IEEE 802.11 frame */ @@ -1390,6 +1373,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, struct ath5k_rx_status *rs) { struct ieee80211_rx_status *rxs; + struct ath_common *common = ath5k_hw_common(ah); ath5k_remove_padding(skb); @@ -1442,11 +1426,13 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, trace_ath5k_rx(ah, skb); - ath5k_update_beacon_rssi(ah, skb, rs->rs_rssi); + if (ath_is_mybeacon(common, (struct ieee80211_hdr *)skb->data)) { + ewma_add(&ah->ah_beacon_rssi_avg, rs->rs_rssi); - /* check beacons in IBSS mode */ - if (ah->opmode == NL80211_IFTYPE_ADHOC) - ath5k_check_ibss_tsf(ah, skb, rxs); + /* check beacons in IBSS mode */ + if (ah->opmode == NL80211_IFTYPE_ADHOC) + ath5k_check_ibss_tsf(ah, skb, rxs); + } ieee80211_rx(ah->hw, skb); } @@ -1663,15 +1649,15 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, ah->stats.tx_bytes_count += skb->len; info = IEEE80211_SKB_CB(skb); + size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); + memcpy(info->status.rates, bf->rates, size); + tries[0] = info->status.rates[0].count; tries[1] = info->status.rates[1].count; tries[2] = info->status.rates[2].count; ieee80211_tx_info_clear_status(info); - size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); - memcpy(info->status.rates, bf->rates, size); - for (i = 0; i < ts->ts_final_idx; i++) { struct ieee80211_tx_rate *r = &info->status.rates[i]; @@ -2549,7 +2535,6 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) hw->wiphy->available_antennas_rx = 0x3; hw->extra_tx_headroom = 2; - hw->channel_change_time = 5000; /* * Mark the device as detached to avoid processing diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index ce86f158423..e6c52f7c26e 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -616,7 +616,16 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) * SISRs will also clear PISR so no need to worry here. */ - pisr_clear = pisr & ~AR5K_ISR_BITS_FROM_SISRS; + /* XXX: There seems to be an issue on some cards + * with tx interrupt flags not being updated + * on PISR despite that all Tx interrupt bits + * are cleared on SISRs. Since we handle all + * Tx queues all together it shouldn't be an + * issue if we clear Tx interrupt flags also + * on PISR to avoid that. + */ + pisr_clear = (pisr & ~AR5K_ISR_BITS_FROM_SISRS) | + (pisr & AR5K_INT_TX_ALL); /* * Write to clear them... @@ -661,7 +670,7 @@ ath5k_hw_get_isr(struct ath5k_hw *ah, enum ath5k_int *interrupt_mask) ah->ah_txq_isr_txok_all |= AR5K_REG_MS(sisr1, AR5K_SISR1_QCU_TXEOL); - /* Currently this is not much usefull since we treat + /* Currently this is not much useful since we treat * all queues the same way if we get a TXURN (update * tx trigger level) but we might need it later on*/ if (pisr & AR5K_ISR_TXURN) diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 4ee01f65423..afb23b3cc7b 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -681,6 +681,7 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) survey->channel = conf->chandef.chan; survey->noise = ah->ah_noise_floor; survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_IN_USE | SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY | SURVEY_INFO_CHANNEL_TIME_RX | diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index d6bc7cb61bf..0fce1c76638 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -110,7 +110,7 @@ ath5k_hw_radio_revision(struct ath5k_hw *ah, enum ieee80211_band band) ath5k_hw_reg_write(ah, 0x00010000, AR5K_PHY(0x20)); if (ah->ah_version == AR5K_AR5210) { - srev = ath5k_hw_reg_read(ah, AR5K_PHY(256) >> 28) & 0xf; + srev = (ath5k_hw_reg_read(ah, AR5K_PHY(256)) >> 28) & 0xf; ret = (u16)ath5k_hw_bitswap(srev, 4) + 1; } else { srev = (ath5k_hw_reg_read(ah, AR5K_PHY(0x100)) >> 24) & 0xff; @@ -3709,8 +3709,8 @@ ath5k_hw_txpower(struct ath5k_hw *ah, struct ieee80211_channel *channel, AR5K_REG_MS(AR5K_TUNE_MAX_TXPOWER, AR5K_TPC_CHIRP), AR5K_TPC); } else { - ath5k_hw_reg_write(ah, AR5K_PHY_TXPOWER_RATE_MAX | - AR5K_TUNE_MAX_TXPOWER, AR5K_PHY_TXPOWER_RATE_MAX); + ath5k_hw_reg_write(ah, AR5K_TUNE_MAX_TXPOWER, + AR5K_PHY_TXPOWER_RATE_MAX); } return 0; diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index e39e5860a2e..9c125ff083f 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -1,11 +1,19 @@ config ATH6KL tristate "Atheros mobile chipsets support" + depends on CFG80211 + ---help--- + This module adds core support for wireless adapters based on + Atheros AR6003 and AR6004 chipsets. You still need separate + bus drivers for USB and SDIO to be able to use real devices. + + If you choose to build it as a module, it will be called + ath6kl_core. Please note that AR6002 and AR6001 are not + supported by this driver. config ATH6KL_SDIO tristate "Atheros ath6kl SDIO support" depends on ATH6KL depends on MMC - depends on CFG80211 ---help--- This module adds support for wireless adapters based on Atheros AR6003 and AR6004 chipsets running over SDIO. If you @@ -17,25 +25,31 @@ config ATH6KL_USB tristate "Atheros ath6kl USB support" depends on ATH6KL depends on USB - depends on CFG80211 ---help--- This module adds support for wireless adapters based on - Atheros AR6004 chipset running over USB. This is still under - implementation and it isn't functional. If you choose to - build it as a module, it will be called ath6kl_usb. + Atheros AR6004 chipset and chipsets based on it running over + USB. If you choose to build it as a module, it will be + called ath6kl_usb. config ATH6KL_DEBUG bool "Atheros ath6kl debugging" depends on ATH6KL ---help--- - Enables debug support + Enables ath6kl debug support, including debug messages + enabled with debug_mask module parameter and debugfs + interface. + + If unsure, say Y to make it easier to debug problems. config ATH6KL_TRACING bool "Atheros ath6kl tracing support" depends on ATH6KL depends on EVENT_TRACING ---help--- - Select this to ath6kl use tracing infrastructure. + Select this to ath6kl use tracing infrastructure which, for + example, can be enabled with help of trace-cmd. All debug + messages and commands are delivered to using individually + enablable trace points. If unsure, say Y to make it easier to debug problems. @@ -47,3 +61,5 @@ config ATH6KL_REGDOMAIN Enabling this makes it possible to change the regdomain in the firmware. This can be only enabled if regulatory requirements are taken into account. + + If unsure, say N. diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 2437ad26949..0e26f4a34fd 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -724,8 +724,9 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to cfg80211\n", bssid); kfree(ie); - } else + } else { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n"); + } return bss; } @@ -790,7 +791,7 @@ void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, if (nw_type & ADHOC_NETWORK) { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "ad-hoc %s selected\n", nw_type & ADHOC_CREATOR ? "creator" : "joiner"); - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(vif->ndev, bssid, chan, GFP_KERNEL); cfg80211_put_bss(ar->wiphy, bss); return; } @@ -861,13 +862,9 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, } if (vif->nw_type & ADHOC_NETWORK) { - if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) { + if (vif->wdev.iftype != NL80211_IFTYPE_ADHOC) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: ath6k not in ibss mode\n", __func__); - return; - } - memset(bssid, 0, ETH_ALEN); - cfg80211_ibss_joined(vif->ndev, bssid, GFP_KERNEL); return; } @@ -974,7 +971,6 @@ static int ath6kl_set_probed_ssids(struct ath6kl *ar, ssid_list[i].flag, ssid_list[i].ssid.ssid_len, ssid_list[i].ssid.ssid); - } /* Make sure no old entries are left behind */ @@ -1109,7 +1105,9 @@ void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); + mutex_lock(&vif->wdev.mtx); cfg80211_ch_switch_notify(vif->ndev, &chandef); + mutex_unlock(&vif->wdev.mtx); } static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, @@ -1761,7 +1759,7 @@ static bool is_rate_ht40(s32 rate, u8 *mcs, bool *sgi) } static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -1899,7 +1897,6 @@ static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif, /* Configure the patterns that we received from the user. */ for (i = 0; i < wow->n_patterns; i++) { - /* * Convert given nl80211 specific mask value to equivalent * driver specific mask value and send it to the chip along @@ -2852,8 +2849,9 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, if (p.prwise_crypto_type == 0) { p.prwise_crypto_type = NONE_CRYPT; ath6kl_set_cipher(vif, 0, true); - } else if (info->crypto.n_ciphers_pairwise == 1) + } else if (info->crypto.n_ciphers_pairwise == 1) { ath6kl_set_cipher(vif, info->crypto.ciphers_pairwise[0], true); + } switch (info->crypto.cipher_group) { case WLAN_CIPHER_SUITE_WEP40: @@ -2899,7 +2897,6 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, } if (info->inactivity_timeout) { - inactivity_timeout = info->inactivity_timeout; if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS) @@ -2977,7 +2974,7 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -2988,7 +2985,8 @@ static int ath6kl_del_station(struct wiphy *wiphy, struct net_device *dev, } static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_parameters *params) + const u8 *mac, + struct station_parameters *params) { struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); @@ -3169,12 +3167,15 @@ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) } static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); + struct ieee80211_channel *chan = params->chan; + const u8 *buf = params->buf; + size_t len = params->len; + unsigned int wait = params->wait; + bool no_cck = params->no_cck; u32 id, freq; const struct ieee80211_mgmt *mgmt; bool more_data, queued; @@ -3251,6 +3252,15 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; int ret, rssi_thold; + int n_match_sets = request->n_match_sets; + + /* + * If there's a matchset w/o an SSID, then assume it's just for + * the RSSI (nothing else is currently supported) and ignore it. + * The device only supports a global RSSI filter that we set below. + */ + if (n_match_sets == 1 && !request->match_sets[0].ssid.ssid_len) + n_match_sets = 0; if (ar->state != ATH6KL_STATE_ON) return -EIO; @@ -3263,11 +3273,11 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, request->n_ssids, request->match_sets, - request->n_match_sets); + n_match_sets); if (ret < 0) return ret; - if (!request->n_match_sets) { + if (!n_match_sets) { ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, ALL_BSS_FILTER, 0); if (ret < 0) @@ -3281,12 +3291,12 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, ar->fw_capabilities)) { - if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) + if (request->min_rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) rssi_thold = 0; - else if (request->rssi_thold < -127) + else if (request->min_rssi_thold < -127) rssi_thold = -127; else - rssi_thold = request->rssi_thold; + rssi_thold = request->min_rssi_thold; ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx, rssi_thold); diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index 98a886154d9..05debf700a8 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -22,8 +22,7 @@ #define ATH6KL_MAX_IE 256 -extern __printf(2, 3) -int ath6kl_printk(const char *level, const char *fmt, ...); +__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...); /* * Reflects the version of binary interface exposed by ATH6KL target diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 4b46adbe8c9..b0b65204276 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -45,9 +45,9 @@ module_param(testmode, uint, 0644); module_param(recovery_enable, uint, 0644); module_param(heart_beat_poll, uint, 0644); MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error"); -MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \ - "polling. This also specifies the polling interval in" \ - "msecs. Set reocvery_enable for this to be effective"); +MODULE_PARM_DESC(heart_beat_poll, + "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective"); + void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index dbfd17d0a5f..55c4064dd50 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -172,7 +172,6 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_enable_reg) { - ath6kl_dbg(ATH6KL_DBG_IRQ, ("<------- Register Table -------->\n")); if (irq_proc_reg != NULL) { @@ -219,7 +218,6 @@ void ath6kl_dump_registers(struct ath6kl_device *dev, "GMBOX lookahead alias 1: 0x%x\n", irq_proc_reg->rx_gmbox_lkahd_alias[1]); } - } if (irq_enable_reg != NULL) { @@ -1396,7 +1394,6 @@ static ssize_t ath6kl_create_qos_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; char buf[200]; @@ -1575,7 +1572,6 @@ static ssize_t ath6kl_delete_qos_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - struct ath6kl *ar = file->private_data; struct ath6kl_vif *vif; char buf[100]; diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 74369de00fb..e194c10d9f0 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -50,11 +50,10 @@ enum ATH6K_DEBUG_MASK { }; extern unsigned int debug_mask; -extern __printf(2, 3) -int ath6kl_printk(const char *level, const char *fmt, ...); -extern __printf(1, 2) int ath6kl_info(const char *fmt, ...); -extern __printf(1, 2) int ath6kl_err(const char *fmt, ...); -extern __printf(1, 2) int ath6kl_warn(const char *fmt, ...); +__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...); +__printf(1, 2) int ath6kl_info(const char *fmt, ...); +__printf(1, 2) int ath6kl_err(const char *fmt, ...); +__printf(1, 2) int ath6kl_warn(const char *fmt, ...); enum ath6kl_war { ATH6KL_WAR_INVALID_RATE, @@ -98,8 +97,8 @@ static inline void ath6kl_dump_registers(struct ath6kl_device *dev, struct ath6kl_irq_proc_registers *irq_proc_reg, struct ath6kl_irq_enable_reg *irq_en_reg) { - } + static inline void dump_cred_dist_stats(struct htc_target *target) { } diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index fea7709b5dd..18c070850a0 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -37,7 +37,6 @@ static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req, buf = req->virt_dma_buf; for (i = 0; i < req->scat_entries; i++) { - if (from_dma) memcpy(req->scat_list[i].buf, buf, req->scat_list[i].len); @@ -116,7 +115,6 @@ static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) le32_to_cpu(regdump_val[i + 2]), le32_to_cpu(regdump_val[i + 3])); } - } static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) @@ -701,5 +699,4 @@ int ath6kl_hif_setup(struct ath6kl_device *dev) fail_setup: return status; - } diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h index 61f6b21fb0a..dc6bd8cd9b8 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h @@ -197,9 +197,9 @@ struct hif_scatter_req { /* bounce buffer for upper layers to copy to/from */ u8 *virt_dma_buf; - struct hif_scatter_item scat_list[1]; - u32 scat_q_depth; + + struct hif_scatter_item scat_list[0]; }; struct ath6kl_irq_proc_registers { diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index a2c8ff80979..14cab1403dd 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h @@ -60,7 +60,7 @@ /* disable credit flow control on a specific service */ #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 -#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 +#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00U /* connect response status codes */ #define HTC_SERVICE_SUCCESS 0 diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index 65e5b719093..e481f14b987 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -112,9 +112,9 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, if (cur_ep_dist->endpoint == ENDPOINT_0) continue; - if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) + if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) { cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg; - else { + } else { /* * For the remaining data endpoints, we assume that * each cred_per_msg are the same. We use a simple @@ -129,7 +129,6 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, count = (count * 3) >> 2; count = max(count, cur_ep_dist->cred_per_msg); cur_ep_dist->cred_norm = count; - } ath6kl_dbg(ATH6KL_DBG_CREDIT, @@ -549,7 +548,6 @@ static int htc_check_credits(struct htc_target *target, enum htc_endpoint_id eid, unsigned int len, int *req_cred) { - *req_cred = (len > target->tgt_cred_sz) ? DIV_ROUND_UP(len, target->tgt_cred_sz) : 1; @@ -608,7 +606,6 @@ static void ath6kl_htc_tx_pkts_get(struct htc_target *target, unsigned int len; while (true) { - flags = 0; if (list_empty(&endpoint->txq)) @@ -889,7 +886,6 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, ac = target->dev->ar->ep2ac_map[endpoint->eid]; while (true) { - if (list_empty(&endpoint->txq)) break; @@ -1190,7 +1186,6 @@ static void ath6kl_htc_mbox_flush_txep(struct htc_target *target, list_add_tail(&packet->list, &container); htc_tx_complete(endpoint, &container); } - } static void ath6kl_htc_flush_txep_all(struct htc_target *target) @@ -1394,7 +1389,6 @@ static int ath6kl_htc_rx_setup(struct htc_target *target, ep_cb = ep->ep_cb; for (j = 0; j < n_msg; j++) { - /* * Reset flag, any packets allocated using the * rx_alloc() API cannot be recycled on @@ -1424,9 +1418,9 @@ static int ath6kl_htc_rx_setup(struct htc_target *target, } } - if (list_empty(&ep->rx_bufq)) + if (list_empty(&ep->rx_bufq)) { packet = NULL; - else { + } else { packet = list_first_entry(&ep->rx_bufq, struct htc_packet, list); list_del(&packet->list); @@ -1487,7 +1481,6 @@ static int ath6kl_htc_rx_alloc(struct htc_target *target, spin_lock_bh(&target->rx_lock); for (i = 0; i < msg; i++) { - htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i]; if (htc_hdr->eid >= ENDPOINT_MAX) { @@ -1708,7 +1701,6 @@ static int htc_parse_trailer(struct htc_target *target, lk_ahd = (struct htc_lookahead_report *) record_buf; if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) && next_lk_ahds) { - ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n", lk_ahd->pre_valid, lk_ahd->post_valid); @@ -1755,7 +1747,6 @@ static int htc_parse_trailer(struct htc_target *target, } return 0; - } static int htc_proc_trailer(struct htc_target *target, @@ -1776,7 +1767,6 @@ static int htc_proc_trailer(struct htc_target *target, status = 0; while (len > 0) { - if (len < sizeof(struct htc_record_hdr)) { status = -ENOMEM; break; @@ -2098,7 +2088,6 @@ static int ath6kl_htc_rx_fetch(struct htc_target *target, } if (!fetched_pkts) { - packet = list_first_entry(rx_pktq, struct htc_packet, list); @@ -2173,7 +2162,6 @@ int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, look_aheads[0] = msg_look_ahead; while (true) { - /* * First lookahead sets the expected endpoint IDs for all * packets in a bundle. @@ -2825,8 +2813,9 @@ static int ath6kl_htc_reset(struct htc_target *target) packet->buf = packet->buf_start; packet->endpoint = ENDPOINT_0; list_add_tail(&packet->list, &target->free_ctrl_rxbuf); - } else + } else { list_add_tail(&packet->list, &target->free_ctrl_txbuf); + } } return 0; diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index 67aa924ed8b..756fe52a12c 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -137,7 +137,6 @@ static void get_htc_packet_credit_based(struct htc_target *target, credits_required = 0; } else { - if (ep->cred_dist.credits < credits_required) break; @@ -169,7 +168,6 @@ static void get_htc_packet_credit_based(struct htc_target *target, /* queue this packet into the caller's queue */ list_add_tail(&packet->list, queue); } - } static void get_htc_packet(struct htc_target *target, @@ -279,7 +277,6 @@ static int htc_issue_packets(struct htc_target *target, list_add(&packet->list, pkt_queue); break; } - } if (status != 0) { @@ -385,7 +382,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, */ list_for_each_entry_safe(packet, tmp_pkt, txq, list) { - ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Indicat overflowed TX pkts: %p\n", __func__, packet); @@ -403,7 +399,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, list_move_tail(&packet->list, &send_queue); } - } if (list_empty(&send_queue)) { @@ -454,7 +449,6 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, * enough transmit resources. */ while (true) { - if (get_queue_depth(&ep->txq) == 0) break; @@ -495,8 +489,8 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, } spin_lock_bh(&target->tx_lock); - } + /* done with this endpoint, we can clear the count */ ep->tx_proc_cnt = 0; spin_unlock_bh(&target->tx_lock); @@ -1106,7 +1100,6 @@ free_skb: dev_kfree_skb(skb); return status; - } static void htc_flush_rx_queue(struct htc_target *target, @@ -1258,7 +1251,6 @@ static int ath6kl_htc_pipe_conn_service(struct htc_target *target, tx_alloc = 0; } else { - tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id); if (tx_alloc == 0) { status = -ENOMEM; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 4f316bdcbab..d5ef211f261 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1192,7 +1192,6 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) if (board_ext_address && ar->fw_board_len == (board_data_size + board_ext_data_size)) { - /* write extended board data */ ath6kl_dbg(ATH6KL_DBG_BOOT, "writing extended board data to 0x%x (%d B)\n", diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 5839fc23bdc..d56554674da 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -571,7 +571,6 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) { - struct ath6kl *ar = vif->ar; vif->profile.ch = cpu_to_le16(channel); @@ -600,7 +599,6 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) { - struct ath6kl_vif *vif; int res = 0; @@ -692,9 +690,9 @@ void ath6kl_tkip_micerr_event(struct ath6kl_vif *vif, u8 keyid, bool ismcast) cfg80211_michael_mic_failure(vif->ndev, sta->mac, NL80211_KEYTYPE_PAIRWISE, keyid, tsc, GFP_KERNEL); - } else + } else { ath6kl_cfg80211_tkip_micerr_event(vif, keyid, ismcast); - + } } static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) @@ -1093,8 +1091,9 @@ static int ath6kl_open(struct net_device *dev) if (test_bit(CONNECTED, &vif->flags)) { netif_carrier_on(dev); netif_wake_queue(dev); - } else + } else { netif_carrier_off(dev); + } return 0; } @@ -1146,7 +1145,6 @@ static int ath6kl_set_features(struct net_device *dev, dev->features = features | NETIF_F_RXCSUM; return err; } - } return err; diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 7126bdd4236..339d89f14d3 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -348,7 +348,7 @@ static int ath6kl_sdio_alloc_prep_scat_req(struct ath6kl_sdio *ar_sdio, int i, scat_req_sz, scat_list_sz, size; u8 *virt_buf; - scat_list_sz = (n_scat_entry - 1) * sizeof(struct hif_scatter_item); + scat_list_sz = n_scat_entry * sizeof(struct hif_scatter_item); scat_req_sz = sizeof(*s_req) + scat_list_sz; if (!virt_scat) @@ -425,8 +425,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, memcpy(tbuf, buf, len); bounced = true; - } else + } else { tbuf = buf; + } ret = ath6kl_sdio_io(ar_sdio->func, request, addr, tbuf, len); if ((request & HIF_READ) && bounced) @@ -441,9 +442,9 @@ static int ath6kl_sdio_read_write_sync(struct ath6kl *ar, u32 addr, u8 *buf, static void __ath6kl_sdio_write_async(struct ath6kl_sdio *ar_sdio, struct bus_request *req) { - if (req->scat_req) + if (req->scat_req) { ath6kl_sdio_scat_rw(ar_sdio, req); - else { + } else { void *context; int status; @@ -656,7 +657,6 @@ static void ath6kl_sdio_scatter_req_add(struct ath6kl *ar, list_add_tail(&s_req->list, &ar_sdio->scat_req); spin_unlock_bh(&ar_sdio->scat_lock); - } /* scatter gather read write request */ @@ -674,9 +674,9 @@ static int ath6kl_sdio_async_rw_scatter(struct ath6kl *ar, "hif-scatter: total len: %d scatter entries: %d\n", scat_req->len, scat_req->scat_entries); - if (request & HIF_SYNCHRONOUS) + if (request & HIF_SYNCHRONOUS) { status = ath6kl_sdio_scat_rw(ar_sdio, scat_req->busrequest); - else { + } else { spin_lock_bh(&ar_sdio->wr_async_lock); list_add_tail(&scat_req->busrequest->list, &ar_sdio->wr_asyncq); spin_unlock_bh(&ar_sdio->wr_async_lock); @@ -856,7 +856,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (ar->suspend_mode == WLAN_POWER_STATE_WOW || (!ar->suspend_mode && wow)) { - ret = ath6kl_set_sdio_pm_caps(ar); if (ret) goto cut_pwr; @@ -878,7 +877,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (ar->suspend_mode == WLAN_POWER_STATE_DEEP_SLEEP || !ar->suspend_mode || try_deepsleep) { - flags = sdio_get_host_pm_caps(func); if (!(flags & MMC_PM_KEEP_POWER)) goto cut_pwr; @@ -1061,7 +1059,6 @@ static int ath6kl_sdio_bmi_credits(struct ath6kl *ar) timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) { - /* * Hit the credit counter with a 4-byte access, the first byte * read will hit the counter and cause a decrement, while the diff --git a/drivers/net/wireless/ath/ath6kl/target.h b/drivers/net/wireless/ath/ath6kl/target.h index a580a629a0d..d5eeeae7711 100644 --- a/drivers/net/wireless/ath/ath6kl/target.h +++ b/drivers/net/wireless/ath/ath6kl/target.h @@ -289,7 +289,7 @@ struct host_interest { u32 hi_hp_rx_traffic_ratio; /* 0xd8 */ /* test applications flags */ - u32 hi_test_apps_related ; /* 0xdc */ + u32 hi_test_apps_related; /* 0xdc */ /* location of test script */ u32 hi_ota_testscript; /* 0xe0 */ /* location of CAL data */ diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index ebb24045a8a..40432fe7a5d 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -125,8 +125,9 @@ static bool ath6kl_process_uapsdq(struct ath6kl_sta *conn, *flags |= WMI_DATA_HDR_FLAGS_UAPSD; spin_unlock_bh(&conn->psq_lock); return false; - } else if (!conn->apsd_info) + } else if (!conn->apsd_info) { return false; + } if (test_bit(WMM_ENABLED, &vif->flags)) { ether_type = be16_to_cpu(datap->h_proto); @@ -316,8 +317,9 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, cookie = NULL; ath6kl_err("wmi ctrl ep full, dropping pkt : 0x%p, len:%d\n", skb, skb->len); - } else + } else { cookie = ath6kl_alloc_cookie(ar); + } if (cookie == NULL) { spin_unlock_bh(&ar->lock); @@ -359,7 +361,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) struct ath6kl_vif *vif = netdev_priv(dev); u32 map_no = 0; u16 htc_tag = ATH6KL_DATA_PKT_TAG; - u8 ac = 99 ; /* initialize to unmapped ac */ + u8 ac = 99; /* initialize to unmapped ac */ bool chk_adhoc_ps_mapping = false; int ret; struct wmi_tx_meta_v2 meta_v2; @@ -449,8 +451,9 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) if (ret) goto fail_tx; } - } else + } else { goto fail_tx; + } spin_lock_bh(&ar->lock); @@ -702,7 +705,6 @@ void ath6kl_tx_complete(struct htc_target *target, /* reap completed packets */ while (!list_empty(packet_queue)) { - packet = list_first_entry(packet_queue, struct htc_packet, list); list_del(&packet->list); @@ -1089,8 +1091,9 @@ static void aggr_deque_frms(struct aggr_info_conn *agg_conn, u8 tid, else skb_queue_tail(&rxtid->q, node->skb); node->skb = NULL; - } else + } else { stats->num_hole++; + } rxtid->seq_next = ATH6KL_NEXT_SEQ_NO(rxtid->seq_next); idx = AGGR_WIN_IDX(rxtid->seq_next, rxtid->hold_q_sz); @@ -1211,7 +1214,7 @@ static bool aggr_process_recv_frm(struct aggr_info_conn *agg_conn, u8 tid, return is_queued; spin_lock_bh(&rxtid->lock); - for (idx = 0 ; idx < rxtid->hold_q_sz; idx++) { + for (idx = 0; idx < rxtid->hold_q_sz; idx++) { if (rxtid->hold_q[idx].skb) { /* * There is a frame in the queue and no @@ -1265,7 +1268,6 @@ static void ath6kl_uapsd_trigger_frame_rx(struct ath6kl_vif *vif, is_apsdq_empty_at_start = is_apsdq_empty; while ((!is_apsdq_empty) && (num_frames_to_deliver)) { - spin_lock_bh(&conn->psq_lock); skb = skb_dequeue(&conn->apsdq); is_apsdq_empty = skb_queue_empty(&conn->apsdq); @@ -1606,16 +1608,18 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) if (!conn) return; aggr_conn = conn->aggr_conn; - } else + } else { aggr_conn = vif->aggr_cntxt->aggr_conn; + } if (aggr_process_recv_frm(aggr_conn, tid, seq_no, is_amsdu, skb)) { /* aggregation code will handle the skb */ return; } - } else if (!is_broadcast_ether_addr(datap->h_dest)) + } else if (!is_broadcast_ether_addr(datap->h_dest)) { vif->net_stats.multicast++; + } ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); } @@ -1710,8 +1714,9 @@ void aggr_recv_addba_req_evt(struct ath6kl_vif *vif, u8 tid_mux, u16 seq_no, sta = ath6kl_find_sta_by_aid(vif->ar, aid); if (sta) aggr_conn = sta->aggr_conn; - } else + } else { aggr_conn = vif->aggr_cntxt->aggr_conn; + } if (!aggr_conn) return; @@ -1766,7 +1771,6 @@ void aggr_conn_init(struct ath6kl_vif *vif, struct aggr_info *aggr_info, skb_queue_head_init(&rxtid->q); spin_lock_init(&rxtid->lock); } - } struct aggr_info *aggr_init(struct ath6kl_vif *vif) @@ -1806,8 +1810,9 @@ void aggr_recv_delba_req_evt(struct ath6kl_vif *vif, u8 tid_mux) sta = ath6kl_find_sta_by_aid(vif->ar, aid); if (sta) aggr_conn = sta->aggr_conn; - } else + } else { aggr_conn = vif->aggr_cntxt->aggr_conn; + } if (!aggr_conn) return; diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index f38ff6a6255..3afc5a463d0 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -24,7 +24,7 @@ /* constants */ #define TX_URB_COUNT 32 #define RX_URB_COUNT 32 -#define ATH6KL_USB_RX_BUFFER_SIZE 1700 +#define ATH6KL_USB_RX_BUFFER_SIZE 4096 /* tx/rx pipes for usb */ enum ATH6KL_USB_PIPE_ID { @@ -236,7 +236,6 @@ static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe) break; kfree(urb_context); } - } static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb) @@ -245,7 +244,6 @@ static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb) for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]); - } static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb, @@ -481,8 +479,8 @@ static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb) * ATH6KL_USB_RX_BUFFER_SIZE); */ - ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = - ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2; + ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = 1; + ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA], ATH6KL_USB_RX_BUFFER_SIZE); } diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 546d5da0b89..4d7f9e4712e 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -289,8 +289,9 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, ath6kl_wmi_determine_user_priority(((u8 *) llc_hdr) + sizeof(struct ath6kl_llc_snap_hdr), layer2_priority); - } else + } else { usr_pri = layer2_priority & 0x7; + } /* * Queue the EAPOL frames in the same WMM_AC_VO queue @@ -359,8 +360,9 @@ int ath6kl_wmi_dot11_hdr_remove(struct wmi *wmi, struct sk_buff *skb) hdr_size = roundup(sizeof(struct ieee80211_qos_hdr), sizeof(u32)); skb_pull(skb, hdr_size); - } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) + } else if (sub_type == cpu_to_le16(IEEE80211_STYPE_DATA)) { skb_pull(skb, sizeof(struct ieee80211_hdr_3addr)); + } datap = skb->data; llc_hdr = (struct ath6kl_llc_snap_hdr *)(datap); @@ -914,7 +916,7 @@ ath6kl_get_regpair(u16 regdmn) return NULL; for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { - if (regDomainPairs[i].regDmnEnum == regdmn) + if (regDomainPairs[i].reg_domain == regdmn) return ®DomainPairs[i]; } @@ -936,7 +938,6 @@ ath6kl_regd_find_country_by_rd(u16 regdmn) static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) { - struct ath6kl_wmi_regdomain *ev; struct country_code_to_enum_rd *country = NULL; struct reg_dmn_pair_mapping *regpair = NULL; @@ -946,15 +947,14 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) ev = (struct ath6kl_wmi_regdomain *) datap; reg_code = le32_to_cpu(ev->reg_code); - if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) + if ((reg_code >> ATH6KL_COUNTRY_RD_SHIFT) & COUNTRY_ERD_FLAG) { country = ath6kl_regd_find_country((u16) reg_code); - else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) { - + } else if (!(((u16) reg_code & WORLD_SKU_MASK) == WORLD_SKU_PREFIX)) { regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); if (regpair) ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", - regpair->regDmnEnum); + regpair->reg_domain); else ath6kl_warn("Regpair not found reg_code 0x%0x\n", reg_code); @@ -1499,7 +1499,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, if ((reply->cac_indication == CAC_INDICATION_ADMISSION_RESP) && (reply->status_code != IEEE80211_TSPEC_STATUS_ADMISS_ACCEPTED)) { - ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion); tsinfo = le16_to_cpu(ts->tsinfo); tsid = (tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) & @@ -1530,7 +1529,6 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, * for delete qos stream from AP */ else if (reply->cac_indication == CAC_INDICATION_DELETE) { - ts = (struct ieee80211_tspec_ie *) &(reply->tspec_suggestion); tsinfo = le16_to_cpu(ts->tsinfo); ts_id = ((tsinfo >> IEEE80211_WMM_IE_TSPEC_TID_SHIFT) & @@ -2322,7 +2320,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, return ret; } -int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk) +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk) { struct sk_buff *skb; struct wmi_add_krk_cmd *cmd; @@ -2479,7 +2477,6 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) goto free_data_skb; for (index = 0; index < num_pri_streams; index++) { - if (WARN_ON(!data_sync_bufs[index].skb)) goto free_data_skb; @@ -2704,7 +2701,6 @@ static void ath6kl_wmi_relinquish_implicit_pstream_credits(struct wmi *wmi) for (i = 0; i < WMM_NUM_AC; i++) { if (stream_exist & (1 << i)) { - /* * FIXME: Is this lock & unlock inside * for loop correct? may need rework. @@ -2754,9 +2750,9 @@ static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, mask->control[band].legacy << 4; /* copy mcs rate mask */ - mcsrate = mask->control[band].mcs[1]; + mcsrate = mask->control[band].ht_mcs[1]; mcsrate <<= 8; - mcsrate |= mask->control[band].mcs[0]; + mcsrate |= mask->control[band].ht_mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 28; } @@ -2806,7 +2802,7 @@ static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, mask->control[band].legacy << 4; /* copy mcs rate mask */ - mcsrate = mask->control[band].mcs[0]; + mcsrate = mask->control[band].ht_mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 20; } @@ -2870,8 +2866,9 @@ int ath6kl_wmi_set_host_sleep_mode_cmd(struct wmi *wmi, u8 if_idx, if (host_mode == ATH6KL_HOST_MODE_ASLEEP) { ath6kl_wmi_relinquish_implicit_pstream_credits(wmi); cmd->asleep = cpu_to_le32(1); - } else + } else { cmd->awake = cpu_to_le32(1); + } ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HOST_SLEEP_MODE_CMDID, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index b5f226503ba..bb23fc00111 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -898,7 +898,6 @@ struct wmi_start_scan_cmd { * flags here */ enum wmi_scan_ctrl_flags_bits { - /* set if can scan in the connect cmd */ CONNECT_SCAN_CTRL_FLAGS = 0x01, @@ -1068,7 +1067,7 @@ struct wmi_power_mode_cmd { } __packed; /* - * Policy to determnine whether power save failure event should be sent to + * Policy to determine whether power save failure event should be sent to * host during scanning */ enum power_save_fail_event_policy { @@ -2617,7 +2616,7 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, u8 *key_material, u8 key_op_ctrl, u8 *mac_addr, enum wmi_sync_flag sync_flag); -int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, u8 *krk); +int ath6kl_wmi_add_krk_cmd(struct wmi *wmi, u8 if_idx, const u8 *krk); int ath6kl_wmi_deletekey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index); int ath6kl_wmi_setpmkid_cmd(struct wmi *wmi, u8 if_idx, const u8 *bssid, const u8 *pmkid, bool set); diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 7944c25c9a4..8fcc029a76a 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -65,6 +65,14 @@ config ATH9K_DEBUGFS Also required for changing debug message flags at run time. +config ATH9K_STATION_STATISTICS + bool "Detailed station statistics" + depends on ATH9K && ATH9K_DEBUGFS && DEBUG_FS + select MAC80211_DEBUGFS + default n + ---help--- + This option enables detailed statistics for association stations. + config ATH9K_DFS_CERTIFIED bool "Atheros DFS support for certified platforms" depends on ATH9K && CFG80211_CERTIFICATION_ONUS @@ -84,17 +92,33 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. -config ATH9K_LEGACY_RATE_CONTROL - bool "Atheros ath9k rate control" - depends on ATH9K +config ATH9K_TX99 + bool "Atheros ath9k TX99 testing support" + depends on ATH9K_DEBUGFS && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems undergoing + certification testing and evaluation in a controlled environment. + Enabling this will only enable TX99 support, all other modes of + operation will be disabled. + + TX99 support enables Specific Absorption Rate (SAR) testing. + SAR is the unit of measurement for the amount of radio frequency(RF) + absorbed by the body when using a wireless device. The RF exposure + limits used are expressed in the terms of SAR, which is a measure + of the electric and magnetic field strength and power density for + transmitters operating at frequencies from 300 kHz to 100 GHz. + Regulatory bodies around the world require that wireless device + be evaluated to meet the RF exposure limits set forth in the + governmental SAR regulations. + +config ATH9K_WOW + bool "Wake on Wireless LAN support (EXPERIMENTAL)" + depends on ATH9K && PM default n ---help--- - Say Y, if you want to use the ath9k specific rate control - module instead of minstrel_ht. Be warned that there are various - issues with the ath9k RC and minstrel is a more robust algorithm. - Note that even if this option is selected, "ath9k_rate_control" - has to be passed to mac80211 using the module parameter, - ieee80211_default_rc_algo. + This option enables Wake on Wireless LAN support for certain cards. + Currently, AR9462 is supported. config ATH9K_RFKILL bool "Atheros ath9k rfkill support" if EXPERT diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 75ee9e7704c..8fcd586d1c3 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -8,16 +8,17 @@ ath9k-y += beacon.o \ antenna.o ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o -ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o ath9k-$(CONFIG_ATH9K_AHB) += ahb.o -ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o -ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ - dfs.o \ - dfs_pattern_detector.o \ - dfs_pri_detector.o -ath9k-$(CONFIG_PM_SLEEP) += wow.o +ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o +ath9k-$(CONFIG_ATH9K_TX99) += tx99.o +ath9k-$(CONFIG_ATH9K_WOW) += wow.o + +ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o \ + spectral.o + +ath9k-$(CONFIG_ATH9K_STATION_STATISTICS) += debug_sta.o obj-$(CONFIG_ATH9K) += ath9k.o @@ -43,12 +44,17 @@ ath9k_hw-y:= \ ar9003_eeprom.o \ ar9003_paprd.o +ath9k_hw-$(CONFIG_ATH9K_WOW) += ar9003_wow.o + ath9k_hw-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += btcoex.o \ ar9003_mci.o obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o -ath9k_common-y:= common.o +ath9k_common-y:= common.o \ + common-init.o \ + common-beacon.o \ + common-debug.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 072e4b53106..be3eb2a8d60 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, + }, {}, }; @@ -54,7 +58,7 @@ static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) struct platform_device *pdev = to_platform_device(sc->dev); struct ath9k_platform_data *pdata; - pdata = (struct ath9k_platform_data *) pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { ath_err(common, "%s: flash read failed, offset %08x is out of range\n", @@ -84,7 +88,7 @@ static int ath_ahb_probe(struct platform_device *pdev) struct ath_hw *ah; char hw_name[64]; - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data specified\n"); return -EINVAL; } @@ -124,9 +128,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"); diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index be466b0ef7a..ba502a2d199 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -155,6 +155,9 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, ATH9K_ANI_RSSI_THR_LOW, ATH9K_ANI_RSSI_THR_HIGH); + if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_OFDM_DEF_LEVEL) + immunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; + if (!scan) aniState->ofdmNoiseImmunityLevel = immunityLevel; @@ -176,16 +179,26 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel, if (ah->opmode == NL80211_IFTYPE_STATION && BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH) weak_sig = true; - /* - * OFDM Weak signal detection is always enabled for AP mode. + * Newer chipsets are better at dealing with high PHY error counts - + * keep weak signal detection enabled when no RSSI threshold is + * available to determine if it is needed (mode != STA) */ - if (ah->opmode != NL80211_IFTYPE_AP && - aniState->ofdmWeakSigDetect != weak_sig) { - ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - entry_ofdm->ofdm_weak_signal_on); - } + else if (AR_SREV_9300_20_OR_LATER(ah) && + ah->opmode != NL80211_IFTYPE_STATION) + weak_sig = true; + + /* Older chipsets are more sensitive to high PHY error counts */ + else if (!AR_SREV_9300_20_OR_LATER(ah) && + aniState->ofdmNoiseImmunityLevel >= 8) + weak_sig = false; + + if (aniState->ofdmWeakSigDetect != weak_sig) + ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, + weak_sig); + + if (!AR_SREV_9300_20_OR_LATER(ah)) + return; if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) { ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; @@ -225,6 +238,9 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, BEACON_RSSI(ah), ATH9K_ANI_RSSI_THR_LOW, ATH9K_ANI_RSSI_THR_HIGH); + if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_CCK_DEF_LEVEL) + immunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; + if (ah->opmode == NL80211_IFTYPE_STATION && BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW && immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) @@ -308,17 +324,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, @@ -338,10 +343,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->cckNoiseImmunityLevel != ATH9K_ANI_CCK_DEF_LEVEL) { ath_dbg(common, ANI, - "Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", + "Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, - chan->channelFlags, is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); @@ -354,10 +358,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) * restore historical levels for this channel */ ath_dbg(common, ANI, - "Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", + "Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, - chan->channelFlags, is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); @@ -485,10 +488,17 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ath_dbg(common, ANI, "Initialize ANI\n"); - ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; - ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; - ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH; - ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW; + if (AR_SREV_9300_20_OR_LATER(ah)) { + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW; + ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH; + ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW; + } else { + ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD; + ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD; + ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD; + ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD; + } ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL; ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL; diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 21e7b83c3f6..c40965b4c1e 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -22,12 +22,16 @@ /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_HIGH 3500 #define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000 +#define ATH9K_ANI_OFDM_TRIG_HIGH_OLD 500 #define ATH9K_ANI_OFDM_TRIG_LOW 400 #define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900 +#define ATH9K_ANI_OFDM_TRIG_LOW_OLD 200 #define ATH9K_ANI_CCK_TRIG_HIGH 600 +#define ATH9K_ANI_CCK_TRIG_HIGH_OLD 200 #define ATH9K_ANI_CCK_TRIG_LOW 300 +#define ATH9K_ANI_CCK_TRIG_LOW_OLD 100 #define ATH9K_ANI_SPUR_IMMUNE_LVL 3 #define ATH9K_ANI_FIRSTEP_LVL 2 diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index dd1cc73d794..a3668433dc0 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -332,7 +332,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, } if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) + div_ant_conf->lna1_lna2_switch_delta) div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; else div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; @@ -554,42 +554,22 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; break; case 0x10: /* LNA2 A-B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x1; - } + ant_conf->fast_div_bias = 0x2; break; case 0x12: /* LNA2 LNA1 */ - ant_conf->fast_div_bias = 0x39; + ant_conf->fast_div_bias = 0x3f; break; case 0x13: /* LNA2 A+B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x1; - } + ant_conf->fast_div_bias = 0x2; break; case 0x20: /* LNA1 A-B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x4; - } + ant_conf->fast_div_bias = 0x3; break; case 0x21: /* LNA1 LNA2 */ - ant_conf->fast_div_bias = 0x6; + ant_conf->fast_div_bias = 0x3; break; case 0x23: /* LNA1 A+B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x6; - } + ant_conf->fast_div_bias = 0x3; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; @@ -638,7 +618,7 @@ static void ath_ant_try_scan(struct ath_ant_comb *antcomb, antcomb->rssi_sub = alt_rssi_avg; antcomb->scan = false; if (antcomb->rssi_lna2 > - (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { + (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) { /* use LNA2 as main LNA */ if ((antcomb->rssi_add > antcomb->rssi_lna1) && (antcomb->rssi_add > antcomb->rssi_sub)) { @@ -744,14 +724,14 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) struct ath_ant_comb *antcomb = &sc->ant_comb; int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; int curr_main_set; - int main_rssi = rs->rs_rssi_ctl0; - int alt_rssi = rs->rs_rssi_ctl1; + int main_rssi = rs->rs_rssi_ctl[0]; + int alt_rssi = rs->rs_rssi_ctl[1]; int rx_ant_conf, main_ant_conf; bool short_scan = false, ret; - rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & + rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) & ATH_ANT_RX_MASK; - main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & + main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) & ATH_ANT_RX_MASK; if (alt_rssi >= antcomb->low_rssi_thresh) { diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 08656473c63..00fb8badbac 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 */ @@ -626,12 +622,11 @@ static void ar5008_hw_override_ini(struct ath_hw *ah, if (AR_SREV_9287_11_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); + val |= AR_PCU_MISC_MODE2_CFP_IGNORE; + REG_WRITE(ah, AR_PCU_MISC_MODE2, val); } - REG_SET_BIT(ah, AR_PHY_CCK_DETECT, - AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); - if (AR_SREV_9280_20_OR_LATER(ah)) return; /* @@ -667,14 +662,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah, if (IS_CHAN_HT40(chan)) { phymode |= AR_PHY_FC_DYN2040_EN; - if ((chan->chanmode == CHANNEL_A_HT40PLUS) || - (chan->chanmode == CHANNEL_G_HT40PLUS)) + if (IS_CHAN_HT40PLUS(chan)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; } REG_WRITE(ah, AR_PHY_TURBO, phymode); - ath9k_hw_set11nmac2040(ah); + ath9k_hw_set11nmac2040(ah, chan); ENABLE_REGWRITE_BUFFER(ah); @@ -692,31 +686,12 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, int i, regWrites = 0; u32 modesIndex, freqIndex; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; + if (IS_CHAN_5GHZ(chan)) { freqIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - freqIndex = 1; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - freqIndex = 2; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + } else { freqIndex = 2; - break; - - default: - return -EINVAL; + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; } /* @@ -815,8 +790,10 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan) if (chan == NULL) return; - rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) - ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + if (IS_CHAN_2GHZ(chan)) + rfMode |= AR_PHY_MODE_DYNAMIC; + else + rfMode |= AR_PHY_MODE_OFDM; if (!AR_SREV_9280_20_OR_LATER(ah)) rfMode |= (IS_CHAN_5GHZ(chan)) ? @@ -940,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:{ @@ -1027,42 +1004,9 @@ 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; 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; - - REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW, - AR_PHY_FIND_SIG_FIRSTEP_LOW, value2); + AR_PHY_FIND_SIG_FIRSTEP, value); if (level != aniState->firstepLevel) { ath_dbg(common, ANI, @@ -1079,7 +1023,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++; @@ -1092,41 +1036,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, @@ -1143,7 +1059,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++; @@ -1219,12 +1135,11 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) iniDef = &aniState->iniDef; - ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", + ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, - chan->channel, - chan->channelFlags); + chan->channel); val = REG_READ(ah, AR_PHY_SFCORR); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index 9f589744a9f..cdc74005650 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -33,15 +33,12 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah, bool supported = false; switch (ah->supp_cals & cal_type) { case IQ_MISMATCH_CAL: - /* Run IQ Mismatch for non-CCK only */ - if (!IS_CHAN_B(chan)) - supported = true; + supported = true; break; case ADC_GAIN_CAL: case ADC_DC_CAL: /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */ - if (!IS_CHAN_B(chan) && - !((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) && + if (!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) && IS_CHAN_HT20(chan))) supported = true; break; @@ -671,7 +668,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF); if (ah->caldata) - nfcal_pending = ah->caldata->nfcal_pending; + nfcal_pending = test_bit(NFCAL_PENDING, &ah->caldata->cal_flags); if (currCal && !nfcal && (currCal->calState == CAL_RUNNING || @@ -861,7 +858,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) ar9002_hw_pa_cal(ah, true); if (ah->caldata) - ah->caldata->nfcal_pending = true; + set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index fb61b081d17..d480d2f3e18 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -32,12 +32,8 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah) return 0; } - if (ah->config.pcie_clock_req) - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9280PciePhy_clkreq_off_L1_9280); - else - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9280PciePhy_clkreq_always_on_L1_9280); + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9280PciePhy_clkreq_always_on_L1_9280); if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1); @@ -387,6 +383,20 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah) } } +static void ar9002_hw_init_hang_checks(struct ath_hw *ah) +{ + if (AR_SREV_9100(ah) || AR_SREV_9160(ah)) { + ah->config.hw_hang_checks |= HW_BB_RIFS_HANG; + ah->config.hw_hang_checks |= HW_BB_DFS_HANG; + } + + if (AR_SREV_9280(ah)) + ah->config.hw_hang_checks |= HW_BB_RX_CLEAR_STUCK_HANG; + + if (AR_SREV_5416(ah) || AR_SREV_9100(ah) || AR_SREV_9160(ah)) + ah->config.hw_hang_checks |= HW_MAC_HANG; +} + /* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */ int ar9002_hw_attach_ops(struct ath_hw *ah) { @@ -399,6 +409,7 @@ int ar9002_hw_attach_ops(struct ath_hw *ah) return ret; priv_ops->init_mode_gain_regs = ar9002_hw_init_mode_gain_regs; + priv_ops->init_hang_checks = ar9002_hw_init_hang_checks; ops->config_pci_powersave = ar9002_hw_configpcipowersave; @@ -419,28 +430,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) u32 modesIndex; int i; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; - break; - - default: - return; - } + if (IS_CHAN_5GHZ(chan)) + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + else + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; ENABLE_REGWRITE_BUFFER(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 8d78253c26c..741b38ddcb3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -29,7 +29,8 @@ static void ar9002_hw_set_desc_link(void *ds, u32 ds_link) ((struct ath_desc*) ds)->ds_link = ds_link; } -static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) +static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p) { u32 isr = 0; u32 mask2 = 0; @@ -76,9 +77,16 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) mask2 |= ATH9K_INT_CST; if (isr2 & AR_ISR_S2_TSFOOR) mask2 |= ATH9K_INT_TSFOOR; + + if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { + REG_WRITE(ah, AR_ISR_S2, isr2); + isr &= ~AR_ISR_BCNMISC; + } } - isr = REG_READ(ah, AR_ISR_RAC); + if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) + isr = REG_READ(ah, AR_ISR_RAC); + if (isr == 0xffffffff) { *masked = 0; return false; @@ -97,11 +105,23 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) *masked |= ATH9K_INT_TX; - s0_s = REG_READ(ah, AR_ISR_S0_S); + if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) { + s0_s = REG_READ(ah, AR_ISR_S0_S); + s1_s = REG_READ(ah, AR_ISR_S1_S); + } else { + s0_s = REG_READ(ah, AR_ISR_S0); + REG_WRITE(ah, AR_ISR_S0, s0_s); + s1_s = REG_READ(ah, AR_ISR_S1); + REG_WRITE(ah, AR_ISR_S1, s1_s); + + isr &= ~(AR_ISR_TXOK | + AR_ISR_TXDESC | + AR_ISR_TXERR | + AR_ISR_TXEOL); + } + ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK); ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC); - - s1_s = REG_READ(ah, AR_ISR_S1_S); ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR); ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL); } @@ -114,13 +134,15 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) *masked |= mask2; } - if (AR_SREV_9100(ah)) - return true; - - if (isr & AR_ISR_GENTMR) { + if (!AR_SREV_9100(ah) && (isr & AR_ISR_GENTMR)) { u32 s5_s; - s5_s = REG_READ(ah, AR_ISR_S5_S); + if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) { + s5_s = REG_READ(ah, AR_ISR_S5_S); + } else { + s5_s = REG_READ(ah, AR_ISR_S5); + } + ah->intr_gen_timer_trigger = MS(s5_s, AR_ISR_S5_GENTIMER_TRIG); @@ -133,10 +155,24 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) if ((s5_s & AR_ISR_S5_TIM_TIMER) && !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) *masked |= ATH9K_INT_TIM_TIMER; + + if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { + REG_WRITE(ah, AR_ISR_S5, s5_s); + isr &= ~AR_ISR_GENTMR; + } } + if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) { + REG_WRITE(ah, AR_ISR, isr); + REG_READ(ah, AR_ISR); + } + + if (AR_SREV_9100(ah)) + return true; + if (sync_cause) { - ath9k_debug_sync_cause(common, sync_cause); + if (sync_cause_p) + *sync_cause_p = sync_cause; fatal_int = (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 1fc1fa955d4..9a2afa2c690 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -201,7 +201,6 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah, ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; - ah->config.spurmode = SPUR_ENABLE_EEPROM; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); @@ -485,7 +484,7 @@ static void ar9002_hw_do_getnf(struct ath_hw *ah, if (IS_CHAN_HT40(ah->curchan)) nfarray[3] = sign_extend32(nf, 8); - if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) + if (!(ah->rxchainmask & BIT(1))) return; nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); @@ -532,6 +531,7 @@ static void ar9002_hw_antdiv_comb_conf_get(struct ath_hw *ah, AR_PHY_9285_ANT_DIV_ALT_LNACONF_S; antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >> AR_PHY_9285_FAST_DIV_BIAS_S; + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -3; antconf->div_group = 0; } @@ -679,6 +679,26 @@ static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9002_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_D_FPCTL, 0x10|qnum); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9002_hw_tx99_stop(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + void ar9002_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -700,6 +720,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9002_hw_tx99_start; + ops->tx99_stop = ar9002_hw_tx99_stop; ar9002_hw_set_nf_limits(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h index 7546b9a7dcb..c38399bc9aa 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -303,7 +303,7 @@ static const u32 ar9300_2p2_mac_postamble[][5] = { {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, + {0x00008120, 0x18f04800, 0x18f04800, 0x18f04810, 0x18f04810}, {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; @@ -352,7 +352,7 @@ static const u32 ar9300_2p2_baseband_postamble[][5] = { {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, + {0x0000a2d0, 0x00041983, 0x00041983, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, @@ -378,9 +378,9 @@ static const u32 ar9300_2p2_baseband_core[][2] = { {0x00009814, 0x9280c00a}, {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, - {0x00009834, 0x6400a290}, + {0x00009834, 0x6400a190}, {0x00009838, 0x0108ecff}, - {0x0000983c, 0x0d000600}, + {0x0000983c, 0x14000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, @@ -401,7 +401,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = { {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, - {0x00009d10, 0x01834061}, + {0x00009d10, 0x01884061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, @@ -410,7 +410,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = { {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x0d261820}, + {0x00009e40, 0x0d261800}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0x00000000}, @@ -459,7 +459,7 @@ static const u32 ar9300_2p2_baseband_core[][2] = { {0x0000a3e8, 0x20202020}, {0x0000a3ec, 0x20202020}, {0x0000a3f0, 0x00000000}, - {0x0000a3f4, 0x00000246}, + {0x0000a3f4, 0x00000000}, {0x0000a3f8, 0x0c9bd380}, {0x0000a3fc, 0x000f0f01}, {0x0000a400, 0x8fa91f01}, @@ -534,107 +534,107 @@ static const u32 ar9300_2p2_baseband_core[][2] = { static const u32 ar9300Modes_high_power_tx_gain_table_2p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, - {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, - {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, - {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, - {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, - {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, - {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, - {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, - {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, - {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, - {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, - {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, - {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, - {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, - {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, - {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, - {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, - {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, - {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, - {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, - {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, - {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, - {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, - {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, - {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, - {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, - {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, - {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, - {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, - {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, - {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, - {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, - {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, - {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, - {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, - {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, - {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, - {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, - {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, - {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, - {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, - {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, - {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400}, + {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402}, + {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404}, + {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640}, + {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x5e08442e, 0x5e08442e, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x620a4431, 0x620a4431, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, + {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, + {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, + {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, + {0x0000a590, 0x15800028, 0x15800028, 0x0f800202, 0x0f800202}, + {0x0000a594, 0x1b80002b, 0x1b80002b, 0x12800400, 0x12800400}, + {0x0000a598, 0x1f820028, 0x1f820028, 0x16800402, 0x16800402}, + {0x0000a59c, 0x2582002b, 0x2582002b, 0x19800404, 0x19800404}, + {0x0000a5a0, 0x2a84002a, 0x2a84002a, 0x1c800603, 0x1c800603}, + {0x0000a5a4, 0x2e86002a, 0x2e86002a, 0x21800a02, 0x21800a02}, + {0x0000a5a8, 0x3382202d, 0x3382202d, 0x25800a04, 0x25800a04}, + {0x0000a5ac, 0x3884202c, 0x3884202c, 0x28800a20, 0x28800a20}, + {0x0000a5b0, 0x3c86202c, 0x3c86202c, 0x2c800e20, 0x2c800e20}, + {0x0000a5b4, 0x4188202d, 0x4188202d, 0x30800e22, 0x30800e22}, + {0x0000a5b8, 0x4586402d, 0x4586402d, 0x34800e24, 0x34800e24}, + {0x0000a5bc, 0x4986222d, 0x4986222d, 0x38801640, 0x38801640}, + {0x0000a5c0, 0x4d862231, 0x4d862231, 0x3c801660, 0x3c801660}, + {0x0000a5c4, 0x50882231, 0x50882231, 0x3f801861, 0x3f801861}, + {0x0000a5c8, 0x5688422e, 0x5688422e, 0x43801a81, 0x43801a81}, + {0x0000a5cc, 0x5e88442e, 0x5e88442e, 0x47801a83, 0x47801a83}, + {0x0000a5d0, 0x628a4431, 0x628a4431, 0x4a801c84, 0x4a801c84}, + {0x0000a5d4, 0x648a4432, 0x648a4432, 0x4e801ce3, 0x4e801ce3}, + {0x0000a5d8, 0x688a4434, 0x688a4434, 0x52801ce5, 0x52801ce5}, + {0x0000a5dc, 0x6c8a6434, 0x6c8a6434, 0x56801ce9, 0x56801ce9}, + {0x0000a5e0, 0x6f8a6633, 0x6f8a6633, 0x5a801ceb, 0x5a801ceb}, + {0x0000a5e4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5e8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5ec, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5f0, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5f4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5f8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5fc, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, - {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, - {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, - {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, - {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000}, + {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501}, + {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501}, + {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, + {0x00016048, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, + {0x00016448, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, - {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, + {0x00016848, 0x61200001, 0x61200001, 0x66480001, 0x66480001}, {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; @@ -644,7 +644,7 @@ static const u32 ar9300Modes_high_ob_db_tx_gain_table_2p2[][5] = { {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, + {0x0000a410, 0x000050d4, 0x000050d4, 0x000050d9, 0x000050d9}, {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, @@ -1086,8 +1086,8 @@ static const u32 ar9300Common_rx_gain_table_2p2[][2] = { {0x0000b074, 0x00000000}, {0x0000b078, 0x00000000}, {0x0000b07c, 0x00000000}, - {0x0000b080, 0x2a2d2f32}, - {0x0000b084, 0x21232328}, + {0x0000b080, 0x23232323}, + {0x0000b084, 0x21232323}, {0x0000b088, 0x19191c1e}, {0x0000b08c, 0x12141417}, {0x0000b090, 0x07070e0e}, @@ -1385,9 +1385,9 @@ static const u32 ar9300_2p2_mac_core[][2] = { {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, - {0x00008244, 0x0010f424}, + {0x00008244, 0x0010f400}, {0x00008248, 0x00000800}, - {0x0000824c, 0x0001e848}, + {0x0000824c, 0x0001e800}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, @@ -1726,16 +1726,30 @@ static const u32 ar9300PciePhy_pll_on_clkreq_disable_L1_2p2[][2] = { static const u32 ar9300PciePhy_clkreq_enable_L1_2p2[][2] = { /* Addr allmodes */ - {0x00004040, 0x08253e5e}, + {0x00004040, 0x0825365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; static const u32 ar9300PciePhy_clkreq_disable_L1_2p2[][2] = { /* Addr allmodes */ - {0x00004040, 0x08213e5e}, + {0x00004040, 0x0821365e}, {0x00004040, 0x0008003b}, {0x00004044, 0x00000000}, }; +static const u32 ar9300_2p2_baseband_core_txfir_coeff_japan_2484[][2] = { + /* Addr allmodes */ + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x6f7f0301}, + {0x0000a3a0, 0xca9228ee}, +}; + +static const u32 ar9300_2p2_baseband_postamble_dfs_channel[][3] = { + /* Addr 5G 2G */ + {0x00009824, 0x5ac668d0, 0x5ac668d0}, + {0x00009e0c, 0x6d4000e2, 0x6d4000e2}, + {0x00009e14, 0x37b9625e, 0x37b9625e}, +}; + #endif /* INITVALS_9003_2P2_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_buffalo_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_buffalo_initvals.h new file mode 100644 index 00000000000..59cf738f70d --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_buffalo_initvals.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INITVALS_9003_BUFFALO_H +#define INITVALS_9003_BUFFALO_H + +static const u32 ar9300Modes_high_power_tx_gain_table_buffalo[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d8, 0x000050d8, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, + {0x0000a504, 0x04002222, 0x04002222, 0x04000002, 0x04000002}, + {0x0000a508, 0x09002421, 0x09002421, 0x08000004, 0x08000004}, + {0x0000a50c, 0x0d002621, 0x0d002621, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x13004620, 0x13004620, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x19004a20, 0x19004a20, 0x11000400, 0x11000400}, + {0x0000a518, 0x1d004e20, 0x1d004e20, 0x15000402, 0x15000402}, + {0x0000a51c, 0x21005420, 0x21005420, 0x19000404, 0x19000404}, + {0x0000a520, 0x26005e20, 0x26005e20, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x2b005e40, 0x2b005e40, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x2f005e42, 0x2f005e42, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x33005e44, 0x33005e44, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x38005e65, 0x38005e65, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x3c005e69, 0x3c005e69, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x40005e6b, 0x40005e6b, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x44005e6d, 0x44005e6d, 0x34001640, 0x34001640}, + {0x0000a540, 0x49005e72, 0x49005e72, 0x38001660, 0x38001660}, + {0x0000a544, 0x4e005eb2, 0x4e005eb2, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x53005f12, 0x53005f12, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x59025eb2, 0x59025eb2, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x5e025f12, 0x5e025f12, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x61027f12, 0x61027f12, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x6702bf12, 0x6702bf12, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x6b02bf14, 0x6b02bf14, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x6f02bf16, 0x6f02bf16, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x6f02bf16, 0x6f02bf16, 0x56001eec, 0x56001eec}, + {0x0000a580, 0x00802220, 0x00802220, 0x00800000, 0x00800000}, + {0x0000a584, 0x04802222, 0x04802222, 0x04800002, 0x04800002}, + {0x0000a588, 0x09802421, 0x09802421, 0x08800004, 0x08800004}, + {0x0000a58c, 0x0d802621, 0x0d802621, 0x0b800200, 0x0b800200}, + {0x0000a590, 0x13804620, 0x13804620, 0x0f800202, 0x0f800202}, + {0x0000a594, 0x19804a20, 0x19804a20, 0x11800400, 0x11800400}, + {0x0000a598, 0x1d804e20, 0x1d804e20, 0x15800402, 0x15800402}, + {0x0000a59c, 0x21805420, 0x21805420, 0x19800404, 0x19800404}, + {0x0000a5a0, 0x26805e20, 0x26805e20, 0x1b800603, 0x1b800603}, + {0x0000a5a4, 0x2b805e40, 0x2b805e40, 0x1f800a02, 0x1f800a02}, + {0x0000a5a8, 0x2f805e42, 0x2f805e42, 0x23800a04, 0x23800a04}, + {0x0000a5ac, 0x33805e44, 0x33805e44, 0x26800a20, 0x26800a20}, + {0x0000a5b0, 0x38805e65, 0x38805e65, 0x2a800e20, 0x2a800e20}, + {0x0000a5b4, 0x3c805e69, 0x3c805e69, 0x2e800e22, 0x2e800e22}, + {0x0000a5b8, 0x40805e6b, 0x40805e6b, 0x31800e24, 0x31800e24}, + {0x0000a5bc, 0x44805e6d, 0x44805e6d, 0x34801640, 0x34801640}, + {0x0000a5c0, 0x49805e72, 0x49805e72, 0x38801660, 0x38801660}, + {0x0000a5c4, 0x4e805eb2, 0x4e805eb2, 0x3b801861, 0x3b801861}, + {0x0000a5c8, 0x53805f12, 0x53805f12, 0x3e801a81, 0x3e801a81}, + {0x0000a5cc, 0x59825eb2, 0x59825eb2, 0x42801a83, 0x42801a83}, + {0x0000a5d0, 0x5e825f12, 0x5e825f12, 0x44801c84, 0x44801c84}, + {0x0000a5d4, 0x61827f12, 0x61827f12, 0x48801ce3, 0x48801ce3}, + {0x0000a5d8, 0x6782bf12, 0x6782bf12, 0x4c801ce5, 0x4c801ce5}, + {0x0000a5dc, 0x6b82bf14, 0x6b82bf14, 0x50801ce9, 0x50801ce9}, + {0x0000a5e0, 0x6f82bf16, 0x6f82bf16, 0x54801ceb, 0x54801ceb}, + {0x0000a5e4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5e8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5ec, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5f0, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5f4, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5f8, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a5fc, 0x6f82bf16, 0x6f82bf16, 0x56801eec, 0x56801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, + {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, + {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, + {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, + {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000c2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016048, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, + {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016448, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, + {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016848, 0x66480001, 0x66480001, 0x66480001, 0x66480001}, + {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, +}; + +#endif /* INITVALS_9003_BUFFALO_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 6988e1d081f..ac8301ef524 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -23,10 +23,11 @@ #define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT #define MAX_MAG_DELTA 11 #define MAX_PHS_DELTA 10 +#define MAXIQCAL 3 struct coeff { - int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT]; - int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT]; + int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; + int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; int iqc_coeff[2]; }; @@ -326,6 +327,224 @@ static void ar9003_hw_init_cal_settings(struct ath_hw *ah) ah->supp_cals = IQ_MISMATCH_CAL; } +#define OFF_UPPER_LT 24 +#define OFF_LOWER_LT 7 + +static bool ar9003_hw_dynamic_osdac_selection(struct ath_hw *ah, + bool txiqcal_done) +{ + struct ath_common *common = ath9k_hw_common(ah); + int ch0_done, osdac_ch0, dc_off_ch0_i1, dc_off_ch0_q1, dc_off_ch0_i2, + dc_off_ch0_q2, dc_off_ch0_i3, dc_off_ch0_q3; + int ch1_done, osdac_ch1, dc_off_ch1_i1, dc_off_ch1_q1, dc_off_ch1_i2, + dc_off_ch1_q2, dc_off_ch1_i3, dc_off_ch1_q3; + int ch2_done, osdac_ch2, dc_off_ch2_i1, dc_off_ch2_q1, dc_off_ch2_i2, + dc_off_ch2_q2, dc_off_ch2_i3, dc_off_ch2_q3; + bool status; + u32 temp, val; + + /* + * Clear offset and IQ calibration, run AGC cal. + */ + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_OFFSET_CAL); + REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); + + status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, + 0, AH_WAIT_TIMEOUT); + if (!status) { + ath_dbg(common, CALIBRATE, + "AGC cal without offset cal failed to complete in 1ms"); + return false; + } + + /* + * Allow only offset calibration and disable the others + * (Carrier Leak calibration, TX Filter calibration and + * Peak Detector offset calibration). + */ + REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_OFFSET_CAL); + REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, + AR_PHY_CL_CAL_ENABLE); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_FLTR_CAL); + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_PKDET_CAL); + + ch0_done = 0; + ch1_done = 0; + ch2_done = 0; + + while ((ch0_done == 0) || (ch1_done == 0) || (ch2_done == 0)) { + osdac_ch0 = (REG_READ(ah, AR_PHY_65NM_CH0_BB1) >> 30) & 0x3; + osdac_ch1 = (REG_READ(ah, AR_PHY_65NM_CH1_BB1) >> 30) & 0x3; + osdac_ch2 = (REG_READ(ah, AR_PHY_65NM_CH2_BB1) >> 30) & 0x3; + + REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); + + status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, + 0, AH_WAIT_TIMEOUT); + if (!status) { + ath_dbg(common, CALIBRATE, + "DC offset cal failed to complete in 1ms"); + return false; + } + + REG_CLR_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + /* + * High gain. + */ + REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (1 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (1 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (1 << 8))); + + temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); + dc_off_ch0_i1 = (temp >> 26) & 0x1f; + dc_off_ch0_q1 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); + dc_off_ch1_i1 = (temp >> 26) & 0x1f; + dc_off_ch1_q1 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); + dc_off_ch2_i1 = (temp >> 26) & 0x1f; + dc_off_ch2_q1 = (temp >> 21) & 0x1f; + + /* + * Low gain. + */ + REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (2 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (2 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (2 << 8))); + + temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); + dc_off_ch0_i2 = (temp >> 26) & 0x1f; + dc_off_ch0_q2 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); + dc_off_ch1_i2 = (temp >> 26) & 0x1f; + dc_off_ch1_q2 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); + dc_off_ch2_i2 = (temp >> 26) & 0x1f; + dc_off_ch2_q2 = (temp >> 21) & 0x1f; + + /* + * Loopback. + */ + REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (3 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (3 << 8))); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, + ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (3 << 8))); + + temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); + dc_off_ch0_i3 = (temp >> 26) & 0x1f; + dc_off_ch0_q3 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); + dc_off_ch1_i3 = (temp >> 26) & 0x1f; + dc_off_ch1_q3 = (temp >> 21) & 0x1f; + + temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); + dc_off_ch2_i3 = (temp >> 26) & 0x1f; + dc_off_ch2_q3 = (temp >> 21) & 0x1f; + + if ((dc_off_ch0_i1 > OFF_UPPER_LT) || (dc_off_ch0_i1 < OFF_LOWER_LT) || + (dc_off_ch0_i2 > OFF_UPPER_LT) || (dc_off_ch0_i2 < OFF_LOWER_LT) || + (dc_off_ch0_i3 > OFF_UPPER_LT) || (dc_off_ch0_i3 < OFF_LOWER_LT) || + (dc_off_ch0_q1 > OFF_UPPER_LT) || (dc_off_ch0_q1 < OFF_LOWER_LT) || + (dc_off_ch0_q2 > OFF_UPPER_LT) || (dc_off_ch0_q2 < OFF_LOWER_LT) || + (dc_off_ch0_q3 > OFF_UPPER_LT) || (dc_off_ch0_q3 < OFF_LOWER_LT)) { + if (osdac_ch0 == 3) { + ch0_done = 1; + } else { + osdac_ch0++; + + val = REG_READ(ah, AR_PHY_65NM_CH0_BB1) & 0x3fffffff; + val |= (osdac_ch0 << 30); + REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, val); + + ch0_done = 0; + } + } else { + ch0_done = 1; + } + + if ((dc_off_ch1_i1 > OFF_UPPER_LT) || (dc_off_ch1_i1 < OFF_LOWER_LT) || + (dc_off_ch1_i2 > OFF_UPPER_LT) || (dc_off_ch1_i2 < OFF_LOWER_LT) || + (dc_off_ch1_i3 > OFF_UPPER_LT) || (dc_off_ch1_i3 < OFF_LOWER_LT) || + (dc_off_ch1_q1 > OFF_UPPER_LT) || (dc_off_ch1_q1 < OFF_LOWER_LT) || + (dc_off_ch1_q2 > OFF_UPPER_LT) || (dc_off_ch1_q2 < OFF_LOWER_LT) || + (dc_off_ch1_q3 > OFF_UPPER_LT) || (dc_off_ch1_q3 < OFF_LOWER_LT)) { + if (osdac_ch1 == 3) { + ch1_done = 1; + } else { + osdac_ch1++; + + val = REG_READ(ah, AR_PHY_65NM_CH1_BB1) & 0x3fffffff; + val |= (osdac_ch1 << 30); + REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, val); + + ch1_done = 0; + } + } else { + ch1_done = 1; + } + + if ((dc_off_ch2_i1 > OFF_UPPER_LT) || (dc_off_ch2_i1 < OFF_LOWER_LT) || + (dc_off_ch2_i2 > OFF_UPPER_LT) || (dc_off_ch2_i2 < OFF_LOWER_LT) || + (dc_off_ch2_i3 > OFF_UPPER_LT) || (dc_off_ch2_i3 < OFF_LOWER_LT) || + (dc_off_ch2_q1 > OFF_UPPER_LT) || (dc_off_ch2_q1 < OFF_LOWER_LT) || + (dc_off_ch2_q2 > OFF_UPPER_LT) || (dc_off_ch2_q2 < OFF_LOWER_LT) || + (dc_off_ch2_q3 > OFF_UPPER_LT) || (dc_off_ch2_q3 < OFF_LOWER_LT)) { + if (osdac_ch2 == 3) { + ch2_done = 1; + } else { + osdac_ch2++; + + val = REG_READ(ah, AR_PHY_65NM_CH2_BB1) & 0x3fffffff; + val |= (osdac_ch2 << 30); + REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, val); + + ch2_done = 0; + } + } else { + ch2_done = 1; + } + } + + REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_OFFSET_CAL); + REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + + /* + * We don't need to check txiqcal_done here since it is always + * set for AR9550. + */ + REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); + + return true; +} + /* * solve 4x4 linear equation used in loopback iq cal. */ @@ -347,7 +566,7 @@ static bool ar9003_hw_solve_iq_cal(struct ath_hw *ah, const s32 result_shift = 1 << 15; struct ath_common *common = ath9k_hw_common(ah); - f2 = (f1 * f1 + f3 * f3) / result_shift; + f2 = ((f1 >> 3) * (f1 >> 3) + (f3 >> 3) * (f3 >> 3)) >> 9; if (!f2) { ath_dbg(common, CALIBRATE, "Divide by 0\n"); @@ -437,9 +656,6 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if (i2_m_q2_a0_d1 > 0x800) i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); - if (i2_p_q2_a0_d1 > 0x800) - i2_p_q2_a0_d1 = -((0xfff - i2_p_q2_a0_d1) + 1); - if (iq_corr_a0_d1 > 0x800) iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); @@ -482,6 +698,19 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, return false; } + if ((i2_p_q2_a0_d0 < 1024) || (i2_p_q2_a0_d0 > 2047) || + (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) || + (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) || + (i2_p_q2_a0_d0 <= iq_corr_a0_d0) || + (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) || + (i2_p_q2_a0_d1 <= iq_corr_a0_d1) || + (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) || + (i2_p_q2_a1_d0 <= iq_corr_a1_d0) || + (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) || + (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) { + return false; + } + mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; @@ -569,7 +798,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if (q_q_coff > 63) q_q_coff = 63; - iqc_coeff[0] = (q_q_coff * 128) + q_i_coff; + iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff); ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[0]); @@ -600,7 +829,7 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, if (q_q_coff > 63) q_q_coff = 63; - iqc_coeff[1] = (q_q_coff * 128) + q_i_coff; + iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff); ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n", chain_idx, iqc_coeff[1]); @@ -608,7 +837,8 @@ static bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, return true; } -static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, +static void ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL], + int nmeasurement, int max_delta) { int mp_max = -64, max_idx = 0; @@ -617,20 +847,20 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, /* find min/max mismatch across all calibrated gains */ for (i = 0; i < nmeasurement; i++) { - if (mp_coeff[i] > mp_max) { - mp_max = mp_coeff[i]; + if (mp_coeff[i][0] > mp_max) { + mp_max = mp_coeff[i][0]; max_idx = i; - } else if (mp_coeff[i] < mp_min) { - mp_min = mp_coeff[i]; + } else if (mp_coeff[i][0] < mp_min) { + mp_min = mp_coeff[i][0]; min_idx = i; } } /* find average (exclude max abs value) */ for (i = 0; i < nmeasurement; i++) { - if ((abs(mp_coeff[i]) < abs(mp_max)) || - (abs(mp_coeff[i]) < abs(mp_min))) { - mp_avg += mp_coeff[i]; + if ((abs(mp_coeff[i][0]) < abs(mp_max)) || + (abs(mp_coeff[i][0]) < abs(mp_min))) { + mp_avg += mp_coeff[i][0]; mp_count++; } } @@ -642,7 +872,7 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, if (mp_count) mp_avg /= mp_count; else - mp_avg = mp_coeff[nmeasurement - 1]; + mp_avg = mp_coeff[nmeasurement - 1][0]; /* detect outlier */ if (abs(mp_max - mp_min) > max_delta) { @@ -651,15 +881,16 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, else outlier_idx = min_idx; - mp_coeff[outlier_idx] = mp_avg; + mp_coeff[outlier_idx][0] = mp_avg; } } -static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, - struct coeff *coeff, - bool is_reusable) +static void ar9003_hw_tx_iq_cal_outlier_detection(struct ath_hw *ah, + struct coeff *coeff, + bool is_reusable) { int i, im, nmeasurement; + int magnitude, phase; u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; struct ath9k_hw_cal_data *caldata = ah->caldata; @@ -689,21 +920,30 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, if (nmeasurement > MAX_MEASUREMENT) nmeasurement = MAX_MEASUREMENT; - /* detect outlier only if nmeasurement > 1 */ - if (nmeasurement > 1) { - /* Detect magnitude outlier */ - ar9003_hw_detect_outlier(coeff->mag_coeff[i], - nmeasurement, MAX_MAG_DELTA); - - /* Detect phase outlier */ - ar9003_hw_detect_outlier(coeff->phs_coeff[i], - nmeasurement, MAX_PHS_DELTA); + /* + * Skip normal outlier detection for AR9550. + */ + if (!AR_SREV_9550(ah)) { + /* detect outlier only if nmeasurement > 1 */ + if (nmeasurement > 1) { + /* Detect magnitude outlier */ + ar9003_hw_detect_outlier(coeff->mag_coeff[i], + nmeasurement, + MAX_MAG_DELTA); + + /* Detect phase outlier */ + ar9003_hw_detect_outlier(coeff->phs_coeff[i], + nmeasurement, + MAX_PHS_DELTA); + } } for (im = 0; im < nmeasurement; im++) { + magnitude = coeff->mag_coeff[i][im][0]; + phase = coeff->phs_coeff[i][im][0]; - coeff->iqc_coeff[0] = (coeff->mag_coeff[i][im] & 0x7f) | - ((coeff->phs_coeff[i][im] & 0x7f) << 7); + coeff->iqc_coeff[0] = + (phase & 0x7f) | ((magnitude & 0x7f) << 7); if ((im % 2) == 0) REG_RMW_FIELD(ah, tx_corr_coeff[im][i], @@ -727,8 +967,12 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); - if (caldata) - caldata->done_txiqcal_once = is_reusable; + if (caldata) { + if (is_reusable) + set_bit(TXIQCAL_DONE, &caldata->cal_flags); + else + clear_bit(TXIQCAL_DONE, &caldata->cal_flags); + } return; } @@ -756,7 +1000,63 @@ static bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah) return true; } -static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) +static void __ar955x_tx_iq_cal_sort(struct ath_hw *ah, + struct coeff *coeff, + int i, int nmeasurement) +{ + struct ath_common *common = ath9k_hw_common(ah); + int im, ix, iy, temp; + + for (im = 0; im < nmeasurement; im++) { + for (ix = 0; ix < MAXIQCAL - 1; ix++) { + for (iy = ix + 1; iy <= MAXIQCAL - 1; iy++) { + if (coeff->mag_coeff[i][im][iy] < + coeff->mag_coeff[i][im][ix]) { + temp = coeff->mag_coeff[i][im][ix]; + coeff->mag_coeff[i][im][ix] = + coeff->mag_coeff[i][im][iy]; + coeff->mag_coeff[i][im][iy] = temp; + } + if (coeff->phs_coeff[i][im][iy] < + coeff->phs_coeff[i][im][ix]) { + temp = coeff->phs_coeff[i][im][ix]; + coeff->phs_coeff[i][im][ix] = + coeff->phs_coeff[i][im][iy]; + coeff->phs_coeff[i][im][iy] = temp; + } + } + } + coeff->mag_coeff[i][im][0] = coeff->mag_coeff[i][im][MAXIQCAL / 2]; + coeff->phs_coeff[i][im][0] = coeff->phs_coeff[i][im][MAXIQCAL / 2]; + + ath_dbg(common, CALIBRATE, + "IQCAL: Median [ch%d][gain%d]: mag = %d phase = %d\n", + i, im, + coeff->mag_coeff[i][im][0], + coeff->phs_coeff[i][im][0]); + } +} + +static bool ar955x_tx_iq_cal_median(struct ath_hw *ah, + struct coeff *coeff, + int iqcal_idx, + int nmeasurement) +{ + int i; + + if ((iqcal_idx + 1) != MAXIQCAL) + return false; + + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + __ar955x_tx_iq_cal_sort(ah, coeff, i, nmeasurement); + } + + return true; +} + +static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, + int iqcal_idx, + bool is_reusable) { struct ath_common *common = ath9k_hw_common(ah); const u32 txiqcal_status[AR9300_MAX_CHAINS] = { @@ -769,10 +1069,11 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) AR_PHY_CHAN_INFO_TAB_1, AR_PHY_CHAN_INFO_TAB_2, }; - struct coeff coeff; + static struct coeff coeff; s32 iq_res[6]; int i, im, j; - int nmeasurement; + int nmeasurement = 0; + bool outlier_detect = true; for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) @@ -830,17 +1131,23 @@ static void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, bool is_reusable) goto tx_iqcal_fail; } - coeff.mag_coeff[i][im] = coeff.iqc_coeff[0] & 0x7f; - coeff.phs_coeff[i][im] = + coeff.phs_coeff[i][im][iqcal_idx] = + coeff.iqc_coeff[0] & 0x7f; + coeff.mag_coeff[i][im][iqcal_idx] = (coeff.iqc_coeff[0] >> 7) & 0x7f; - if (coeff.mag_coeff[i][im] > 63) - coeff.mag_coeff[i][im] -= 128; - if (coeff.phs_coeff[i][im] > 63) - coeff.phs_coeff[i][im] -= 128; + if (coeff.mag_coeff[i][im][iqcal_idx] > 63) + coeff.mag_coeff[i][im][iqcal_idx] -= 128; + if (coeff.phs_coeff[i][im][iqcal_idx] > 63) + coeff.phs_coeff[i][im][iqcal_idx] -= 128; } } - ar9003_hw_tx_iqcal_load_avg_2_passes(ah, &coeff, is_reusable); + + if (AR_SREV_9550(ah)) + outlier_detect = ar955x_tx_iq_cal_median(ah, &coeff, + iqcal_idx, nmeasurement); + if (outlier_detect) + ar9003_hw_tx_iq_cal_outlier_detection(ah, &coeff, is_reusable); return; @@ -894,7 +1201,7 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) { - int offset[8], total = 0, test; + int offset[8] = {0}, total = 0, test; int agc_out, i; REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), @@ -919,12 +1226,18 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR, 0x1); REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1); - if (is_2g) - REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), - AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); - else + + if (AR_SREV_9330_11(ah)) { REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), - AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); + } else { + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + } for (i = 6; i > 0; i--) { offset[i] = BIT(i - 1); @@ -960,19 +1273,45 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); } -static void ar9003_hw_do_manual_peak_cal(struct ath_hw *ah, - struct ath9k_channel *chan) +static void ar9003_hw_do_pcoem_manual_peak_cal(struct ath_hw *ah, + struct ath9k_channel *chan, + bool run_rtt_cal) { + struct ath9k_hw_cal_data *caldata = ah->caldata; int i; if (!AR_SREV_9462(ah) && !AR_SREV_9565(ah) && !AR_SREV_9485(ah)) return; + if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && !run_rtt_cal) + return; + for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->rxchainmask & (1 << i))) continue; ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan)); } + + if (caldata) + set_bit(SW_PKDET_DONE, &caldata->cal_flags); + + if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && caldata) { + if (IS_CHAN_2GHZ(chan)){ + caldata->caldac[0] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR); + caldata->caldac[1] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR); + } else { + caldata->caldac[0] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR); + caldata->caldac[1] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR); + } + } } static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) @@ -990,7 +1329,7 @@ static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_CLC_SUCCESS); - if (caldata->done_txclcal_once) { + if (test_bit(TXCLCAL_DONE, &caldata->cal_flags)) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; @@ -1006,19 +1345,20 @@ static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) caldata->tx_clcal[i][j] = REG_READ(ah, CL_TAB_ENTRY(cl_idx[i])); } - caldata->done_txclcal_once = true; + set_bit(TXCLCAL_DONE, &caldata->cal_flags); } } -static bool ar9003_hw_init_cal(struct ath_hw *ah, - struct ath9k_channel *chan) +static bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah, + struct ath9k_channel *chan) { struct ath_common *common = ath9k_hw_common(ah); struct ath9k_hw_cal_data *caldata = ah->caldata; bool txiqcal_done = false; bool is_reusable = true, status = true; - bool run_rtt_cal = false, run_agc_cal, sep_iq_cal = false; + bool run_rtt_cal = false, run_agc_cal; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); + u32 rx_delay = 0; u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL; @@ -1042,17 +1382,22 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, ar9003_hw_rtt_clear_hist(ah); } - if (rtt && !run_rtt_cal) { - agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); - agc_supp_cals &= agc_ctrl; - agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | - AR_PHY_AGC_CONTROL_FLTR_CAL | - AR_PHY_AGC_CONTROL_PKDET_CAL); - REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); + if (rtt) { + if (!run_rtt_cal) { + agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); + agc_supp_cals &= agc_ctrl; + agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | + AR_PHY_AGC_CONTROL_FLTR_CAL | + AR_PHY_AGC_CONTROL_PKDET_CAL); + REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); + } else { + if (ah->ah_flags & AH_FASTCC) + run_agc_cal = true; + } } if (ah->enabled_cals & TX_CL_CAL) { - if (caldata && caldata->done_txclcal_once) + if (caldata && test_bit(TXCLCAL_DONE, &caldata->cal_flags)) REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); else { @@ -1076,26 +1421,25 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, * AGC calibration */ if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { - if (caldata && !caldata->done_txiqcal_once) + if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags)) REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); else REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); txiqcal_done = run_agc_cal = true; - } else if (caldata && !caldata->done_txiqcal_once) { - run_agc_cal = true; - sep_iq_cal = true; } skip_tx_iqcal: if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_req(ah, &is_reusable); - if (sep_iq_cal) { - txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); + if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { + rx_delay = REG_READ(ah, AR_PHY_RX_DELAY); + /* Disable BB_active */ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); udelay(5); + REG_WRITE(ah, AR_PHY_RX_DELAY, AR_PHY_RX_DELAY_DELAY); REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); } @@ -1110,7 +1454,12 @@ skip_tx_iqcal: AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); - ar9003_hw_do_manual_peak_cal(ah, chan); + ar9003_hw_do_pcoem_manual_peak_cal(ah, chan, run_rtt_cal); + } + + if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { + REG_WRITE(ah, AR_PHY_RX_DELAY, rx_delay); + udelay(5); } if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) @@ -1132,20 +1481,24 @@ skip_tx_iqcal: } if (txiqcal_done) - ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable); - else if (caldata && caldata->done_txiqcal_once) + ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable); + else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags)) ar9003_hw_tx_iq_cal_reload(ah); ar9003_hw_cl_cal_post_proc(ah, is_reusable); if (run_rtt_cal && caldata) { if (is_reusable) { - if (!ath9k_hw_rfbus_req(ah)) + if (!ath9k_hw_rfbus_req(ah)) { ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); - else + } else { ar9003_hw_rtt_fill_hist(ah); + if (test_bit(SW_PKDET_DONE, &caldata->cal_flags)) + ar9003_hw_rtt_load_hist(ah); + } + ath9k_hw_rfbus_done(ah); } @@ -1174,13 +1527,156 @@ skip_tx_iqcal: return true; } +static bool do_ar9003_agc_cal(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + bool status; + + REG_WRITE(ah, AR_PHY_AGC_CONTROL, + REG_READ(ah, AR_PHY_AGC_CONTROL) | + AR_PHY_AGC_CONTROL_CAL); + + status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, + 0, AH_WAIT_TIMEOUT); + if (!status) { + ath_dbg(common, CALIBRATE, + "offset calibration failed to complete in %d ms," + "noisy environment?\n", + AH_WAIT_TIMEOUT / 1000); + return false; + } + + return true; +} + +static bool ar9003_hw_init_cal_soc(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_hw_cal_data *caldata = ah->caldata; + bool txiqcal_done = false; + bool status = true; + bool run_agc_cal = false, sep_iq_cal = false; + int i = 0; + + /* Use chip chainmask only for calibration */ + ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); + + if (ah->enabled_cals & TX_CL_CAL) { + REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + run_agc_cal = true; + } + + if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) + goto skip_tx_iqcal; + + /* Do Tx IQ Calibration */ + REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1, + AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, + DELPT); + + /* + * For AR9485 or later chips, TxIQ cal runs as part of + * AGC calibration. Specifically, AR9550 in SoC chips. + */ + if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { + if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, + AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) { + txiqcal_done = true; + } else { + txiqcal_done = false; + } + run_agc_cal = true; + } else { + sep_iq_cal = true; + run_agc_cal = true; + } + + /* + * In the SoC family, this will run for AR9300, AR9331 and AR9340. + */ + if (sep_iq_cal) { + txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + udelay(5); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + } + + if (AR_SREV_9550(ah) && IS_CHAN_2GHZ(chan)) { + if (!ar9003_hw_dynamic_osdac_selection(ah, txiqcal_done)) + return false; + } + +skip_tx_iqcal: + if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { + if (AR_SREV_9330_11(ah)) + ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan)); + + /* + * For non-AR9550 chips, we just trigger AGC calibration + * in the HW, poll for completion and then process + * the results. + * + * For AR955x, we run it multiple times and use + * median IQ correction. + */ + if (!AR_SREV_9550(ah)) { + status = do_ar9003_agc_cal(ah); + if (!status) + return false; + + if (txiqcal_done) + ar9003_hw_tx_iq_cal_post_proc(ah, 0, false); + } else { + if (!txiqcal_done) { + status = do_ar9003_agc_cal(ah); + if (!status) + return false; + } else { + for (i = 0; i < MAXIQCAL; i++) { + status = do_ar9003_agc_cal(ah); + if (!status) + return false; + ar9003_hw_tx_iq_cal_post_proc(ah, i, false); + } + } + } + } + + /* Revert chainmask to runtime parameters */ + ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); + + /* Initialize list pointers */ + ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; + + INIT_CAL(&ah->iq_caldata); + INSERT_CAL(ah, &ah->iq_caldata); + ath_dbg(common, CALIBRATE, "enabling IQ Calibration\n"); + + /* Initialize current pointer to first element in list */ + ah->cal_list_curr = ah->cal_list; + + if (ah->cal_list_curr) + ath9k_hw_reset_calibration(ah, ah->cal_list_curr); + + if (caldata) + caldata->CalValid = 0; + + return true; +} + void ar9003_hw_attach_calib_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_ops *ops = ath9k_hw_ops(ah); + if (AR_SREV_9485(ah) || AR_SREV_9462(ah) || AR_SREV_9565(ah)) + priv_ops->init_cal = ar9003_hw_init_cal_pcoem; + else + priv_ops->init_cal = ar9003_hw_init_cal_soc; + priv_ops->init_cal_settings = ar9003_hw_init_cal_settings; - priv_ops->init_cal = ar9003_hw_init_cal; priv_ops->setup_calibration = ar9003_hw_setup_calibration; ops->calibrate = ar9003_hw_calibrate; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index f4864807e15..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) @@ -131,6 +131,7 @@ static const struct ar9300_eeprom ar9300_default = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -138,7 +139,7 @@ static const struct ar9300_eeprom ar9300_default = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -333,6 +334,7 @@ static const struct ar9300_eeprom ar9300_default = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -707,6 +709,7 @@ static const struct ar9300_eeprom ar9300_x113 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -714,7 +717,7 @@ static const struct ar9300_eeprom ar9300_x113 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -909,6 +912,7 @@ static const struct ar9300_eeprom ar9300_x113 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1284,6 +1288,7 @@ static const struct ar9300_eeprom ar9300_h112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1291,7 +1296,7 @@ static const struct ar9300_eeprom ar9300_h112 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -1486,6 +1491,7 @@ static const struct ar9300_eeprom ar9300_h112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1861,6 +1867,7 @@ static const struct ar9300_eeprom ar9300_x112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1868,7 +1875,7 @@ static const struct ar9300_eeprom ar9300_x112 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -2063,6 +2070,7 @@ static const struct ar9300_eeprom ar9300_x112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -2437,6 +2445,7 @@ static const struct ar9300_eeprom ar9300_h116 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80C080), .papdRateMaskHt40 = LE32(0x0080C080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -2444,7 +2453,7 @@ static const struct ar9300_eeprom ar9300_h116 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -2639,6 +2648,7 @@ static const struct ar9300_eeprom ar9300_h116 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -2991,7 +3001,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah, case EEP_CHAIN_MASK_REDUCE: return (pBase->miscConfiguration >> 0x3) & 0x1; case EEP_ANT_DIV_CTL1: - return eep->base_ext1.ant_div_control; + if (AR_SREV_9565(ah)) + return AR9300_EEP_ANTDIV_CONTROL_DEFAULT_VALUE; + else + return eep->base_ext1.ant_div_control; case EEP_ANTENNA_GAIN_5G: return eep->modalHeader5G.antennaGain; case EEP_ANTENNA_GAIN_2G: @@ -3424,12 +3437,12 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct ar9300_base_eep_hdr *pBase; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader2G); - len += snprintf(buf + len, size - len, - "%20s :\n", "5GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "5GHz modal Header"); len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader5G); goto out; @@ -3479,8 +3492,8 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Rx Gain", pBase->txrxgain & 0xf); PR_EEP("SW Reg", le32_to_cpu(pBase->swreg)); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - ah->eeprom.ar9300_eep.macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + ah->eeprom.ar9300_eep.macAddr); out: if (len > size) len = size; @@ -3585,7 +3598,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9462_ALL, value); - } else if (AR_SREV_9550(ah)) { + } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9550_ALL, value); } else @@ -3656,9 +3669,23 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) if (AR_SREV_9565(ah)) { if (common->bt_ant_diversity) { regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S); + + REG_SET_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); + + /* Force WLAN LNA diversity ON */ + REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); } else { regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S); regval &= ~(1 << AR_PHY_ANT_SW_RX_PROT_S); + + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, + (1 << AR_PHY_ANT_SW_RX_PROT_S)); + + /* Force WLAN LNA diversity OFF */ + REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); } } @@ -3669,7 +3696,8 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) regval &= (~AR_FAST_DIV_ENABLE); regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; - if (AR_SREV_9485(ah) && common->bt_ant_diversity) + if ((AR_SREV_9485(ah) || AR_SREV_9565(ah)) + && common->bt_ant_diversity) regval |= AR_FAST_DIV_ENABLE; REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); @@ -3947,7 +3975,7 @@ static void ar9003_hw_apply_tuning_caps(struct ath_hw *ah) struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; u8 tuning_caps_param = eep->baseEepHeader.params_for_tuning_caps[0]; - if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9531(ah)) return; if (eep->baseEepHeader.featureEnable & 0x40) { @@ -3966,18 +3994,20 @@ static void ar9003_hw_quick_drop_apply(struct ath_hw *ah, u16 freq) int quick_drop; s32 t[3], f[3] = {5180, 5500, 5785}; - if (!(pBase->miscConfiguration & BIT(1))) + if (!(pBase->miscConfiguration & BIT(4))) return; - if (freq < 4000) - quick_drop = eep->modalHeader2G.quick_drop; - else { - t[0] = eep->base_ext1.quick_drop_low; - t[1] = eep->modalHeader5G.quick_drop; - t[2] = eep->base_ext1.quick_drop_high; - quick_drop = ar9003_hw_power_interpolate(freq, f, t, 3); + if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9340(ah)) { + if (freq < 4000) { + quick_drop = eep->modalHeader2G.quick_drop; + } else { + t[0] = eep->base_ext1.quick_drop_low; + t[1] = eep->modalHeader5G.quick_drop; + t[2] = eep->base_ext1.quick_drop_high; + quick_drop = ar9003_hw_power_interpolate(freq, f, t, 3); + } + REG_RMW_FIELD(ah, AR_PHY_AGC, AR_PHY_AGC_QUICK_DROP, quick_drop); } - REG_RMW_FIELD(ah, AR_PHY_AGC, AR_PHY_AGC_QUICK_DROP, quick_drop); } static void ar9003_hw_txend_to_xpa_off_apply(struct ath_hw *ah, bool is2ghz) @@ -4000,7 +4030,10 @@ static void ar9003_hw_xpa_timing_control_apply(struct ath_hw *ah, bool is2ghz) if (!(eep->baseEepHeader.featureEnable & 0x80)) return; - if (!AR_SREV_9300(ah) && !AR_SREV_9340(ah) && !AR_SREV_9580(ah)) + if (!AR_SREV_9300(ah) && + !AR_SREV_9340(ah) && + !AR_SREV_9580(ah) && + !AR_SREV_9531(ah)) return; xpa_ctl = ar9003_modal_header(ah, is2ghz)->txFrameToXpaOn; @@ -4017,7 +4050,7 @@ static void ar9003_hw_xlna_bias_strength_apply(struct ath_hw *ah, bool is2ghz) struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; u8 bias; - if (!(eep->baseEepHeader.featureEnable & 0x40)) + if (!(eep->baseEepHeader.miscConfiguration & 0x40)) return; if (!AR_SREV_9300(ah)) @@ -4091,6 +4124,37 @@ static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah) } } +static void ar9003_hw_apply_minccapwr_thresh(struct ath_hw *ah, + bool is2ghz) +{ + struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; + const u_int32_t cca_ctrl[AR9300_MAX_CHAINS] = { + AR_PHY_CCA_CTRL_0, + AR_PHY_CCA_CTRL_1, + AR_PHY_CCA_CTRL_2, + }; + int chain; + u32 val; + + if (is2ghz) { + if (!(eep->base_ext1.misc_enable & BIT(2))) + return; + } else { + if (!(eep->base_ext1.misc_enable & BIT(3))) + return; + } + + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->caps.tx_chainmask & BIT(chain))) + continue; + + val = ar9003_modal_header(ah, is2ghz)->noiseFloorThreshCh[chain]; + REG_RMW_FIELD(ah, cca_ctrl[chain], + AR_PHY_EXT_CCA0_THRESH62_1, val); + } + +} + static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -4102,9 +4166,10 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah, ar9003_hw_xlna_bias_strength_apply(ah, is2ghz); ar9003_hw_atten_apply(ah, chan); ar9003_hw_quick_drop_apply(ah, chan->channel); - if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9550(ah)) + if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah)) ar9003_hw_internal_regulator_apply(ah); ar9003_hw_apply_tuning_caps(ah); + ar9003_hw_apply_minccapwr_thresh(ah, chan); ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz); ar9003_hw_thermometer_apply(ah); ar9003_hw_thermo_cal_apply(ah); @@ -4726,44 +4791,55 @@ static void ar9003_hw_power_control_override(struct ath_hw *ah, } tempslope: - if (AR_SREV_9550(ah)) { + 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, @@ -5000,6 +5076,10 @@ static u16 ar9003_hw_get_max_edge_power(struct ar9300_eeprom *eep, break; } } + + if (is2GHz && !twiceMaxEdgePower) + twiceMaxEdgePower = 60; + return twiceMaxEdgePower; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 75d4fb41962..694ca2e680e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -52,6 +52,8 @@ #define AR9300_PAPRD_SCALE_2 0x70000000 #define AR9300_PAPRD_SCALE_2_S 28 +#define AR9300_EEP_ANTDIV_CONTROL_DEFAULT_VALUE 0xc9 + /* Delta from which to start power to pdadc table */ /* This offset is used in both open loop and closed loop power control * schemes. In open loop power control, it is not really needed, but for @@ -268,10 +270,20 @@ struct cal_ctl_data_5g { u8 ctlEdges[AR9300_NUM_BAND_EDGES_5G]; } __packed; +#define MAX_BASE_EXTENSION_FUTURE 2 + struct ar9300_BaseExtension_1 { u8 ant_div_control; - u8 future[3]; - u8 tempslopextension[8]; + u8 future[MAX_BASE_EXTENSION_FUTURE]; + /* + * misc_enable: + * + * BIT 0 - TX Gain Cap enable. + * BIT 1 - Uncompressed Checksum enable. + * BIT 2/3 - MinCCApwr enable 2g/5g. + */ + u8 misc_enable; + int8_t tempslopextension[8]; int8_t quick_drop_low; int8_t quick_drop_high; } __packed; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 608bb4824e2..ec1da0cc25f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -17,6 +17,7 @@ #include "hw.h" #include "ar9003_mac.h" #include "ar9003_2p2_initvals.h" +#include "ar9003_buffalo_initvals.h" #include "ar9485_initvals.h" #include "ar9340_initvals.h" #include "ar9330_1p1_initvals.h" @@ -26,6 +27,8 @@ #include "ar9462_2p0_initvals.h" #include "ar9462_2p1_initvals.h" #include "ar9565_1p0_initvals.h" +#include "ar9565_1p1_initvals.h" +#include "ar953x_initvals.h" /* General hardware code for the AR9003 hadware family */ @@ -148,7 +151,11 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9340Modes_high_ob_db_tx_gain_table_1p0); INIT_INI_ARRAY(&ah->iniModesFastClock, - ar9340Modes_fast_clock_1p0); + ar9340Modes_fast_clock_1p0); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9340_1p0_baseband_core_txfir_coeff_japan_2484); + INIT_INI_ARRAY(&ah->ini_dfs, + ar9340_1p0_baseband_postamble_dfs_channel); if (!ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, @@ -187,17 +194,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9485_1_1_baseband_core_txfir_coeff_japan_2484); - /* Load PCIE SERDES settings from INI */ - - /* Awake Setting */ - - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9485_1_1_pcie_phy_clkreq_disable_L1); - - /* Sleep Setting */ - - INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9485_1_1_pcie_phy_clkreq_disable_L1); + if (ah->config.no_pll_pwrsave) { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9485_1_1_pcie_phy_clkreq_disable_L1); + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9485_1_1_pcie_phy_clkreq_disable_L1); + } else { + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1); + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1); + } } else if (AR_SREV_9462_21(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p1_mac_core); @@ -223,6 +230,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_2p1_modes_fast_clock); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9462_2p1_baseband_core_txfir_coeff_japan_2484); + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9462_2p1_pciephy_clkreq_disable_L1); + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9462_2p1_pciephy_clkreq_disable_L1); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9462_2p0_mac_core); @@ -247,18 +258,18 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_2p0_soc_postamble); INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_common_rx_gain_table_2p0); + ar9462_2p0_common_rx_gain); /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9462_pciephy_clkreq_disable_L1_2p0); + ar9462_2p0_pciephy_clkreq_disable_L1); /* Sleep -> Awake Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9462_pciephy_clkreq_disable_L1_2p0); + ar9462_2p0_pciephy_clkreq_disable_L1); /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, - ar9462_modes_fast_clock_2p0); + ar9462_2p0_modes_fast_clock); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, ar9462_2p0_baseband_core_txfir_coeff_japan_2484); @@ -298,6 +309,31 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, ar955x_1p0_modes_fast_clock); + } else if (AR_SREV_9531(ah)) { + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], + qca953x_1p0_mac_core); + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], + qca953x_1p0_mac_postamble); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + qca953x_1p0_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + qca953x_1p0_baseband_postamble); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], + qca953x_1p0_radio_core); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], + qca953x_1p0_radio_postamble); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], + qca953x_1p0_soc_preamble); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], + qca953x_1p0_soc_postamble); + INIT_INI_ARRAY(&ah->iniModesRxGain, + qca953x_1p0_common_wo_xlna_rx_gain_table); + INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, + qca953x_1p0_common_wo_xlna_rx_gain_bounds); + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca953x_1p0_modes_no_xpa_tx_gain_table); + INIT_INI_ARRAY(&ah->iniModesFastClock, + qca953x_1p0_modes_fast_clock); } else if (AR_SREV_9580(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -330,7 +366,46 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9580_1p0_low_ob_db_tx_gain_table); INIT_INI_ARRAY(&ah->iniModesFastClock, - ar9580_1p0_modes_fast_clock); + ar9580_1p0_modes_fast_clock); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9580_1p0_baseband_core_txfir_coeff_japan_2484); + INIT_INI_ARRAY(&ah->ini_dfs, + ar9580_1p0_baseband_postamble_dfs_channel); + } else if (AR_SREV_9565_11_OR_LATER(ah)) { + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], + ar9565_1p1_mac_core); + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], + ar9565_1p1_mac_postamble); + + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + ar9565_1p1_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + ar9565_1p1_baseband_postamble); + + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], + ar9565_1p1_radio_core); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], + ar9565_1p1_radio_postamble); + + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], + ar9565_1p1_soc_preamble); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], + ar9565_1p1_soc_postamble); + + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9565_1p1_Common_rx_gain_table); + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9565_1p1_Modes_lowest_ob_db_tx_gain_table); + + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9565_1p1_pciephy_clkreq_disable_L1); + INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, + ar9565_1p1_pciephy_clkreq_disable_L1); + + INIT_INI_ARRAY(&ah->iniModesFastClock, + ar9565_1p1_modes_fast_clock); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9565_1p1_baseband_core_txfir_coeff_japan_2484); } else if (AR_SREV_9565(ah)) { INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], ar9565_1p0_mac_core); @@ -364,6 +439,8 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesFastClock, ar9565_1p0_modes_fast_clock); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9565_1p0_baseband_core_txfir_coeff_japan_2484); } else { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -409,7 +486,11 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) /* Fast clock modal settings */ INIT_INI_ARRAY(&ah->iniModesFastClock, - ar9300Modes_fast_clock_2p2); + ar9300Modes_fast_clock_2p2); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9300_2p2_baseband_core_txfir_coeff_japan_2484); + INIT_INI_ARRAY(&ah->ini_dfs, + ar9300_2p2_baseband_postamble_dfs_channel); } } @@ -430,6 +511,9 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) else if (AR_SREV_9550(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar955x_1p0_modes_xpa_tx_gain_table); + else if (AR_SREV_9531(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca953x_1p0_modes_xpa_tx_gain_table); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_lowest_ob_db_tx_gain_table); @@ -438,7 +522,10 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) ar9462_2p1_modes_low_ob_db_tx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9462_modes_low_ob_db_tx_gain_table_2p0); + ar9462_2p0_modes_low_ob_db_tx_gain); + else if (AR_SREV_9565_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9565_1p1_modes_low_ob_db_tx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_low_ob_db_tx_gain_table); @@ -467,12 +554,22 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah) else if (AR_SREV_9550(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar955x_1p0_modes_no_xpa_tx_gain_table); - else if (AR_SREV_9462_21(ah)) + else if (AR_SREV_9531(ah)) { + if (AR_SREV_9531_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca953x_1p1_modes_no_xpa_tx_gain_table); + else + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca953x_1p0_modes_no_xpa_tx_gain_table); + } else if (AR_SREV_9462_21(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_2p1_modes_high_ob_db_tx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9462_modes_high_ob_db_tx_gain_table_2p0); + ar9462_2p0_modes_high_ob_db_tx_gain); + else if (AR_SREV_9565_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9565_1p1_modes_high_ob_db_tx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_high_ob_db_tx_gain_table); @@ -498,6 +595,9 @@ static void ar9003_tx_gain_table_mode2(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_low_ob_db_tx_gain_table); + else if (AR_SREV_9565_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9565_1p1_modes_low_ob_db_tx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_low_ob_db_tx_gain_table); @@ -523,12 +623,20 @@ static void ar9003_tx_gain_table_mode3(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_high_power_tx_gain_table); + else if (AR_SREV_9565_11(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9565_1p1_modes_high_power_tx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p0_modes_high_power_tx_gain_table); - else - INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9300Modes_high_power_tx_gain_table_2p2); + else { + if (ah->config.tx_gain_buffalo) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_high_power_tx_gain_table_buffalo); + else + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9300Modes_high_power_tx_gain_table_2p2); + } } static void ar9003_tx_gain_table_mode4(struct ath_hw *ah) @@ -544,7 +652,7 @@ static void ar9003_tx_gain_table_mode4(struct ath_hw *ah) ar9462_2p1_modes_mix_ob_db_tx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, - ar9462_modes_mix_ob_db_tx_gain_table_2p0); + ar9462_2p0_modes_mix_ob_db_tx_gain); else INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_mixed_ob_db_tx_gain_table_2p2); @@ -579,6 +687,13 @@ static void ar9003_tx_gain_table_mode6(struct ath_hw *ah) ar9580_1p0_type6_tx_gain_table); } +static void ar9003_tx_gain_table_mode7(struct ath_hw *ah) +{ + if (AR_SREV_9340(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + ar9340_cus227_tx_gain_table_1p0); +} + typedef void (*ath_txgain_tab)(struct ath_hw *ah); static void ar9003_tx_gain_table_apply(struct ath_hw *ah) @@ -591,6 +706,7 @@ static void ar9003_tx_gain_table_apply(struct ath_hw *ah) ar9003_tx_gain_table_mode4, ar9003_tx_gain_table_mode5, ar9003_tx_gain_table_mode6, + ar9003_tx_gain_table_mode7, }; int idx = ar9003_hw_get_tx_gain_idx(ah); @@ -619,6 +735,11 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) ar955x_1p0_common_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, ar955x_1p0_common_rx_gain_bounds); + } else if (AR_SREV_9531(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + qca953x_1p0_common_rx_gain_table); + INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, + qca953x_1p0_common_rx_gain_bounds); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_rx_gain_table); @@ -627,7 +748,13 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) ar9462_2p1_common_rx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_common_rx_gain_table_2p0); + ar9462_2p0_common_rx_gain); + else if (AR_SREV_9565_11(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9565_1p1_Common_rx_gain_table); + else if (AR_SREV_9565(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9565_1p0_Common_rx_gain_table); else INIT_INI_ARRAY(&ah->iniModesRxGain, ar9300Common_rx_gain_table_2p2); @@ -652,15 +779,23 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) ar9462_2p1_common_wo_xlna_rx_gain); else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_common_wo_xlna_rx_gain_table_2p0); + ar9462_2p0_common_wo_xlna_rx_gain); else if (AR_SREV_9550(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, ar955x_1p0_common_wo_xlna_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, ar955x_1p0_common_wo_xlna_rx_gain_bounds); + } else if (AR_SREV_9531(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + qca953x_1p0_common_wo_xlna_rx_gain_table); + INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, + qca953x_1p0_common_wo_xlna_rx_gain_bounds); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_wo_xlna_rx_gain_table); + else if (AR_SREV_9565_11(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9565_1p1_common_wo_xlna_rx_gain_table); else if (AR_SREV_9565(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9565_1p0_common_wo_xlna_rx_gain_table); @@ -682,7 +817,7 @@ static void ar9003_rx_gain_table_mode2(struct ath_hw *ah) ar9462_2p1_baseband_postamble_5g_xlna); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_common_mixed_rx_gain_table_2p0); + ar9462_2p0_common_mixed_rx_gain); INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_core, ar9462_2p0_baseband_core_mix_rxgain); INIT_INI_ARRAY(&ah->ini_modes_rxgain_bb_postamble, @@ -696,12 +831,12 @@ static void ar9003_rx_gain_table_mode3(struct ath_hw *ah) { if (AR_SREV_9462_21(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_2p1_common_5g_xlna_only_rx_gain); + ar9462_2p1_common_5g_xlna_only_rxgain); INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, ar9462_2p1_baseband_postamble_5g_xlna); } else if (AR_SREV_9462_20(ah)) { INIT_INI_ARRAY(&ah->iniModesRxGain, - ar9462_2p0_5g_xlna_only_rxgain); + ar9462_2p0_common_5g_xlna_only_rxgain); INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, ar9462_2p0_baseband_postamble_5g_xlna); } @@ -745,6 +880,9 @@ static void ar9003_hw_init_mode_gain_regs(struct ath_hw *ah) static void ar9003_hw_configpcipowersave(struct ath_hw *ah, bool power_off) { + unsigned int i; + struct ar5416IniArray *array; + /* * Increase L1 Entry Latency. Some WB222 boards don't have * this change in eeprom/OTP. @@ -770,19 +908,125 @@ static void ar9003_hw_configpcipowersave(struct ath_hw *ah, * Configire PCIE after Ini init. SERDES values now come from ini file * This enables PCIe low power mode. */ - if (ah->config.pcieSerDesWrite) { - unsigned int i; - struct ar5416IniArray *array; + array = power_off ? &ah->iniPcieSerdes : + &ah->iniPcieSerdesLowPower; + + for (i = 0; i < array->ia_rows; i++) { + REG_WRITE(ah, + INI_RA(array, i, 0), + INI_RA(array, i, 1)); + } +} + +static void ar9003_hw_init_hang_checks(struct ath_hw *ah) +{ + /* + * All chips support detection of BB/MAC hangs. + */ + ah->config.hw_hang_checks |= HW_BB_WATCHDOG; + ah->config.hw_hang_checks |= HW_MAC_HANG; + + /* + * This is not required for AR9580 1.0 + */ + if (AR_SREV_9300_22(ah)) + ah->config.hw_hang_checks |= HW_PHYRESTART_CLC_WAR; - array = power_off ? &ah->iniPcieSerdes : - &ah->iniPcieSerdesLowPower; + if (AR_SREV_9330(ah)) + ah->bb_watchdog_timeout_ms = 85; + else + ah->bb_watchdog_timeout_ms = 25; +} + +/* + * MAC HW hang check + * ================= + * + * Signature: dcu_chain_state is 0x6 and dcu_complete_state is 0x1. + * + * The state of each DCU chain (mapped to TX queues) is available from these + * DMA debug registers: + * + * Chain 0 state : Bits 4:0 of AR_DMADBG_4 + * Chain 1 state : Bits 9:5 of AR_DMADBG_4 + * Chain 2 state : Bits 14:10 of AR_DMADBG_4 + * Chain 3 state : Bits 19:15 of AR_DMADBG_4 + * Chain 4 state : Bits 24:20 of AR_DMADBG_4 + * Chain 5 state : Bits 29:25 of AR_DMADBG_4 + * Chain 6 state : Bits 4:0 of AR_DMADBG_5 + * Chain 7 state : Bits 9:5 of AR_DMADBG_5 + * Chain 8 state : Bits 14:10 of AR_DMADBG_5 + * Chain 9 state : Bits 19:15 of AR_DMADBG_5 + * + * The DCU chain state "0x6" means "WAIT_FRDONE" - wait for TX frame to be done. + */ + +#define NUM_STATUS_READS 50 + +static bool ath9k_hw_verify_hang(struct ath_hw *ah, unsigned int queue) +{ + u32 dma_dbg_chain, dma_dbg_complete; + u8 dcu_chain_state, dcu_complete_state; + int i; + + for (i = 0; i < NUM_STATUS_READS; i++) { + if (queue < 6) + dma_dbg_chain = REG_READ(ah, AR_DMADBG_4); + else + dma_dbg_chain = REG_READ(ah, AR_DMADBG_5); + + dma_dbg_complete = REG_READ(ah, AR_DMADBG_6); + + dcu_chain_state = (dma_dbg_chain >> (5 * queue)) & 0x1f; + dcu_complete_state = dma_dbg_complete & 0x3; + + if ((dcu_chain_state != 0x6) || (dcu_complete_state != 0x1)) + return false; + } + + ath_dbg(ath9k_hw_common(ah), RESET, + "MAC Hang signature found for queue: %d\n", queue); + + return true; +} + +static bool ar9003_hw_detect_mac_hang(struct ath_hw *ah) +{ + u32 dma_dbg_4, dma_dbg_5, dma_dbg_6, chk_dbg; + u8 dcu_chain_state, dcu_complete_state; + bool dcu_wait_frdone = false; + unsigned long chk_dcu = 0; + unsigned int i = 0; + + dma_dbg_4 = REG_READ(ah, AR_DMADBG_4); + dma_dbg_5 = REG_READ(ah, AR_DMADBG_5); + dma_dbg_6 = REG_READ(ah, AR_DMADBG_6); + + dcu_complete_state = dma_dbg_6 & 0x3; + if (dcu_complete_state != 0x1) + goto exit; + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (i < 6) + chk_dbg = dma_dbg_4; + else + chk_dbg = dma_dbg_5; + + dcu_chain_state = (chk_dbg >> (5 * i)) & 0x1f; + if (dcu_chain_state == 0x6) { + dcu_wait_frdone = true; + chk_dcu |= BIT(i); + } + } - for (i = 0; i < array->ia_rows; i++) { - REG_WRITE(ah, - INI_RA(array, i, 0), - INI_RA(array, i, 1)); + if ((dcu_complete_state == 0x1) && dcu_wait_frdone) { + for_each_set_bit(i, &chk_dcu, ATH9K_NUM_TX_QUEUES) { + if (ath9k_hw_verify_hang(ah, i)) + return true; } } +exit: + return false; } /* Sets up the AR9003 hardware familiy callbacks */ @@ -793,6 +1037,8 @@ void ar9003_hw_attach_ops(struct ath_hw *ah) ar9003_hw_init_mode_regs(ah); priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs; + priv_ops->init_hang_checks = ar9003_hw_init_hang_checks; + priv_ops->detect_mac_hang = ar9003_hw_detect_mac_hang; ops->config_pci_powersave = ar9003_hw_configpcipowersave; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index f6c5c1b5047..729ffbf0734 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -175,7 +175,8 @@ static void ar9003_hw_set_desc_link(void *ds, u32 ds_link) ads->ctl10 |= ar9003_calc_ptr_chksum(ads); } -static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) +static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p) { u32 isr = 0; u32 mask2 = 0; @@ -310,7 +311,8 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) ar9003_mci_get_isr(ah, masked); if (sync_cause) { - ath9k_debug_sync_cause(common, sync_cause); + if (sync_cause_p) + *sync_cause_p = sync_cause; fatal_int = (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) @@ -476,12 +478,12 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, /* XXX: Keycache */ rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined); - rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00); - rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01); - rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02); - rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10); - rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11); - rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12); + rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00); + rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01); + rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02); + rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10); + rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11); + rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12); if (rxsp->status11 & AR_RxKeyIdxValid) rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 8dd069259e7..7b94a6c7db3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -753,9 +753,9 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); if (caldata) { - caldata->done_txiqcal_once = false; - caldata->done_txclcal_once = false; - caldata->rtt_done = false; + clear_bit(TXIQCAL_DONE, &caldata->cal_flags); + clear_bit(TXCLCAL_DONE, &caldata->cal_flags); + clear_bit(RTT_DONE, &caldata->cal_flags); } if (!ath9k_hw_init_cal(ah, chan)) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index e897648d323..8927fc34d84 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -103,7 +103,7 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) } else { channelSel = CHANSEL_2G(freq) >> 1; } - } else if (AR_SREV_9550(ah)) { + } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { if (ah->is_clk_25mhz) div = 75; else @@ -118,7 +118,7 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) /* Set to 2G mode */ bMode = 1; } else { - if ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) && + if ((AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) && ah->is_clk_25mhz) { channelSel = freq / 75; chan_frac = ((freq % 75) * 0x20000) / 75; @@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah, if (IS_CHAN_HT40(chan)) { phymode |= AR_PHY_GC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ - if ((chan->chanmode == CHANNEL_A_HT40PLUS) || - (chan->chanmode == CHANNEL_G_HT40PLUS)) + if (IS_CHAN_HT40PLUS(chan)) phymode |= AR_PHY_GC_DYN2040_PRI_CH; } @@ -565,7 +564,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode); /* Configure MAC for 20/40 operation */ - ath9k_hw_set11nmac2040(ah); + ath9k_hw_set11nmac2040(ah, chan); /* global transmit timeout (25 TUs default)*/ REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); @@ -627,11 +626,10 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) * MAC addr only will fail. */ val = REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); - REG_WRITE(ah, AR_PCU_MISC_MODE2, - val | AR_AGG_WEP_ENABLE_FIX | AR_AGG_WEP_ENABLE); - - REG_SET_BIT(ah, AR_PHY_CCK_DETECT, - AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + val |= AR_AGG_WEP_ENABLE_FIX | + AR_AGG_WEP_ENABLE | + AR_PCU_MISC_MODE2_CFP_IGNORE; + REG_WRITE(ah, AR_PCU_MISC_MODE2, val); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, @@ -643,11 +641,12 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) else ah->enabled_cals &= ~TX_IQ_CAL; - if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) - ah->enabled_cals |= TX_CL_CAL; - else - ah->enabled_cals &= ~TX_CL_CAL; } + + if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) + ah->enabled_cals |= TX_CL_CAL; + else + ah->enabled_cals &= ~TX_CL_CAL; } static void ar9003_hw_prog_ini(struct ath_hw *ah, @@ -683,43 +682,72 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah, { int ret; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - if (chan->channel <= 5350) - ret = 1; - else if ((chan->channel > 5350) && (chan->channel <= 5600)) - ret = 3; + if (IS_CHAN_2GHZ(chan)) { + if (IS_CHAN_HT40(chan)) + return 7; else - ret = 5; - break; + return 8; + } - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - if (chan->channel <= 5350) - ret = 2; - else if ((chan->channel > 5350) && (chan->channel <= 5600)) - ret = 4; - else - ret = 6; - break; + if (chan->channel <= 5350) + ret = 1; + else if ((chan->channel > 5350) && (chan->channel <= 5600)) + ret = 3; + else + ret = 5; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - ret = 8; - break; + if (IS_CHAN_HT40(chan)) + ret++; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - ret = 7; - break; + return ret; +} - default: - ret = -EINVAL; +static void ar9003_doubler_fix(struct ath_hw *ah) +{ + if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) { + REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); + REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); + REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S, 0); + + udelay(200); + + REG_CLR_BIT(ah, AR_PHY_65NM_CH0_RXTX2, + AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); + REG_CLR_BIT(ah, AR_PHY_65NM_CH1_RXTX2, + AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); + REG_CLR_BIT(ah, AR_PHY_65NM_CH2_RXTX2, + AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK); + + udelay(1); + + REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_RXTX2, + AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); + REG_RMW_FIELD(ah, AR_PHY_65NM_CH1_RXTX2, + AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); + REG_RMW_FIELD(ah, AR_PHY_65NM_CH2_RXTX2, + AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK, 1); + + udelay(200); + + REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_SYNTH12, + AR_PHY_65NM_CH0_SYNTH12_VREFMUL3, 0xf); + + REG_RMW(ah, AR_PHY_65NM_CH0_RXTX2, 0, + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); + REG_RMW(ah, AR_PHY_65NM_CH1_RXTX2, 0, + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); + REG_RMW(ah, AR_PHY_65NM_CH2_RXTX2, 0, + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S | + 1 << AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S); } - - return ret; } static int ar9003_hw_process_ini(struct ath_hw *ah, @@ -728,28 +756,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, unsigned int regWrites = 0, i; u32 modesIndex; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; - break; - - default: - return -EINVAL; - } + if (IS_CHAN_5GHZ(chan)) + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + else + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; /* * SOC, MAC, BB, RADIO initvals. @@ -765,6 +775,8 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, modesIndex); } + ar9003_doubler_fix(ah); + /* * RXGAIN initvals. */ @@ -798,10 +810,12 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, /* * TXGAIN initvals. */ - if (AR_SREV_9550(ah)) { - int modes_txgain_index; + if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + int modes_txgain_index = 1; + + if (AR_SREV_9550(ah)) + modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan); - modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan); if (modes_txgain_index < 0) return -EINVAL; @@ -847,15 +861,13 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah, if (chan == NULL) return; - rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) - ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + if (IS_CHAN_2GHZ(chan)) + rfMode |= AR_PHY_MODE_DYNAMIC; + else + rfMode |= AR_PHY_MODE_OFDM; if (IS_CHAN_A_FAST_CLOCK(ah, chan)) rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); - if (IS_CHAN_QUARTER_RATE(chan)) - rfMode |= AR_PHY_MODE_QUARTER; - if (IS_CHAN_HALF_RATE(chan)) - rfMode |= AR_PHY_MODE_HALF; if (rfMode & (AR_PHY_MODE_QUARTER | AR_PHY_MODE_HALF)) REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, @@ -1274,12 +1286,11 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState = &ah->ani; iniDef = &aniState->iniDef; - ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", + ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, - chan->channel, - chan->channelFlags); + chan->channel); val = REG_READ(ah, AR_PHY_SFCORR); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); @@ -1319,6 +1330,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) static void ar9003_hw_set_radar_params(struct ath_hw *ah, struct ath_hw_radar_conf *conf) { + unsigned int regWrites = 0; u32 radar_0 = 0, radar_1 = 0; if (!conf) { @@ -1345,6 +1357,11 @@ static void ar9003_hw_set_radar_params(struct ath_hw *ah, REG_SET_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); else REG_CLR_BIT(ah, AR_PHY_RADAR_EXT, AR_PHY_RADAR_EXT_ENA); + + if (AR_SREV_9300(ah) || AR_SREV_9340(ah) || AR_SREV_9580(ah)) { + REG_WRITE_ARRAY(&ah->ini_dfs, + IS_CHAN_HT40(ah->curchan) ? 2 : 1, regWrites); + } } static void ar9003_hw_set_radar_conf(struct ath_hw *ah) @@ -1375,15 +1392,19 @@ static void ar9003_hw_antdiv_comb_conf_get(struct ath_hw *ah, AR_PHY_ANT_FAST_DIV_BIAS_S; if (AR_SREV_9330_11(ah)) { + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -9; antconf->div_group = 1; } else if (AR_SREV_9485(ah)) { + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -9; antconf->div_group = 2; } else if (AR_SREV_9565(ah)) { - antconf->lna1_lna2_delta = -3; + antconf->lna1_lna2_switch_delta = 3; + antconf->lna1_lna2_delta = -9; antconf->div_group = 3; } else { + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -3; antconf->div_group = 0; } @@ -1489,17 +1510,24 @@ static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) } else if (AR_SREV_9565(ah)) { if (enable) { REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, + AR_ANT_DIV_ENABLE); + REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, (1 << AR_PHY_ANT_SW_RX_PROT_S)); - if (ah->curchan && IS_CHAN_2GHZ(ah->curchan)) - REG_SET_BIT(ah, AR_PHY_RESTART, - AR_PHY_RESTART_ENABLE_DIV_M2FLAG); + REG_SET_BIT(ah, AR_PHY_CCK_DETECT, + AR_FAST_DIV_ENABLE); + REG_SET_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); } else { - REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, + AR_ANT_DIV_ENABLE); REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, (1 << AR_PHY_ANT_SW_RX_PROT_S)); - REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, + AR_FAST_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); @@ -1526,28 +1554,10 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, unsigned int regWrites = 0; u32 modesIndex; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; - break; - - default: - return -EINVAL; - } + if (IS_CHAN_5GHZ(chan)) + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + else + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; if (modesIndex == ah->modes_index) { *ini_reloaded = false; @@ -1662,6 +1672,98 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */ + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9003_hw_tx99_stop(struct ath_hw *ah) +{ + REG_CLR_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + +static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower) +{ + static s16 p_pwr_array[ar9300RateSize] = { 0 }; + unsigned int i; + + if (txpower <= MAX_RATE_POWER) { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = txpower; + } else { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = MAX_RATE_POWER; + } + + REG_WRITE(ah, 0xa458, 0); + + REG_WRITE(ah, 0xa3c0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3cc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3d0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8)| + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0)); + REG_WRITE(ah, 0xa3d4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_6], 0)); + REG_WRITE(ah, 0xa3e4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_14], 0)); + REG_WRITE(ah, 0xa3e8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_23], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_22], 0)); + REG_WRITE(ah, 0xa3d8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0)); + REG_WRITE(ah, 0xa3dc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_6], 0)); + REG_WRITE(ah, 0xa3ec, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0)); +} + void ar9003_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -1701,12 +1803,77 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9003_hw_tx99_start; + ops->tx99_stop = ar9003_hw_tx99_stop; + ops->tx99_set_txpower = ar9003_hw_tx99_set_txpower; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs)); } +/* + * Baseband Watchdog signatures: + * + * 0x04000539: BB hang when operating in HT40 DFS Channel. + * Full chip reset is not required, but a recovery + * mechanism is needed. + * + * 0x1300000a: Related to CAC deafness. + * Chip reset is not required. + * + * 0x0400000a: Related to CAC deafness. + * Full chip reset is required. + * + * 0x04000b09: RX state machine gets into an illegal state + * when a packet with unsupported rate is received. + * Full chip reset is required and PHY_RESTART has + * to be disabled. + * + * 0x04000409: Packet stuck on receive. + * Full chip reset is required for all chips except AR9340. + */ + +/* + * ar9003_hw_bb_watchdog_check(): Returns true if a chip reset is required. + */ +bool ar9003_hw_bb_watchdog_check(struct ath_hw *ah) +{ + u32 val; + + switch(ah->bb_watchdog_last_status) { + case 0x04000539: + val = REG_READ(ah, AR_PHY_RADAR_0); + val &= (~AR_PHY_RADAR_0_FIRPWR); + val |= SM(0x7f, AR_PHY_RADAR_0_FIRPWR); + REG_WRITE(ah, AR_PHY_RADAR_0, val); + udelay(1); + val = REG_READ(ah, AR_PHY_RADAR_0); + val &= ~AR_PHY_RADAR_0_FIRPWR; + val |= SM(AR9300_DFS_FIRPWR, AR_PHY_RADAR_0_FIRPWR); + REG_WRITE(ah, AR_PHY_RADAR_0, val); + + return false; + case 0x1300000a: + return false; + case 0x0400000a: + case 0x04000b09: + return true; + case 0x04000409: + if (AR_SREV_9340(ah) || AR_SREV_9531(ah)) + return false; + else + return true; + default: + /* + * For any other unknown signatures, do a + * full chip reset. + */ + return true; + } +} +EXPORT_SYMBOL(ar9003_hw_bb_watchdog_check); + void ar9003_hw_bb_watchdog_config(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1823,6 +1990,7 @@ EXPORT_SYMBOL(ar9003_hw_bb_watchdog_dbg_info); void ar9003_hw_disable_phy_restart(struct ath_hw *ah) { + u8 result; u32 val; /* While receiving unsupported rate frame rx state machine @@ -1830,15 +1998,13 @@ void ar9003_hw_disable_phy_restart(struct ath_hw *ah) * state, BB would go hang. If RXSM is in 0xb state after * first bb panic, ensure to disable the phy_restart. */ - if (!((MS(ah->bb_watchdog_last_status, - AR_PHY_WATCHDOG_RX_OFDM_SM) == 0xb) || - ah->bb_hang_rx_ofdm)) - return; + result = MS(ah->bb_watchdog_last_status, AR_PHY_WATCHDOG_RX_OFDM_SM); - ah->bb_hang_rx_ofdm = true; - val = REG_READ(ah, AR_PHY_RESTART); - val &= ~AR_PHY_RESTART_ENA; - - REG_WRITE(ah, AR_PHY_RESTART, val); + if ((result == 0xb) || ah->bb_hang_rx_ofdm) { + ah->bb_hang_rx_ofdm = true; + val = REG_READ(ah, AR_PHY_RESTART); + val &= ~AR_PHY_RESTART_ENA; + REG_WRITE(ah, AR_PHY_RESTART, val); + } } EXPORT_SYMBOL(ar9003_hw_disable_phy_restart); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 6fd752321e3..fd090b1f2d0 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -270,7 +270,7 @@ #define AR_PHY_AGC (AR_AGC_BASE + 0x14) #define AR_PHY_EXT_ATTEN_CTL_0 (AR_AGC_BASE + 0x18) #define AR_PHY_CCA_0 (AR_AGC_BASE + 0x1c) -#define AR_PHY_EXT_CCA0 (AR_AGC_BASE + 0x20) +#define AR_PHY_CCA_CTRL_0 (AR_AGC_BASE + 0x20) #define AR_PHY_RESTART (AR_AGC_BASE + 0x24) /* @@ -338,13 +338,17 @@ #define AR_PHY_CCA_NOM_VAL_9300_5GHZ -115 #define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ -125 #define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ -125 -#define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ -95 -#define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ -100 +#define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ -60 +#define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ -60 +#define AR_PHY_CCA_MAX_GOOD_VAL_9300_FCC_2GHZ -95 +#define AR_PHY_CCA_MAX_GOOD_VAL_9300_FCC_5GHZ -100 #define AR_PHY_CCA_NOM_VAL_9462_2GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ -127 +#define AR_PHY_CCA_MAX_GOOD_VAL_9462_2GHZ -60 #define AR_PHY_CCA_NOM_VAL_9462_5GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ -127 +#define AR_PHY_CCA_MAX_GOOD_VAL_9462_5GHZ -60 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118 @@ -393,6 +397,8 @@ #define AR9280_PHY_CCA_THRESH62_S 12 #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF #define AR_PHY_EXT_CCA0_THRESH62_S 0 +#define AR_PHY_EXT_CCA0_THRESH62_1 0x000001FF +#define AR_PHY_EXT_CCA0_THRESH62_1_S 0 #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0 @@ -652,13 +658,34 @@ #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x00000001 : 0x00000002) #define AR_PHY_SYNTH4_LONG_SHIFT_SELECT_S ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0 : 1) #define AR_PHY_65NM_CH0_SYNTH7 0x16098 +#define AR_PHY_65NM_CH0_SYNTH12 0x160ac #define AR_PHY_65NM_CH0_BIAS1 0x160c0 #define AR_PHY_65NM_CH0_BIAS2 0x160c4 #define AR_PHY_65NM_CH0_BIAS4 0x160cc +#define AR_PHY_65NM_CH0_RXTX2 0x16104 +#define AR_PHY_65NM_CH1_RXTX2 0x16504 +#define AR_PHY_65NM_CH2_RXTX2 0x16904 #define AR_PHY_65NM_CH0_RXTX4 0x1610c #define AR_PHY_65NM_CH1_RXTX4 0x1650c #define AR_PHY_65NM_CH2_RXTX4 0x1690c +#define AR_PHY_65NM_CH0_BB1 0x16140 +#define AR_PHY_65NM_CH0_BB2 0x16144 +#define AR_PHY_65NM_CH0_BB3 0x16148 +#define AR_PHY_65NM_CH1_BB1 0x16540 +#define AR_PHY_65NM_CH1_BB2 0x16544 +#define AR_PHY_65NM_CH1_BB3 0x16548 +#define AR_PHY_65NM_CH2_BB1 0x16940 +#define AR_PHY_65NM_CH2_BB2 0x16944 +#define AR_PHY_65NM_CH2_BB3 0x16948 + +#define AR_PHY_65NM_CH0_SYNTH12_VREFMUL3 0x00780000 +#define AR_PHY_65NM_CH0_SYNTH12_VREFMUL3_S 19 +#define AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK 0x00000004 +#define AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK_S 2 +#define AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK 0x00000008 +#define AR_PHY_65NM_CH0_RXTX2_SYNTHOVR_MASK_S 3 + #define AR_CH0_TOP (AR_SREV_9300(ah) ? 0x16288 : \ (((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x1628c : 0x16280))) #define AR_CH0_TOP_XPABIASLVL (AR_SREV_9550(ah) ? 0x3c0 : 0x300) @@ -1316,4 +1343,6 @@ #define AR_PHY_65NM_RXRF_AGC_AGC_OUT 0x00000004 #define AR_PHY_65NM_RXRF_AGC_AGC_OUT_S 2 +#define AR9300_DFS_FIRPWR -28 + #endif /* AR9003_PHY_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c index 74de3539c2c..934418872e8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c @@ -118,6 +118,27 @@ void ar9003_hw_rtt_load_hist(struct ath_hw *ah) } } +static void ar9003_hw_patch_rtt(struct ath_hw *ah, int index, int chain) +{ + int agc, caldac; + + if (!test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) + return; + + if ((index != 5) || (chain >= 2)) + return; + + agc = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE); + if (!agc) + return; + + caldac = ah->caldata->caldac[chain]; + ah->caldata->rtt_table[chain][index] &= 0xFFFF05FF; + caldac = (caldac & 0x20) | ((caldac & 0x1F) << 7); + ah->caldata->rtt_table[chain][index] |= (caldac << 4); +} + static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index) { u32 val; @@ -155,13 +176,16 @@ void ar9003_hw_rtt_fill_hist(struct ath_hw *ah) for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { ah->caldata->rtt_table[chain][i] = ar9003_hw_rtt_fill_hist_entry(ah, chain, i); + + ar9003_hw_patch_rtt(ah, i, chain); + ath_dbg(ath9k_hw_common(ah), CALIBRATE, "RTT value at idx %d, chain %d is: 0x%x\n", i, chain, ah->caldata->rtt_table[chain][i]); } } - ah->caldata->rtt_done = true; + set_bit(RTT_DONE, &ah->caldata->cal_flags); } void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) @@ -176,7 +200,7 @@ void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) } if (ah->caldata) - ah->caldata->rtt_done = false; + clear_bit(RTT_DONE, &ah->caldata->cal_flags); } bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) @@ -186,11 +210,37 @@ bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) if (!ah->caldata) return false; - if (!ah->caldata->rtt_done) + if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) { + if (IS_CHAN_2GHZ(chan)){ + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, + ah->caldata->caldac[0]); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, + ah->caldata->caldac[1]); + } else { + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, + ah->caldata->caldac[0]); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, + ah->caldata->caldac[1]); + } + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); + } + + if (!test_bit(RTT_DONE, &ah->caldata->cal_flags)) return false; ar9003_hw_rtt_enable(ah); - ar9003_hw_rtt_set_mask(ah, 0x10); + + if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) + ar9003_hw_rtt_set_mask(ah, 0x30); + else + ar9003_hw_rtt_set_mask(ah, 0x10); if (!ath9k_hw_rfbus_req(ah)) { ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_wow.c b/drivers/net/wireless/ath/ath9k/ar9003_wow.c new file mode 100644 index 00000000000..81c88dd606d --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9003_wow.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/export.h> +#include "ath9k.h" +#include "reg.h" +#include "hw-ops.h" + +const char *ath9k_hw_wow_event_to_string(u32 wow_event) +{ + if (wow_event & AH_WOW_MAGIC_PATTERN_EN) + return "Magic pattern"; + if (wow_event & AH_WOW_USER_PATTERN_EN) + return "User pattern"; + if (wow_event & AH_WOW_LINK_CHANGE) + return "Link change"; + if (wow_event & AH_WOW_BEACON_MISS) + return "Beacon miss"; + + return "unknown reason"; +} +EXPORT_SYMBOL(ath9k_hw_wow_event_to_string); + +static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + + /* set rx disable bit */ + REG_WRITE(ah, AR_CR, AR_CR_RXD); + + if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) { + ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", + REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); + return; + } + + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); +} + +static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + u8 sta_mac_addr[ETH_ALEN], ap_mac_addr[ETH_ALEN]; + u32 ctl[13] = {0}; + u32 data_word[KAL_NUM_DATA_WORDS]; + u8 i; + u32 wow_ka_data_word0; + + memcpy(sta_mac_addr, common->macaddr, ETH_ALEN); + memcpy(ap_mac_addr, common->curbssid, ETH_ALEN); + + /* set the transmit buffer */ + ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16)); + ctl[1] = 0; + ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */ + ctl[4] = 0; + ctl[7] = (ah->txchainmask) << 2; + ctl[2] = 0xf << 16; /* tx_tries 0 */ + + for (i = 0; i < KAL_NUM_DESC_WORDS; i++) + REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); + + REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); + + data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) | + (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16); + data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) | + (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); + data_word[2] = (sta_mac_addr[1] << 24) | (sta_mac_addr[0] << 16) | + (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); + data_word[3] = (sta_mac_addr[5] << 24) | (sta_mac_addr[4] << 16) | + (sta_mac_addr[3] << 8) | (sta_mac_addr[2]); + data_word[4] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) | + (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); + data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); + + if (AR_SREV_9462_20(ah)) { + /* AR9462 2.0 has an extra descriptor word (time based + * discard) compared to other chips */ + REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0); + wow_ka_data_word0 = AR_WOW_TXBUF(13); + } else { + wow_ka_data_word0 = AR_WOW_TXBUF(12); + } + + for (i = 0; i < KAL_NUM_DATA_WORDS; i++) + REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]); + +} + +void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, + u8 *user_mask, int pattern_count, + int pattern_len) +{ + int i; + u32 pattern_val, mask_val; + u32 set, clr; + + /* FIXME: should check count by querying the hardware capability */ + if (pattern_count >= MAX_NUM_PATTERN) + return; + + REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count)); + + /* set the registers for pattern */ + for (i = 0; i < MAX_PATTERN_SIZE; i += 4) { + memcpy(&pattern_val, user_pattern, 4); + REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i), + pattern_val); + user_pattern += 4; + } + + /* set the registers for mask */ + for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) { + memcpy(&mask_val, user_mask, 4); + REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val); + user_mask += 4; + } + + /* set the pattern length to be matched + * + * AR_WOW_LENGTH1_REG1 + * bit 31:24 pattern 0 length + * bit 23:16 pattern 1 length + * bit 15:8 pattern 2 length + * bit 7:0 pattern 3 length + * + * AR_WOW_LENGTH1_REG2 + * bit 31:24 pattern 4 length + * bit 23:16 pattern 5 length + * bit 15:8 pattern 6 length + * bit 7:0 pattern 7 length + * + * the below logic writes out the new + * pattern length for the corresponding + * pattern_count, while masking out the + * other fields + */ + + ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); + + if (pattern_count < 4) { + /* Pattern 0-3 uses AR_WOW_LENGTH1 register */ + set = (pattern_len & AR_WOW_LENGTH_MAX) << + AR_WOW_LEN1_SHIFT(pattern_count); + clr = AR_WOW_LENGTH1_MASK(pattern_count); + REG_RMW(ah, AR_WOW_LENGTH1, set, clr); + } else { + /* Pattern 4-7 uses AR_WOW_LENGTH2 register */ + set = (pattern_len & AR_WOW_LENGTH_MAX) << + AR_WOW_LEN2_SHIFT(pattern_count); + clr = AR_WOW_LENGTH2_MASK(pattern_count); + REG_RMW(ah, AR_WOW_LENGTH2, set, clr); + } + +} +EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern); + +u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) +{ + u32 wow_status = 0; + u32 val = 0, rval; + + /* + * read the WoW status register to know + * the wakeup reason + */ + rval = REG_READ(ah, AR_WOW_PATTERN); + val = AR_WOW_STATUS(rval); + + /* + * mask only the WoW events that we have enabled. Sometimes + * we have spurious WoW events from the AR_WOW_PATTERN + * register. This mask will clean it up. + */ + + val &= ah->wow_event_mask; + + if (val) { + if (val & AR_WOW_MAGIC_PAT_FOUND) + wow_status |= AH_WOW_MAGIC_PATTERN_EN; + if (AR_WOW_PATTERN_FOUND(val)) + wow_status |= AH_WOW_USER_PATTERN_EN; + if (val & AR_WOW_KEEP_ALIVE_FAIL) + wow_status |= AH_WOW_LINK_CHANGE; + if (val & AR_WOW_BEACON_FAIL) + wow_status |= AH_WOW_BEACON_MISS; + } + + /* + * set and clear WOW_PME_CLEAR registers for the chip to + * generate next wow signal. + * disable D3 before accessing other registers ? + */ + + /* do we need to check the bit value 0x01000000 (7-10) ?? */ + REG_RMW(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR, + AR_PMCTRL_PWR_STATE_D1D3); + + /* + * clear all events + */ + REG_WRITE(ah, AR_WOW_PATTERN, + AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN))); + + /* + * restore the beacon threshold to init value + */ + REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); + + /* + * Restore the way the PCI-E reset, Power-On-Reset, external + * PCIE_POR_SHORT pins are tied to its original value. + * Previously just before WoW sleep, we untie the PCI-E + * reset to our Chip's Power On Reset so that any PCI-E + * reset from the bus will not reset our chip + */ + if (ah->is_pciexpress) + ath9k_hw_configpcipowersave(ah, false); + + ah->wow_event_mask = 0; + + return wow_status; +} +EXPORT_SYMBOL(ath9k_hw_wow_wakeup); + +void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) +{ + u32 wow_event_mask; + u32 set, clr; + + /* + * wow_event_mask is a mask to the AR_WOW_PATTERN register to + * indicate which WoW events we have enabled. The WoW events + * are from the 'pattern_enable' in this function and + * 'pattern_count' of ath9k_hw_wow_apply_pattern() + */ + wow_event_mask = ah->wow_event_mask; + + /* + * Untie Power-on-Reset from the PCI-E-Reset. When we are in + * WOW sleep, we do want the Reset from the PCI-E to disturb + * our hw state + */ + if (ah->is_pciexpress) { + /* + * we need to untie the internal POR (power-on-reset) + * to the external PCI-E reset. We also need to tie + * the PCI-E Phy reset to the PCI-E reset. + */ + set = AR_WA_RESET_EN | AR_WA_POR_SHORT; + clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE; + REG_RMW(ah, AR_WA, set, clr); + } + + /* + * set the power states appropriately and enable PME + */ + set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA | + AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR; + + /* + * set and clear WOW_PME_CLEAR registers for the chip + * to generate next wow signal. + */ + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); + clr = AR_PMCTRL_WOW_PME_CLR; + REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); + + /* + * Setup for: + * - beacon misses + * - magic pattern + * - keep alive timeout + * - pattern matching + */ + + /* + * Program default values for pattern backoff, aifs/slot/KAL count, + * beacon miss timeout, KAL timeout, etc. + */ + set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF); + REG_SET_BIT(ah, AR_WOW_PATTERN, set); + + set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | + AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | + AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT); + REG_SET_BIT(ah, AR_WOW_COUNT, set); + + if (pattern_enable & AH_WOW_BEACON_MISS) + set = AR_WOW_BEACON_TIMO; + /* We are not using beacon miss, program a large value */ + else + set = AR_WOW_BEACON_TIMO_MAX; + + REG_WRITE(ah, AR_WOW_BCN_TIMO, set); + + /* + * Keep alive timo in ms except AR9280 + */ + if (!pattern_enable) + set = AR_WOW_KEEP_ALIVE_NEVER; + else + set = KAL_TIMEOUT * 32; + + REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set); + + /* + * Keep alive delay in us. based on 'power on clock', + * therefore in usec + */ + set = KAL_DELAY * 1000; + REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set); + + /* + * Create keep alive pattern to respond to beacons + */ + ath9k_wow_create_keep_alive_pattern(ah); + + /* + * Configure MAC WoW Registers + */ + set = 0; + /* Send keep alive timeouts anyway */ + clr = AR_WOW_KEEP_ALIVE_AUTO_DIS; + + if (pattern_enable & AH_WOW_LINK_CHANGE) + wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL; + else + set = AR_WOW_KEEP_ALIVE_FAIL_DIS; + + set = AR_WOW_KEEP_ALIVE_FAIL_DIS; + REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr); + + /* + * we are relying on a bmiss failure. ensure we have + * enough threshold to prevent false positives + */ + REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, + AR_WOW_BMISSTHRESHOLD); + + set = 0; + clr = 0; + + if (pattern_enable & AH_WOW_BEACON_MISS) { + set = AR_WOW_BEACON_FAIL_EN; + wow_event_mask |= AR_WOW_BEACON_FAIL; + } else { + clr = AR_WOW_BEACON_FAIL_EN; + } + + REG_RMW(ah, AR_WOW_BCN_EN, set, clr); + + set = 0; + clr = 0; + /* + * Enable the magic packet registers + */ + if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) { + set = AR_WOW_MAGIC_EN; + wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND; + } else { + clr = AR_WOW_MAGIC_EN; + } + set |= AR_WOW_MAC_INTR_EN; + REG_RMW(ah, AR_WOW_PATTERN, set, clr); + + REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B, + AR_WOW_PATTERN_SUPPORTED); + + /* + * Set the power states appropriately and enable PME + */ + clr = 0; + set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | + AR_PMCTRL_PWR_PM_CTRL_ENA; + + clr = AR_PCIE_PM_CTRL_ENA; + REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr); + + /* + * this is needed to prevent the chip waking up + * the host within 3-4 seconds with certain + * platform/BIOS. The fix is to enable + * D1 & D3 to match original definition and + * also match the OTP value. Anyway this + * is more related to SW WOW. + */ + clr = AR_PMCTRL_PWR_STATE_D1D3; + REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); + + set = AR_PMCTRL_PWR_STATE_D1D3_REAL; + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); + + REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); + + /* to bring down WOW power low margin */ + set = BIT(13); + REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); + /* HW WoW */ + clr = BIT(5); + REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr); + + ath9k_hw_set_powermode_wow_sleep(ah); + ah->wow_event_mask = wow_event_mask; +} +EXPORT_SYMBOL(ath9k_hw_wow_enable); diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h index 6e1756bc383..2c42ff05efa 100644 --- a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h @@ -18,6 +18,10 @@ #ifndef INITVALS_9330_1P1_H #define INITVALS_9330_1P1_H +#define ar9331_1p1_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 + +#define ar9331_modes_high_power_tx_gain_1p1 ar9331_modes_lowest_ob_db_tx_gain_1p1 + static const u32 ar9331_1p1_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, @@ -55,7 +59,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18}, {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -252,7 +256,7 @@ static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = { {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000}, {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000}, - {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0}, + {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d4, 0x000050d4}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, @@ -337,8 +341,6 @@ static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = { {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, }; -#define ar9331_1p1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 - static const u32 ar9331_1p1_xtal_25M[][2] = { /* Addr allmodes */ {0x00007038, 0x000002f8}, @@ -373,17 +375,17 @@ static const u32 ar9331_1p1_radio_core[][2] = { {0x000160b4, 0x92480040}, {0x000160c0, 0x006db6db}, {0x000160c4, 0x0186db60}, - {0x000160c8, 0x6db4db6c}, + {0x000160c8, 0x6db6db6c}, {0x000160cc, 0x6de6c300}, {0x000160d0, 0x14500820}, {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80015}, {0x00016108, 0x00080010}, {0x0001610c, 0x00170000}, - {0x00016140, 0x10800000}, + {0x00016140, 0x50804000}, {0x00016144, 0x01884080}, {0x00016148, 0x000080c0}, - {0x00016280, 0x01000015}, + {0x00016280, 0x01001015}, {0x00016284, 0x14d20000}, {0x00016288, 0x00318000}, {0x0001628c, 0x50000000}, @@ -590,7 +592,7 @@ static const u32 ar9331_1p1_baseband_core[][2] = { {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x0d261820}, + {0x00009e40, 0x0d261800}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009fc0, 0x803e4788}, @@ -622,12 +624,12 @@ static const u32 ar9331_1p1_baseband_core[][2] = { {0x0000a370, 0x00000000}, {0x0000a390, 0x00000001}, {0x0000a394, 0x00000444}, - {0x0000a398, 0x001f0e0f}, - {0x0000a39c, 0x0075393f}, - {0x0000a3a0, 0xb79f6427}, - {0x0000a3a4, 0x00000000}, - {0x0000a3a8, 0xaaaaaaaa}, - {0x0000a3ac, 0x3c466478}, + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x210d0401}, + {0x0000a3a0, 0xab9a7144}, + {0x0000a3a4, 0x00000011}, + {0x0000a3a8, 0x3c3c003d}, + {0x0000a3ac, 0x30310030}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, @@ -686,100 +688,18 @@ static const u32 ar9331_1p1_baseband_core[][2] = { {0x0000a7dc, 0x00000001}, }; -static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = { +static const u32 ar9331_1p1_mac_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, - {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, - {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, - {0x0000a2e4, 0xfffff000, 0xfffff000, 0xfffff000, 0xfffff000}, - {0x0000a2e8, 0xfffe0000, 0xfffe0000, 0xfffe0000, 0xfffe0000}, - {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d0, 0x000050d0}, - {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, - {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, - {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, - {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, - {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, - {0x0000a520, 0x2f001f04, 0x2f001f04, 0x23000a00, 0x23000a00}, - {0x0000a524, 0x35001fc4, 0x35001fc4, 0x27000a02, 0x27000a02}, - {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2b000a04, 0x2b000a04}, - {0x0000a52c, 0x41023e85, 0x41023e85, 0x2d000a20, 0x2d000a20}, - {0x0000a530, 0x48023ec6, 0x48023ec6, 0x31000a22, 0x31000a22}, - {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000a24, 0x35000a24}, - {0x0000a538, 0x53023f4b, 0x53023f4b, 0x38000a43, 0x38000a43}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x3b000e42, 0x3b000e42}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x3f000e44, 0x3f000e44}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x42000e64, 0x42000e64}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x46000e66, 0x46000e66}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x4a000ea6, 0x4a000ea6}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x4a000ea6, 0x4a000ea6}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x4a000ea6, 0x4a000ea6}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x4a000ea6, 0x4a000ea6}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x4a000ea6, 0x4a000ea6}, - {0x0000a580, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, - {0x0000a584, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, - {0x0000a588, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, - {0x0000a58c, 0x11062202, 0x11062202, 0x0b000200, 0x0b000200}, - {0x0000a590, 0x17022e00, 0x17022e00, 0x0f000202, 0x0f000202}, - {0x0000a594, 0x1d000ec2, 0x1d000ec2, 0x11000400, 0x11000400}, - {0x0000a598, 0x25020ec0, 0x25020ec0, 0x15000402, 0x15000402}, - {0x0000a59c, 0x2b020ec3, 0x2b020ec3, 0x19000404, 0x19000404}, - {0x0000a5a0, 0x2f001f04, 0x2f001f04, 0x1b000603, 0x1b000603}, - {0x0000a5a4, 0x35001fc4, 0x35001fc4, 0x1f000a02, 0x1f000a02}, - {0x0000a5a8, 0x3c022f04, 0x3c022f04, 0x23000a04, 0x23000a04}, - {0x0000a5ac, 0x41023e85, 0x41023e85, 0x26000a20, 0x26000a20}, - {0x0000a5b0, 0x48023ec6, 0x48023ec6, 0x2a000e20, 0x2a000e20}, - {0x0000a5b4, 0x4d023f01, 0x4d023f01, 0x2e000e22, 0x2e000e22}, - {0x0000a5b8, 0x53023f4b, 0x53023f4b, 0x31000e24, 0x31000e24}, - {0x0000a5bc, 0x5a027f09, 0x5a027f09, 0x34001640, 0x34001640}, - {0x0000a5c0, 0x5f027fc9, 0x5f027fc9, 0x38001660, 0x38001660}, - {0x0000a5c4, 0x6502feca, 0x6502feca, 0x3b001861, 0x3b001861}, - {0x0000a5c8, 0x6b02ff4a, 0x6b02ff4a, 0x3e001a81, 0x3e001a81}, - {0x0000a5cc, 0x7203feca, 0x7203feca, 0x42001a83, 0x42001a83}, - {0x0000a5d0, 0x7703ff0b, 0x7703ff0b, 0x44001c84, 0x44001c84}, - {0x0000a5d4, 0x7d06ffcb, 0x7d06ffcb, 0x48001ce3, 0x48001ce3}, - {0x0000a5d8, 0x8407ff0b, 0x8407ff0b, 0x4c001ce5, 0x4c001ce5}, - {0x0000a5dc, 0x8907ffcb, 0x8907ffcb, 0x50001ce9, 0x50001ce9}, - {0x0000a5e0, 0x900fff0b, 0x900fff0b, 0x54001ceb, 0x54001ceb}, - {0x0000a5e4, 0x960fffcb, 0x960fffcb, 0x56001eec, 0x56001eec}, - {0x0000a5e8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5ec, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f0, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f4, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5f8, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a5fc, 0x9c1fff0b, 0x9c1fff0b, 0x56001eec, 0x56001eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, - {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, - {0x0000a61c, 0x02008802, 0x02008802, 0x02008802, 0x02008802}, - {0x0000a620, 0x0280c802, 0x0280c802, 0x0280c802, 0x0280c802}, - {0x0000a624, 0x03010a03, 0x03010a03, 0x03010a03, 0x03010a03}, - {0x0000a628, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, - {0x0000a62c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, - {0x0000a630, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, - {0x0000a634, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, - {0x0000a638, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, - {0x0000a63c, 0x03010c04, 0x03010c04, 0x03010c04, 0x03010c04}, - {0x00016044, 0x034922db, 0x034922db, 0x034922db, 0x034922db}, - {0x00016284, 0x14d3f000, 0x14d3f000, 0x14d3f000, 0x14d3f000}, + {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, + {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, + {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, + {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, + {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, + {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, + {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, + {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, }; -#define ar9331_1p1_mac_postamble ar9300_2p2_mac_postamble - static const u32 ar9331_1p1_soc_preamble[][2] = { /* Addr allmodes */ {0x00007020, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h index 57ed8a11217..2154efcd390 100644 --- a/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9330_1p2_initvals.h @@ -18,6 +18,28 @@ #ifndef INITVALS_9330_1P2_H #define INITVALS_9330_1P2_H +#define ar9331_modes_high_power_tx_gain_1p2 ar9331_modes_high_ob_db_tx_gain_1p2 + +#define ar9331_modes_low_ob_db_tx_gain_1p2 ar9331_modes_high_ob_db_tx_gain_1p2 + +#define ar9331_modes_lowest_ob_db_tx_gain_1p2 ar9331_modes_high_ob_db_tx_gain_1p2 + +#define ar9331_1p2_baseband_core_txfir_coeff_japan_2484 ar9331_1p1_baseband_core_txfir_coeff_japan_2484 + +#define ar9331_1p2_xtal_25M ar9331_1p1_xtal_25M + +#define ar9331_1p2_xtal_40M ar9331_1p1_xtal_40M + +#define ar9331_1p2_soc_postamble ar9331_1p1_soc_postamble + +#define ar9331_1p2_mac_postamble ar9331_1p1_mac_postamble + +#define ar9331_1p2_soc_preamble ar9331_1p1_soc_preamble + +#define ar9331_1p2_mac_core ar9331_1p1_mac_core + +#define ar9331_common_wo_xlna_rx_gain_1p2 ar9331_common_wo_xlna_rx_gain_1p1 + static const u32 ar9331_modes_high_ob_db_tx_gain_1p2[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a410, 0x000050d7, 0x000050d7, 0x000050d7, 0x000050d7}, @@ -103,57 +125,6 @@ static const u32 ar9331_modes_high_ob_db_tx_gain_1p2[][5] = { {0x0000a63c, 0x04011004, 0x04011004, 0x04011004, 0x04011004}, }; -#define ar9331_modes_high_power_tx_gain_1p2 ar9331_modes_high_ob_db_tx_gain_1p2 - -#define ar9331_modes_low_ob_db_tx_gain_1p2 ar9331_modes_high_power_tx_gain_1p2 - -#define ar9331_modes_lowest_ob_db_tx_gain_1p2 ar9331_modes_low_ob_db_tx_gain_1p2 - -static const u32 ar9331_1p2_baseband_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, - {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, - {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, - {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, - {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, - {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, - {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, - {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4}, - {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, - {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, - {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, - {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, - {0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221}, - {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, - {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, - {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, - {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, - {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0}, - {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, - {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, - {0x0000a234, 0x00000fff, 0x00000fff, 0x10000fff, 0x00000fff}, - {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, - {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, - {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, - {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, - {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, - {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, - {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, - {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, - {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981}, - {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, - {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, - {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, -}; - static const u32 ar9331_1p2_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, @@ -219,24 +190,318 @@ static const u32 ar9331_1p2_radio_core[][2] = { {0x000163d4, 0x00000000}, }; -#define ar9331_1p2_baseband_core_txfir_coeff_japan_2484 ar9331_1p1_baseband_core_txfir_coeff_japan_2484 - -#define ar9331_1p2_xtal_25M ar9331_1p1_xtal_25M - -#define ar9331_1p2_xtal_40M ar9331_1p1_xtal_40M - -#define ar9331_1p2_baseband_core ar9331_1p1_baseband_core - -#define ar9331_1p2_soc_postamble ar9331_1p1_soc_postamble - -#define ar9331_1p2_mac_postamble ar9331_1p1_mac_postamble - -#define ar9331_1p2_soc_preamble ar9331_1p1_soc_preamble - -#define ar9331_1p2_mac_core ar9331_1p1_mac_core +static const u32 ar9331_1p2_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a8f6b}, + {0x0000980c, 0x04800000}, + {0x00009814, 0x9280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x5f3ca3de}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x14750600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32840bbe}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0x00000000}, + {0x00009c08, 0x03200000}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x1883800a}, + {0x00009d10, 0x01834061}, + {0x00009d14, 0x00c00400}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038233c}, + {0x00009e24, 0x9927b515}, + {0x00009e28, 0x12ef0200}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261800}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009fc0, 0x803e4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x0000a20c, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a2a0, 0x00000001}, + {0x0000a2c0, 0x00000001}, + {0x0000a2c8, 0x00000000}, + {0x0000a2cc, 0x18c43433}, + {0x0000a2d4, 0x00000000}, + {0x0000a2dc, 0x00000000}, + {0x0000a2e0, 0x00000000}, + {0x0000a2e4, 0x00000000}, + {0x0000a2e8, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x00000000}, + {0x0000a3a8, 0xaaaaaaaa}, + {0x0000a3ac, 0x3c466478}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000006}, + {0x0000a3f8, 0x0cdbd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce739ce}, + {0x0000a418, 0x2d001dce}, + {0x0000a41c, 0x1ce739ce}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce739ce}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00000000}, + {0x0000a440, 0x00000000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x04000000}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a458, 0x00000000}, + {0x0000a640, 0x00000000}, + {0x0000a644, 0x3fad9d74}, + {0x0000a648, 0x0048060a}, + {0x0000a64c, 0x00003c37}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x09080504}, + {0x0000a678, 0x0d0c0b0a}, + {0x0000a67c, 0x13121110}, + {0x0000a680, 0x31301514}, + {0x0000a684, 0x35343332}, + {0x0000a688, 0x00000036}, + {0x0000a690, 0x00000838}, + {0x0000a7c0, 0x00000000}, + {0x0000a7c4, 0xfffffffc}, + {0x0000a7c8, 0x00000000}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000001}, +}; -#define ar9331_common_wo_xlna_rx_gain_1p2 ar9331_common_wo_xlna_rx_gain_1p1 +static const u32 ar9331_1p2_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, + {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, + {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, + {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, + {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4}, + {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e}, + {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00003221, 0x00003221}, + {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, + {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x00003fc0, 0x00003fc4, 0x00003fc4, 0x00003fc0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, + {0x0000a234, 0x00000fff, 0x00000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb81018, 0xffb81018, 0xffb81018, 0xffb81018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a260, 0x3a021501, 0x3a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, +}; -#define ar9331_common_rx_gain_1p2 ar9485_common_rx_gain_1_1 +static const u32 ar9331_common_rx_gain_1p2[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x01800082}, + {0x0000a014, 0x01820181}, + {0x0000a018, 0x01840183}, + {0x0000a01c, 0x01880185}, + {0x0000a020, 0x018a0189}, + {0x0000a024, 0x02850284}, + {0x0000a028, 0x02890288}, + {0x0000a02c, 0x03850384}, + {0x0000a030, 0x03890388}, + {0x0000a034, 0x038b038a}, + {0x0000a038, 0x038d038c}, + {0x0000a03c, 0x03910390}, + {0x0000a040, 0x03930392}, + {0x0000a044, 0x03950394}, + {0x0000a048, 0x00000396}, + {0x0000a04c, 0x00000000}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x28282828}, + {0x0000a084, 0x28282828}, + {0x0000a088, 0x28282828}, + {0x0000a08c, 0x28282828}, + {0x0000a090, 0x28282828}, + {0x0000a094, 0x21212128}, + {0x0000a098, 0x171c1c1c}, + {0x0000a09c, 0x02020212}, + {0x0000a0a0, 0x00000202}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x111f1100}, + {0x0000a0c8, 0x111d111e}, + {0x0000a0cc, 0x111b111c}, + {0x0000a0d0, 0x22032204}, + {0x0000a0d4, 0x22012202}, + {0x0000a0d8, 0x221f2200}, + {0x0000a0dc, 0x221d221e}, + {0x0000a0e0, 0x33013302}, + {0x0000a0e4, 0x331f3300}, + {0x0000a0e8, 0x4402331e}, + {0x0000a0ec, 0x44004401}, + {0x0000a0f0, 0x441e441f}, + {0x0000a0f4, 0x55015502}, + {0x0000a0f8, 0x551f5500}, + {0x0000a0fc, 0x6602551e}, + {0x0000a100, 0x66006601}, + {0x0000a104, 0x661e661f}, + {0x0000a108, 0x7703661d}, + {0x0000a10c, 0x77017702}, + {0x0000a110, 0x00007700}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x111f1100}, + {0x0000a148, 0x111d111e}, + {0x0000a14c, 0x111b111c}, + {0x0000a150, 0x22032204}, + {0x0000a154, 0x22012202}, + {0x0000a158, 0x221f2200}, + {0x0000a15c, 0x221d221e}, + {0x0000a160, 0x33013302}, + {0x0000a164, 0x331f3300}, + {0x0000a168, 0x4402331e}, + {0x0000a16c, 0x44004401}, + {0x0000a170, 0x441e441f}, + {0x0000a174, 0x55015502}, + {0x0000a178, 0x551f5500}, + {0x0000a17c, 0x6602551e}, + {0x0000a180, 0x66006601}, + {0x0000a184, 0x661e661f}, + {0x0000a188, 0x7703661d}, + {0x0000a18c, 0x77017702}, + {0x0000a190, 0x00007700}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000296}, +}; #endif /* INITVALS_9330_1P2_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h index 25db9215985..b995ffe88b3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9340_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9340_initvals.h @@ -18,6 +18,20 @@ #ifndef INITVALS_9340_H #define INITVALS_9340_H +#define ar9340_1p0_mac_postamble ar9300_2p2_mac_postamble + +#define ar9340_1p0_soc_postamble ar9300_2p2_soc_postamble + +#define ar9340Modes_fast_clock_1p0 ar9300Modes_fast_clock_2p2 + +#define ar9340Common_rx_gain_table_1p0 ar9300Common_rx_gain_table_2p2 + +#define ar9340Common_wo_xlna_rx_gain_table_1p0 ar9300Common_wo_xlna_rx_gain_table_2p2 + +#define ar9340_1p0_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 + +#define ar9340_1p0_baseband_postamble_dfs_channel ar9300_2p2_baseband_postamble_dfs_channel + static const u32 ar9340_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646800, 0xa4646800, 0xa4646800, 0xa4646800}, @@ -100,8 +114,6 @@ static const u32 ar9340Modes_lowest_ob_db_tx_gain_table_1p0[][5] = { {0x00016448, 0x24925266, 0x24925266, 0x24925266, 0x24925266}, }; -#define ar9340Modes_fast_clock_1p0 ar9300Modes_fast_clock_2p2 - static const u32 ar9340_1p0_radio_core[][2] = { /* Addr allmodes */ {0x00016000, 0x36db6db6}, @@ -215,16 +227,12 @@ static const u32 ar9340_1p0_radio_core_40M[][2] = { {0x0000824c, 0x0001e800}, }; -#define ar9340_1p0_mac_postamble ar9300_2p2_mac_postamble - -#define ar9340_1p0_soc_postamble ar9300_2p2_soc_postamble - static const u32 ar9340_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a022e, 0x206a022e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, - {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x00009828, 0x06903081, 0x06903081, 0x09103881, 0x09103881}, {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, @@ -310,7 +318,7 @@ static const u32 ar9340_1p0_baseband_core[][2] = { {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x0d261820}, + {0x00009e40, 0x0d261800}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0x00000000}, @@ -714,266 +722,6 @@ static const u32 ar9340Modes_ub124_tx_gain_table_1p0[][5] = { {0x0000b2e8, 0xfffe0000, 0xfffe0000, 0xfffc0000, 0xfffc0000}, }; -static const u32 ar9340Common_rx_gain_table_1p0[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x01910190}, - {0x0000a030, 0x01930192}, - {0x0000a034, 0x01950194}, - {0x0000a038, 0x038a0196}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, - {0x0000a094, 0x11111717}, - {0x0000a098, 0x00030311}, - {0x0000a09c, 0x00000000}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x23232323}, - {0x0000b084, 0x21232323}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - static const u32 ar9340Modes_low_ob_db_tx_gain_table_1p0[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, @@ -1437,8 +1185,6 @@ static const u32 ar9340_1p0_mac_core[][2] = { {0x000083d0, 0x000101ff}, }; -#define ar9340Common_wo_xlna_rx_gain_table_1p0 ar9300Common_wo_xlna_rx_gain_table_2p2 - static const u32 ar9340_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x00007008, 0x00000000}, @@ -1447,4 +1193,106 @@ static const u32 ar9340_1p0_soc_preamble[][2] = { {0x00007038, 0x000004c2}, }; +static const u32 ar9340_cus227_tx_gain_table_1p0[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1c000223, 0x1c000223, 0x11000400, 0x11000400}, + {0x0000a518, 0x21002220, 0x21002220, 0x15000402, 0x15000402}, + {0x0000a51c, 0x27002223, 0x27002223, 0x19000404, 0x19000404}, + {0x0000a520, 0x2c022220, 0x2c022220, 0x1b000603, 0x1b000603}, + {0x0000a524, 0x30022222, 0x30022222, 0x1f000a02, 0x1f000a02}, + {0x0000a528, 0x35022225, 0x35022225, 0x23000a04, 0x23000a04}, + {0x0000a52c, 0x3b02222a, 0x3b02222a, 0x26000a20, 0x26000a20}, + {0x0000a530, 0x3f02222c, 0x3f02222c, 0x2a000e20, 0x2a000e20}, + {0x0000a534, 0x4202242a, 0x4202242a, 0x2e000e22, 0x2e000e22}, + {0x0000a538, 0x4702244a, 0x4702244a, 0x31000e24, 0x31000e24}, + {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x34001640, 0x34001640}, + {0x0000a540, 0x4e02246c, 0x4e02246c, 0x38001660, 0x38001660}, + {0x0000a544, 0x5302266c, 0x5302266c, 0x3b001861, 0x3b001861}, + {0x0000a548, 0x5702286c, 0x5702286c, 0x3e001a81, 0x3e001a81}, + {0x0000a54c, 0x5c02486b, 0x5c02486b, 0x42001a83, 0x42001a83}, + {0x0000a550, 0x61024a6c, 0x61024a6c, 0x44001c84, 0x44001c84}, + {0x0000a554, 0x66026a6c, 0x66026a6c, 0x48001ce3, 0x48001ce3}, + {0x0000a558, 0x6b026e6c, 0x6b026e6c, 0x4c001ce5, 0x4c001ce5}, + {0x0000a55c, 0x7002708c, 0x7002708c, 0x50001ce9, 0x50001ce9}, + {0x0000a560, 0x7302b08a, 0x7302b08a, 0x54001ceb, 0x54001ceb}, + {0x0000a564, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a568, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a56c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a570, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a574, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a578, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a57c, 0x7702b08c, 0x7702b08c, 0x56001eec, 0x56001eec}, + {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, + {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, + {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, + {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, + {0x0000a590, 0x16800220, 0x16800220, 0x0f800202, 0x0f800202}, + {0x0000a594, 0x1c800223, 0x1c800223, 0x11800400, 0x11800400}, + {0x0000a598, 0x21820220, 0x21820220, 0x15800402, 0x15800402}, + {0x0000a59c, 0x27820223, 0x27820223, 0x19800404, 0x19800404}, + {0x0000a5a0, 0x2b822220, 0x2b822220, 0x1b800603, 0x1b800603}, + {0x0000a5a4, 0x2f822222, 0x2f822222, 0x1f800a02, 0x1f800a02}, + {0x0000a5a8, 0x34822225, 0x34822225, 0x23800a04, 0x23800a04}, + {0x0000a5ac, 0x3a82222a, 0x3a82222a, 0x26800a20, 0x26800a20}, + {0x0000a5b0, 0x3e82222c, 0x3e82222c, 0x2a800e20, 0x2a800e20}, + {0x0000a5b4, 0x4282242a, 0x4282242a, 0x2e800e22, 0x2e800e22}, + {0x0000a5b8, 0x4782244a, 0x4782244a, 0x31800e24, 0x31800e24}, + {0x0000a5bc, 0x4b82244c, 0x4b82244c, 0x34801640, 0x34801640}, + {0x0000a5c0, 0x4e82246c, 0x4e82246c, 0x38801660, 0x38801660}, + {0x0000a5c4, 0x5382266c, 0x5382266c, 0x3b801861, 0x3b801861}, + {0x0000a5c8, 0x5782286c, 0x5782286c, 0x3e801a81, 0x3e801a81}, + {0x0000a5cc, 0x5c84286b, 0x5c84286b, 0x42801a83, 0x42801a83}, + {0x0000a5d0, 0x61842a6c, 0x61842a6c, 0x44801c84, 0x44801c84}, + {0x0000a5d4, 0x66862a6c, 0x66862a6c, 0x48801ce3, 0x48801ce3}, + {0x0000a5d8, 0x6b862e6c, 0x6b862e6c, 0x4c801ce5, 0x4c801ce5}, + {0x0000a5dc, 0x7086308c, 0x7086308c, 0x50801ce9, 0x50801ce9}, + {0x0000a5e0, 0x738a308a, 0x738a308a, 0x54801ceb, 0x54801ceb}, + {0x0000a5e4, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a5e8, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a5ec, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a5f0, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a5f4, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a5f8, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a5fc, 0x778a308c, 0x778a308c, 0x56801eec, 0x56801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, + {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, + {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, + {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, + {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x056db2db, 0x056db2db, 0x03b6d2e4, 0x03b6d2e4}, + {0x00016048, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, + {0x00016280, 0x01000015, 0x01000015, 0x01001015, 0x01001015}, + {0x00016288, 0x30318000, 0x30318000, 0x00318000, 0x00318000}, + {0x00016444, 0x056db2db, 0x056db2db, 0x03b6d2e4, 0x03b6d2e4}, + {0x00016448, 0x24925666, 0x24925666, 0x8e481266, 0x8e481266}, + {0x0000a3a4, 0x00000011, 0x00000011, 0x00000011, 0x00000011}, + {0x0000a3a8, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c, 0x3c3c3c3c}, + {0x0000a3ac, 0x30303030, 0x30303030, 0x30303030, 0x30303030}, +}; + #endif /* INITVALS_9340_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 092b9d412e7..1b6b4d0cfa9 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -20,7 +20,15 @@ /* AR9462 2.0 */ -static const u32 ar9462_modes_fast_clock_2p0[][3] = { +#define ar9462_2p0_mac_postamble ar9331_1p1_mac_postamble + +#define ar9462_2p0_common_wo_xlna_rx_gain ar9300Common_wo_xlna_rx_gain_table_2p2 + +#define ar9462_2p0_common_5g_xlna_only_rxgain ar9462_2p0_common_mixed_rx_gain + +#define ar9462_2p0_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 + +static const u32 ar9462_2p0_modes_fast_clock[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, {0x00001070, 0x0000018c, 0x00000318}, @@ -33,13 +41,6 @@ static const u32 ar9462_modes_fast_clock_2p0[][3] = { {0x0000a254, 0x00000898, 0x00001130}, }; -static const u32 ar9462_pciephy_clkreq_enable_L1_2p0[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18253ede}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0003780c}, -}; - static const u32 ar9462_2p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d}, @@ -56,7 +57,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, @@ -95,11 +96,11 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000ae20, 0x000001a6, 0x000001a6, 0x000001aa, 0x000001aa}, {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, }; -static const u32 ar9462_common_rx_gain_table_2p0[][2] = { +static const u32 ar9462_2p0_common_rx_gain[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -359,20 +360,13 @@ static const u32 ar9462_common_rx_gain_table_2p0[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9462_pciephy_clkreq_disable_L1_2p0[][2] = { +static const u32 ar9462_2p0_pciephy_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18213ede}, {0x00018c04, 0x000801d8}, {0x00018c08, 0x0003780c}, }; -static const u32 ar9462_pciephy_pll_on_clkreq_disable_L1_2p0[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18212ede}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0003780c}, -}; - static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, @@ -380,274 +374,7 @@ static const u32 ar9462_2p0_radio_postamble_sys2ant[][5] = { {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; -static const u32 ar9462_common_wo_xlna_rx_gain_table_2p0[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9462_2p0_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; - -static const u32 ar9462_modes_low_ob_db_tx_gain_table_2p0[][5] = { +static const u32 ar9462_2p0_modes_low_ob_db_tx_gain[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, @@ -879,7 +606,7 @@ static const u32 ar9462_2p0_radio_postamble[][5] = { {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, }; -static const u32 ar9462_modes_mix_ob_db_tx_gain_table_2p0[][5] = { +static const u32 ar9462_2p0_modes_mix_ob_db_tx_gain[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, @@ -942,7 +669,7 @@ static const u32 ar9462_modes_mix_ob_db_tx_gain_table_2p0[][5] = { {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, }; -static const u32 ar9462_modes_high_ob_db_tx_gain_table_2p0[][5] = { +static const u32 ar9462_2p0_modes_high_ob_db_tx_gain[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, @@ -1240,19 +967,7 @@ static const u32 ar9462_2p0_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9462_2p0_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; - -static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = { +static const u32 ar9462_2p0_common_mixed_rx_gain[][2] = { /* Addr allmodes */ {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, @@ -1517,266 +1232,6 @@ static const u32 ar9462_2p0_baseband_postamble_5g_xlna[][5] = { {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, }; -static const u32 ar9462_2p0_5g_xlna_only_rxgain[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x2a2d2f32}, - {0x0000b084, 0x21232328}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - static const u32 ar9462_2p0_baseband_core_mix_rxgain[][2] = { /* Addr allmodes */ {0x00009fd0, 0x0a2d6b93}, diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h index 4dbc294df7e..dc3adda46e8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p1_initvals.h @@ -20,6 +20,44 @@ /* AR9462 2.1 */ +#define ar9462_2p1_mac_postamble ar9462_2p0_mac_postamble + +#define ar9462_2p1_baseband_core ar9462_2p0_baseband_core + +#define ar9462_2p1_radio_core ar9462_2p0_radio_core + +#define ar9462_2p1_radio_postamble ar9462_2p0_radio_postamble + +#define ar9462_2p1_soc_postamble ar9462_2p0_soc_postamble + +#define ar9462_2p1_radio_postamble_sys2ant ar9462_2p0_radio_postamble_sys2ant + +#define ar9462_2p1_common_rx_gain ar9462_2p0_common_rx_gain + +#define ar9462_2p1_common_mixed_rx_gain ar9462_2p0_common_mixed_rx_gain + +#define ar9462_2p1_common_5g_xlna_only_rxgain ar9462_2p0_common_5g_xlna_only_rxgain + +#define ar9462_2p1_baseband_core_mix_rxgain ar9462_2p0_baseband_core_mix_rxgain + +#define ar9462_2p1_baseband_postamble_mix_rxgain ar9462_2p0_baseband_postamble_mix_rxgain + +#define ar9462_2p1_baseband_postamble_5g_xlna ar9462_2p0_baseband_postamble_5g_xlna + +#define ar9462_2p1_common_wo_xlna_rx_gain ar9462_2p0_common_wo_xlna_rx_gain + +#define ar9462_2p1_modes_low_ob_db_tx_gain ar9462_2p0_modes_low_ob_db_tx_gain + +#define ar9462_2p1_modes_high_ob_db_tx_gain ar9462_2p0_modes_high_ob_db_tx_gain + +#define ar9462_2p1_modes_mix_ob_db_tx_gain ar9462_2p0_modes_mix_ob_db_tx_gain + +#define ar9462_2p1_modes_fast_clock ar9462_2p0_modes_fast_clock + +#define ar9462_2p1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 + +#define ar9462_2p1_pciephy_clkreq_disable_L1 ar9462_2p0_pciephy_clkreq_disable_L1 + static const u32 ar9462_2p1_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, @@ -183,168 +221,6 @@ static const u32 ar9462_2p1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -static const u32 ar9462_2p1_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; - -static const u32 ar9462_2p1_baseband_core[][2] = { - /* Addr allmodes */ - {0x00009800, 0xafe68e30}, - {0x00009804, 0xfd14e000}, - {0x00009808, 0x9c0a9f6b}, - {0x0000980c, 0x04900000}, - {0x00009814, 0x9280c00a}, - {0x00009818, 0x00000000}, - {0x0000981c, 0x00020028}, - {0x00009834, 0x6400a290}, - {0x00009838, 0x0108ecff}, - {0x0000983c, 0x0d000600}, - {0x00009880, 0x201fff00}, - {0x00009884, 0x00001042}, - {0x000098a4, 0x00200400}, - {0x000098b0, 0x32440bbe}, - {0x000098d0, 0x004b6a8e}, - {0x000098d4, 0x00000820}, - {0x000098dc, 0x00000000}, - {0x000098e4, 0x01ffffff}, - {0x000098e8, 0x01ffffff}, - {0x000098ec, 0x01ffffff}, - {0x000098f0, 0x00000000}, - {0x000098f4, 0x00000000}, - {0x00009bf0, 0x80000000}, - {0x00009c04, 0xff55ff55}, - {0x00009c08, 0x0320ff55}, - {0x00009c0c, 0x00000000}, - {0x00009c10, 0x00000000}, - {0x00009c14, 0x00046384}, - {0x00009c18, 0x05b6b440}, - {0x00009c1c, 0x00b6b440}, - {0x00009d00, 0xc080a333}, - {0x00009d04, 0x40206c10}, - {0x00009d08, 0x009c4060}, - {0x00009d0c, 0x9883800a}, - {0x00009d10, 0x01834061}, - {0x00009d14, 0x00c0040b}, - {0x00009d18, 0x00000000}, - {0x00009e08, 0x0038230c}, - {0x00009e24, 0x990bb515}, - {0x00009e28, 0x0c6f0000}, - {0x00009e30, 0x06336f77}, - {0x00009e34, 0x6af6532f}, - {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x15262820}, - {0x00009e4c, 0x00001004}, - {0x00009e50, 0x00ff03f1}, - {0x00009e54, 0xe4c555c2}, - {0x00009e58, 0xfd857722}, - {0x00009e5c, 0xe9198724}, - {0x00009fc0, 0x803e4788}, - {0x00009fc4, 0x0001efb5}, - {0x00009fcc, 0x40000014}, - {0x00009fd0, 0x0a193b93}, - {0x0000a20c, 0x00000000}, - {0x0000a220, 0x00000000}, - {0x0000a224, 0x00000000}, - {0x0000a228, 0x10002310}, - {0x0000a23c, 0x00000000}, - {0x0000a244, 0x0c000000}, - {0x0000a2a0, 0x00000001}, - {0x0000a2c0, 0x00000001}, - {0x0000a2c8, 0x00000000}, - {0x0000a2cc, 0x18c43433}, - {0x0000a2d4, 0x00000000}, - {0x0000a2ec, 0x00000000}, - {0x0000a2f0, 0x00000000}, - {0x0000a2f4, 0x00000000}, - {0x0000a2f8, 0x00000000}, - {0x0000a344, 0x00000000}, - {0x0000a34c, 0x00000000}, - {0x0000a350, 0x0000a000}, - {0x0000a364, 0x00000000}, - {0x0000a370, 0x00000000}, - {0x0000a390, 0x00000001}, - {0x0000a394, 0x00000444}, - {0x0000a398, 0x001f0e0f}, - {0x0000a39c, 0x0075393f}, - {0x0000a3a0, 0xb79f6427}, - {0x0000a3c0, 0x20202020}, - {0x0000a3c4, 0x22222220}, - {0x0000a3c8, 0x20200020}, - {0x0000a3cc, 0x20202020}, - {0x0000a3d0, 0x20202020}, - {0x0000a3d4, 0x20202020}, - {0x0000a3d8, 0x20202020}, - {0x0000a3dc, 0x20202020}, - {0x0000a3e0, 0x20202020}, - {0x0000a3e4, 0x20202020}, - {0x0000a3e8, 0x20202020}, - {0x0000a3ec, 0x20202020}, - {0x0000a3f0, 0x00000000}, - {0x0000a3f4, 0x00000006}, - {0x0000a3f8, 0x0c9bd380}, - {0x0000a3fc, 0x000f0f01}, - {0x0000a400, 0x8fa91f01}, - {0x0000a404, 0x00000000}, - {0x0000a408, 0x0e79e5c6}, - {0x0000a40c, 0x00820820}, - {0x0000a414, 0x1ce739ce}, - {0x0000a418, 0x2d001dce}, - {0x0000a434, 0x00000000}, - {0x0000a438, 0x00001801}, - {0x0000a43c, 0x00100000}, - {0x0000a444, 0x00000000}, - {0x0000a448, 0x05000080}, - {0x0000a44c, 0x00000001}, - {0x0000a450, 0x00010000}, - {0x0000a454, 0x07000000}, - {0x0000a644, 0xbfad9d74}, - {0x0000a648, 0x0048060a}, - {0x0000a64c, 0x00002037}, - {0x0000a670, 0x03020100}, - {0x0000a674, 0x09080504}, - {0x0000a678, 0x0d0c0b0a}, - {0x0000a67c, 0x13121110}, - {0x0000a680, 0x31301514}, - {0x0000a684, 0x35343332}, - {0x0000a688, 0x00000036}, - {0x0000a690, 0x00000838}, - {0x0000a6b0, 0x0000000a}, - {0x0000a6b4, 0x00512c01}, - {0x0000a7c0, 0x00000000}, - {0x0000a7c4, 0xfffffffc}, - {0x0000a7c8, 0x00000000}, - {0x0000a7cc, 0x00000000}, - {0x0000a7d0, 0x00000000}, - {0x0000a7d4, 0x00000004}, - {0x0000a7dc, 0x00000000}, - {0x0000a7f0, 0x80000000}, - {0x0000a8d0, 0x004b6a8e}, - {0x0000a8d4, 0x00000820}, - {0x0000a8dc, 0x00000000}, - {0x0000a8f0, 0x00000000}, - {0x0000a8f4, 0x00000000}, - {0x0000abf0, 0x80000000}, - {0x0000b2d0, 0x00000080}, - {0x0000b2d4, 0x00000000}, - {0x0000b2ec, 0x00000000}, - {0x0000b2f0, 0x00000000}, - {0x0000b2f4, 0x00000000}, - {0x0000b2f8, 0x00000000}, - {0x0000b408, 0x0e79e5c0}, - {0x0000b40c, 0x00820820}, - {0x0000b420, 0x00000000}, - {0x0000b6b0, 0x0000000a}, - {0x0000b6b4, 0x00000001}, -}; - static const u32 ar9462_2p1_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a800d}, @@ -361,7 +237,7 @@ static const u32 ar9462_2p1_baseband_postamble[][5] = { {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32365a5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e20, 0x000003a5, 0x000003a5, 0x000003a5, 0x000003a5}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, {0x00009e44, 0x62321e27, 0x62321e27, 0xfe291e27, 0xfe291e27}, @@ -400,1375 +276,16 @@ static const u32 ar9462_2p1_baseband_postamble[][5] = { {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x00100000}, {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, - {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000ae20, 0x000001a6, 0x000001a6, 0x000001aa, 0x000001aa}, {0x0000b284, 0x00000000, 0x00000000, 0x00000550, 0x00000550}, }; -static const u32 ar9462_2p1_radio_core[][2] = { - /* Addr allmodes */ - {0x00016000, 0x36db6db6}, - {0x00016004, 0x6db6db40}, - {0x00016008, 0x73f00000}, - {0x0001600c, 0x00000000}, - {0x00016010, 0x6d820001}, - {0x00016040, 0x7f80fff8}, - {0x0001604c, 0x2699e04f}, - {0x00016050, 0x6db6db6c}, - {0x00016058, 0x6c200000}, - {0x00016080, 0x000c0000}, - {0x00016084, 0x9a68048c}, - {0x00016088, 0x54214514}, - {0x0001608c, 0x1203040b}, - {0x00016090, 0x24926490}, - {0x00016098, 0xd2888888}, - {0x000160a0, 0x0a108ffe}, - {0x000160a4, 0x812fc491}, - {0x000160a8, 0x423c8000}, - {0x000160b4, 0x92000000}, - {0x000160b8, 0x0285dddc}, - {0x000160bc, 0x02908888}, - {0x000160c0, 0x00adb6d0}, - {0x000160c4, 0x6db6db60}, - {0x000160c8, 0x6db6db6c}, - {0x000160cc, 0x0de6c1b0}, - {0x00016100, 0x3fffbe04}, - {0x00016104, 0xfff80000}, - {0x00016108, 0x00200400}, - {0x00016110, 0x00000000}, - {0x00016144, 0x02084080}, - {0x00016148, 0x000080c0}, - {0x00016280, 0x050a0001}, - {0x00016284, 0x3d841418}, - {0x00016288, 0x00000000}, - {0x0001628c, 0xe3000000}, - {0x00016290, 0xa1005080}, - {0x00016294, 0x00000020}, - {0x00016298, 0x54a82900}, - {0x00016340, 0x121e4276}, - {0x00016344, 0x00300000}, - {0x00016400, 0x36db6db6}, - {0x00016404, 0x6db6db40}, - {0x00016408, 0x73f00000}, - {0x0001640c, 0x00000000}, - {0x00016410, 0x6c800001}, - {0x00016440, 0x7f80fff8}, - {0x0001644c, 0x4699e04f}, - {0x00016450, 0x6db6db6c}, - {0x00016500, 0x3fffbe04}, - {0x00016504, 0xfff80000}, - {0x00016508, 0x00200400}, - {0x00016510, 0x00000000}, - {0x00016544, 0x02084080}, - {0x00016548, 0x000080c0}, -}; - -static const u32 ar9462_2p1_radio_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524}, - {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70}, - {0x0001610c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, - {0x0001650c, 0x48000000, 0x40000000, 0x40000000, 0x40000000}, -}; - static const u32 ar9462_2p1_soc_preamble[][2] = { /* Addr allmodes */ - {0x000040a4, 0x00a0c1c9}, + {0x000040a4, 0x00a0c9c9}, {0x00007020, 0x00000000}, {0x00007034, 0x00000002}, {0x00007038, 0x000004c2}, }; -static const u32 ar9462_2p1_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00000033, 0x00000033, 0x00000033, 0x00000033}, -}; - -static const u32 ar9462_2p1_radio_postamble_sys2ant[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, - {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, - {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, -}; - -static const u32 ar9462_2p1_common_rx_gain[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x01910190}, - {0x0000a030, 0x01930192}, - {0x0000a034, 0x01950194}, - {0x0000a038, 0x038a0196}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, - {0x0000a094, 0x11111717}, - {0x0000a098, 0x00030311}, - {0x0000a09c, 0x00000000}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x2a2d2f32}, - {0x0000b084, 0x21232328}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9462_2p1_common_mixed_rx_gain[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x2a2d2f32}, - {0x0000b084, 0x21232328}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9462_2p1_baseband_core_mix_rxgain[][2] = { - /* Addr allmodes */ - {0x00009fd0, 0x0a2d6b93}, -}; - -static const u32 ar9462_2p1_baseband_postamble_mix_rxgain[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae}, - {0x00009824, 0x63c640de, 0x5ac640d0, 0x63c640da, 0x63c640da}, - {0x00009828, 0x0796be89, 0x0696b081, 0x0916be81, 0x0916be81}, - {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000d8, 0x6c4000d8}, - {0x00009e10, 0x92c88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec86d2e}, - {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3236605e, 0x32395c5e}, -}; - -static const u32 ar9462_2p1_baseband_postamble_5g_xlna[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00009e3c, 0xcf946220, 0xcf946220, 0xcfd5c782, 0xcfd5c282}, -}; - -static const u32 ar9462_2p1_common_wo_xlna_rx_gain[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9462_2p1_common_5g_xlna_only_rx_gain[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x2a2d2f32}, - {0x0000b084, 0x21232328}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - -static const u32 ar9462_2p1_modes_low_ob_db_tx_gain[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, - {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, - {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, - {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, - {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, - {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x01404000, 0x01404000, 0x01404000, 0x01404000}, - {0x0000a618, 0x01404501, 0x01404501, 0x01404501, 0x01404501}, - {0x0000a61c, 0x02008802, 0x02008802, 0x02008501, 0x02008501}, - {0x0000a620, 0x0300cc03, 0x0300cc03, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x0300cc03, 0x0300cc03, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x03810c03, 0x03810c03, 0x04015005, 0x04015005}, - {0x0000a630, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000a634, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000a638, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000a63c, 0x03810e04, 0x03810e04, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0000f800, 0x0000f800, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x03ff0000, 0x03ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, - {0x00016048, 0x64992060, 0x64992060, 0x64992060, 0x64992060}, - {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, - {0x00016444, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, - {0x00016448, 0x64992000, 0x64992000, 0x64992000, 0x64992000}, - {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, -}; - -static const u32 ar9462_2p1_modes_high_ob_db_tx_gain[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, - {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x000050da, 0x000050da, 0x000050de, 0x000050de}, - {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x18022622, 0x18022622, 0x11000400, 0x11000400}, - {0x0000a518, 0x1b022822, 0x1b022822, 0x15000402, 0x15000402}, - {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, - {0x0000a520, 0x22022c41, 0x22022c41, 0x1b000603, 0x1b000603}, - {0x0000a524, 0x28023042, 0x28023042, 0x1f000a02, 0x1f000a02}, - {0x0000a528, 0x2c023044, 0x2c023044, 0x23000a04, 0x23000a04}, - {0x0000a52c, 0x2f023644, 0x2f023644, 0x26000a20, 0x26000a20}, - {0x0000a530, 0x34025643, 0x34025643, 0x2a000e20, 0x2a000e20}, - {0x0000a534, 0x38025a44, 0x38025a44, 0x2e000e22, 0x2e000e22}, - {0x0000a538, 0x3b025e45, 0x3b025e45, 0x31000e24, 0x31000e24}, - {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x34001640, 0x34001640}, - {0x0000a540, 0x48025e6c, 0x48025e6c, 0x38001660, 0x38001660}, - {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3b001861, 0x3b001861}, - {0x0000a548, 0x55025eb3, 0x55025eb3, 0x3e001a81, 0x3e001a81}, - {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x42001a83, 0x42001a83}, - {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x44001a84, 0x44001a84}, - {0x0000a554, 0x62025f56, 0x62025f56, 0x48001ce3, 0x48001ce3}, - {0x0000a558, 0x66027f56, 0x66027f56, 0x4c001ce5, 0x4c001ce5}, - {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x50001ce9, 0x50001ce9}, - {0x0000a560, 0x70049f56, 0x70049f56, 0x54001ceb, 0x54001ceb}, - {0x0000a564, 0x751ffff6, 0x751ffff6, 0x56001eec, 0x56001eec}, - {0x0000a568, 0x751ffff6, 0x751ffff6, 0x58001ef0, 0x58001ef0}, - {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x5a001ef4, 0x5a001ef4}, - {0x0000a570, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, - {0x0000a574, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, - {0x0000a578, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, - {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x5c001ff6, 0x5c001ff6}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, - {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, - {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, - {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, - {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x00016044, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, - {0x00016048, 0x8db49060, 0x8db49060, 0x8db49060, 0x8db49060}, - {0x00016054, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, - {0x00016444, 0x056d82e4, 0x056d82e4, 0x056d82e4, 0x056d82e4}, - {0x00016448, 0x8db49000, 0x8db49000, 0x8db49000, 0x8db49000}, - {0x00016454, 0x6db60000, 0x6db60000, 0x6db60000, 0x6db60000}, -}; - -static const u32 ar9462_2p1_modes_mix_ob_db_tx_gain[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, - {0x0000a2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000a2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000a2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, - {0x0000a410, 0x0000d0da, 0x0000d0da, 0x0000d0de, 0x0000d0de}, - {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a500, 0x00002220, 0x00002220, 0x00000000, 0x00000000}, - {0x0000a504, 0x06002223, 0x06002223, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a022220, 0x0a022220, 0x08000004, 0x08000004}, - {0x0000a50c, 0x0f022223, 0x0f022223, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x14022620, 0x14022620, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x18022622, 0x18022622, 0x12000400, 0x12000400}, - {0x0000a518, 0x1b022822, 0x1b022822, 0x16000402, 0x16000402}, - {0x0000a51c, 0x20022842, 0x20022842, 0x19000404, 0x19000404}, - {0x0000a520, 0x22022c41, 0x22022c41, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x28023042, 0x28023042, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x2c023044, 0x2c023044, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x2f023644, 0x2f023644, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x34025643, 0x34025643, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x38025a44, 0x38025a44, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x3b025e45, 0x3b025e45, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x41025e4a, 0x41025e4a, 0x38001640, 0x38001640}, - {0x0000a540, 0x48025e6c, 0x48025e6c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x4e025e8e, 0x4e025e8e, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x55025eb3, 0x55025eb3, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x58025ef3, 0x58025ef3, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x5d025ef6, 0x5d025ef6, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x62025f56, 0x62025f56, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x66027f56, 0x66027f56, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x6a029f56, 0x6a029f56, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x70049f56, 0x70049f56, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x751ffff6, 0x751ffff6, 0x5c001eec, 0x5c001eec}, - {0x0000a568, 0x751ffff6, 0x751ffff6, 0x5e001ef0, 0x5e001ef0}, - {0x0000a56c, 0x751ffff6, 0x751ffff6, 0x60001ef4, 0x60001ef4}, - {0x0000a570, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, - {0x0000a574, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, - {0x0000a578, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, - {0x0000a57c, 0x751ffff6, 0x751ffff6, 0x62001ff6, 0x62001ff6}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00804000, 0x00804000, 0x00000000, 0x00000000}, - {0x0000a614, 0x00804201, 0x00804201, 0x01404000, 0x01404000}, - {0x0000a618, 0x0280c802, 0x0280c802, 0x01404501, 0x01404501}, - {0x0000a61c, 0x0280ca03, 0x0280ca03, 0x02008501, 0x02008501}, - {0x0000a620, 0x04c15104, 0x04c15104, 0x0280ca03, 0x0280ca03}, - {0x0000a624, 0x04c15305, 0x04c15305, 0x03010c04, 0x03010c04}, - {0x0000a628, 0x04c15305, 0x04c15305, 0x04014c04, 0x04014c04}, - {0x0000a62c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a630, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a634, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a638, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000a63c, 0x04c15305, 0x04c15305, 0x04015005, 0x04015005}, - {0x0000b2dc, 0x01feee00, 0x01feee00, 0x03aaa352, 0x03aaa352}, - {0x0000b2e0, 0x0000f000, 0x0000f000, 0x03ccc584, 0x03ccc584}, - {0x0000b2e4, 0x01ff0000, 0x01ff0000, 0x03f0f800, 0x03f0f800}, - {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, -}; - -static const u32 ar9462_2p1_modes_fast_clock[][3] = { - /* Addr 5G_HT20 5G_HT40 */ - {0x00001030, 0x00000268, 0x000004d0}, - {0x00001070, 0x0000018c, 0x00000318}, - {0x000010b0, 0x00000fd0, 0x00001fa0}, - {0x00008014, 0x044c044c, 0x08980898}, - {0x0000801c, 0x148ec02b, 0x148ec057}, - {0x00008318, 0x000044c0, 0x00008980}, - {0x00009e00, 0x0372131c, 0x0372131c}, - {0x0000a230, 0x0000400b, 0x00004016}, - {0x0000a254, 0x00000898, 0x00001130}, -}; - -static const u32 ar9462_2p1_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; - #endif /* INITVALS_9462_2P1_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index 88ff1d7b53a..ce83ce47a1c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -20,20 +20,18 @@ /* AR9485 1.1 */ -#define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble +#define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18012e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, -}; +#define ar9485_1_1_mac_postamble ar9331_1p1_mac_postamble + +#define ar9485_1_1_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { /* Addr allmodes */ {0x00009e00, 0x037216a0}, {0x00009e04, 0x00182020}, {0x00009e18, 0x00000000}, + {0x00009e20, 0x000003a8}, {0x00009e2c, 0x00004121}, {0x00009e44, 0x02282324}, {0x0000a000, 0x00060005}, @@ -174,7 +172,7 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, @@ -200,14 +198,14 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62001eee, 0x62001eee}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001ff6, 0x66001ff6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -263,6 +261,11 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, @@ -297,6 +300,22 @@ static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = { {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, @@ -341,7 +360,7 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { {0x0000a2e0, 0x00000000, 0x00000000, 0xffc63a84, 0xffc63a84}, {0x0000a2e4, 0x00000000, 0x00000000, 0xfe0fc000, 0xfe0fc000}, {0x0000a2e8, 0x00000000, 0x00000000, 0xfff00000, 0xfff00000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, @@ -367,14 +386,14 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62001eee, 0x62001eee}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001ff6, 0x66001ff6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -521,12 +540,15 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -#define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 - static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xffad452a, 0xffad452a}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xffc98634, 0xffc98634}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfff60780, 0xfff60780}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfffff800, 0xfffff800}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201}, @@ -543,23 +565,39 @@ static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = { {0x0000a530, 0x48023ec6, 0x48023ec6, 0x310006e0, 0x310006e0}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x330006e0, 0x330006e0}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x3e0008e3, 0x3e0008e3}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x410008e5, 0x410008e5}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x430008e6, 0x430008e6}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4a0008ec, 0x4a0008ec}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4e0008f1, 0x4e0008f1}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x520008f3, 0x520008f3}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x54000eed, 0x54000eed}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x58000ef1, 0x58000ef1}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5c000ef3, 0x5c000ef3}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x60000ef5, 0x60000ef5}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x62000ef6, 0x62000ef6}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x430008e6, 0x430008e6}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4a0008ec, 0x4a0008ec}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4e0008f1, 0x4e0008f1}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x520008f3, 0x520008f3}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x54000eed, 0x54000eed}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x58000ef1, 0x58000ef1}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5c000ef3, 0x5c000ef3}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001ff0, 0x66001ff0}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x68001ff6, 0x68001ff6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x01804000, 0x01804000}, + {0x0000a590, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a594, 0x00000000, 0x00000000, 0x0340ca02, 0x0340ca02}, + {0x0000a598, 0x00000000, 0x00000000, 0x0340cd03, 0x0340cd03}, + {0x0000a59c, 0x00000000, 0x00000000, 0x0340cd03, 0x0340cd03}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x06415304, 0x06415304}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x04c11905, 0x04c11905}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, @@ -823,6 +861,7 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x00009e00, 0x03721b20}, {0x00009e04, 0x00082020}, {0x00009e18, 0x0300501e}, + {0x00009e20, 0x000003ba}, {0x00009e2c, 0x00002e21}, {0x00009e44, 0x02182324}, {0x0000a000, 0x00060005}, @@ -955,20 +994,6 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a1fc, 0x00000296}, }; -static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_enable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18052e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, -}; - -static const u32 ar9485_1_1_pcie_phy_clkreq_enable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18053e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, -}; - static const u32 ar9485_1_1_soc_preamble[][2] = { /* Addr allmodes */ {0x00004014, 0xba280400}, @@ -1001,7 +1026,6 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, {0x00009e14, 0x31395d53, 0x31396053, 0x312e6053, 0x312e5d53}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, @@ -1020,7 +1044,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0}, {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1028,13 +1052,6 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; -static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { - /* Addr allmodes */ - {0x00018c00, 0x18013e5e}, - {0x00018c04, 0x000801d8}, - {0x00018c08, 0x0000080c}, -}; - static const u32 ar9485_1_1_radio_postamble[][2] = { /* Addr allmodes */ {0x0001609c, 0x0b283f31}, @@ -1206,6 +1223,18 @@ static const u32 ar9485_1_1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -#define ar9485_1_1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 +static const u32 ar9485_1_1_pcie_phy_clkreq_disable_L1[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x18013e5e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000080c}, +}; + +static const u32 ar9485_1_1_pll_on_cdr_on_clkreq_disable_L1[][2] = { + /* Addr allmodes */ + {0x00018c00, 0x1801265e}, + {0x00018c04, 0x000801d8}, + {0x00018c08, 0x0000080c}, +}; #endif /* INITVALS_9485_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h new file mode 100644 index 00000000000..8e5c3b9786e --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INITVALS_953X_H +#define INITVALS_953X_H + +#define qca953x_1p0_mac_postamble ar9300_2p2_mac_postamble + +#define qca953x_1p0_soc_postamble ar9300_2p2_soc_postamble + +#define qca953x_1p0_common_rx_gain_table ar9300Common_rx_gain_table_2p2 + +#define qca953x_1p0_common_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 + +#define qca953x_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2 + +static const u32 qca953x_1p0_mac_core[][2] = { + /* Addr allmodes */ + {0x00000008, 0x00000000}, + {0x00000030, 0x00020085}, + {0x00000034, 0x00000005}, + {0x00000040, 0x00000000}, + {0x00000044, 0x00000000}, + {0x00000048, 0x00000008}, + {0x0000004c, 0x00000010}, + {0x00000050, 0x00000000}, + {0x00001040, 0x002ffc0f}, + {0x00001044, 0x002ffc0f}, + {0x00001048, 0x002ffc0f}, + {0x0000104c, 0x002ffc0f}, + {0x00001050, 0x002ffc0f}, + {0x00001054, 0x002ffc0f}, + {0x00001058, 0x002ffc0f}, + {0x0000105c, 0x002ffc0f}, + {0x00001060, 0x002ffc0f}, + {0x00001064, 0x002ffc0f}, + {0x000010f0, 0x00000100}, + {0x00001270, 0x00000000}, + {0x000012b0, 0x00000000}, + {0x000012f0, 0x00000000}, + {0x0000143c, 0x00000000}, + {0x0000147c, 0x00000000}, + {0x00008000, 0x00000000}, + {0x00008004, 0x00000000}, + {0x00008008, 0x00000000}, + {0x0000800c, 0x00000000}, + {0x00008018, 0x00000000}, + {0x00008020, 0x00000000}, + {0x00008038, 0x00000000}, + {0x0000803c, 0x00000000}, + {0x00008040, 0x00000000}, + {0x00008044, 0x00000000}, + {0x00008048, 0x00000000}, + {0x0000804c, 0xffffffff}, + {0x00008054, 0x00000000}, + {0x00008058, 0x00000000}, + {0x0000805c, 0x000fc78f}, + {0x00008060, 0x0000000f}, + {0x00008064, 0x00000000}, + {0x00008070, 0x00000310}, + {0x00008074, 0x00000020}, + {0x00008078, 0x00000000}, + {0x0000809c, 0x0000000f}, + {0x000080a0, 0x00000000}, + {0x000080a4, 0x02ff0000}, + {0x000080a8, 0x0e070605}, + {0x000080ac, 0x0000000d}, + {0x000080b0, 0x00000000}, + {0x000080b4, 0x00000000}, + {0x000080b8, 0x00000000}, + {0x000080bc, 0x00000000}, + {0x000080c0, 0x2a800000}, + {0x000080c4, 0x06900168}, + {0x000080c8, 0x13881c22}, + {0x000080cc, 0x01f40000}, + {0x000080d0, 0x00252500}, + {0x000080d4, 0x00a00000}, + {0x000080d8, 0x00400000}, + {0x000080dc, 0x00000000}, + {0x000080e0, 0xffffffff}, + {0x000080e4, 0x0000ffff}, + {0x000080e8, 0x3f3f3f3f}, + {0x000080ec, 0x00000000}, + {0x000080f0, 0x00000000}, + {0x000080f4, 0x00000000}, + {0x000080fc, 0x00020000}, + {0x00008100, 0x00000000}, + {0x00008108, 0x00000052}, + {0x0000810c, 0x00000000}, + {0x00008110, 0x00000000}, + {0x00008114, 0x000007ff}, + {0x00008118, 0x000000aa}, + {0x0000811c, 0x00003210}, + {0x00008124, 0x00000000}, + {0x00008128, 0x00000000}, + {0x0000812c, 0x00000000}, + {0x00008130, 0x00000000}, + {0x00008134, 0x00000000}, + {0x00008138, 0x00000000}, + {0x0000813c, 0x0000ffff}, + {0x00008140, 0x000000fe}, + {0x00008144, 0xffffffff}, + {0x00008168, 0x00000000}, + {0x0000816c, 0x00000000}, + {0x000081c0, 0x00000000}, + {0x000081c4, 0x33332210}, + {0x000081ec, 0x00000000}, + {0x000081f0, 0x00000000}, + {0x000081f4, 0x00000000}, + {0x000081f8, 0x00000000}, + {0x000081fc, 0x00000000}, + {0x00008240, 0x00100000}, + {0x00008244, 0x0010f3d7}, + {0x00008248, 0x00000852}, + {0x0000824c, 0x0001e7ae}, + {0x00008250, 0x00000000}, + {0x00008254, 0x00000000}, + {0x00008258, 0x00000000}, + {0x0000825c, 0x40000000}, + {0x00008260, 0x00080922}, + {0x00008264, 0x9d400010}, + {0x00008268, 0xffffffff}, + {0x0000826c, 0x0000ffff}, + {0x00008270, 0x00000000}, + {0x00008274, 0x40000000}, + {0x00008278, 0x003e4180}, + {0x0000827c, 0x00000004}, + {0x00008284, 0x0000002c}, + {0x00008288, 0x0000002c}, + {0x0000828c, 0x000000ff}, + {0x00008294, 0x00000000}, + {0x00008298, 0x00000000}, + {0x0000829c, 0x00000000}, + {0x00008300, 0x00001d40}, + {0x00008314, 0x00000000}, + {0x0000831c, 0x0000010d}, + {0x00008328, 0x00000000}, + {0x0000832c, 0x0000001f}, + {0x00008330, 0x00000302}, + {0x00008334, 0x00000700}, + {0x00008338, 0xffff0000}, + {0x0000833c, 0x02400000}, + {0x00008340, 0x000107ff}, + {0x00008344, 0xaa48107b}, + {0x00008348, 0x008f0000}, + {0x0000835c, 0x00000000}, + {0x00008360, 0xffffffff}, + {0x00008364, 0xffffffff}, + {0x00008368, 0x00000000}, + {0x00008370, 0x00000000}, + {0x00008374, 0x000000ff}, + {0x00008378, 0x00000000}, + {0x0000837c, 0x00000000}, + {0x00008380, 0xffffffff}, + {0x00008384, 0xffffffff}, + {0x00008390, 0xffffffff}, + {0x00008394, 0xffffffff}, + {0x00008398, 0x00000000}, + {0x0000839c, 0x00000000}, + {0x000083a0, 0x00000000}, + {0x000083a4, 0x0000fa14}, + {0x000083a8, 0x000f0c00}, + {0x000083ac, 0x33332210}, + {0x000083b0, 0x33332210}, + {0x000083b4, 0x33332210}, + {0x000083b8, 0x33332210}, + {0x000083bc, 0x00000000}, + {0x000083c0, 0x00000000}, + {0x000083c4, 0x00000000}, + {0x000083c8, 0x00000000}, + {0x000083cc, 0x00000200}, + {0x000083d0, 0x8c7901ff}, +}; + +static const u32 qca953x_1p0_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a9f6b}, + {0x0000980c, 0x04900000}, + {0x00009814, 0x0280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x6400a190}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x14000600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32840bbe}, + {0x000098bc, 0x00000002}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x9883800a}, + {0x00009d10, 0x01884061}, + {0x00009d14, 0x00c0040b}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x990bb515}, + {0x00009e28, 0x0c6f0000}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009fc0, 0x813e4788}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x00009fd0, 0x01193b91}, + {0x0000a20c, 0x00000000}, + {0x0000a220, 0x00000000}, + {0x0000a224, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a248, 0x00000140}, + {0x0000a2a0, 0x00000007}, + {0x0000a2c0, 0x00000007}, + {0x0000a2c8, 0x00000000}, + {0x0000a2d4, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a364, 0x00000000}, + {0x0000a370, 0x00000000}, + {0x0000a390, 0x00000001}, + {0x0000a394, 0x00000444}, + {0x0000a398, 0x001f0e0f}, + {0x0000a39c, 0x0075393f}, + {0x0000a3a0, 0xb79f6427}, + {0x0000a3a4, 0x000000ff}, + {0x0000a3a8, 0x6a6a6a6a}, + {0x0000a3ac, 0x6a6a6a6a}, + {0x0000a3b0, 0x00c8641a}, + {0x0000a3b4, 0x0000001a}, + {0x0000a3b8, 0x0088642a}, + {0x0000a3bc, 0x000001fa}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000000}, + {0x0000a3f8, 0x0c9bd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce42108}, + {0x0000a418, 0x2d001dce}, + {0x0000a41c, 0x1ce73908}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce738e7}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00100000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x05000080}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a458, 0x00000000}, + {0x0000a644, 0xbfad9d74}, + {0x0000a648, 0x0048060a}, + {0x0000a64c, 0x00003c37}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x09080504}, + {0x0000a678, 0x0d0c0b0a}, + {0x0000a67c, 0x13121110}, + {0x0000a680, 0x31301514}, + {0x0000a684, 0x35343332}, + {0x0000a688, 0x00000036}, + {0x0000a690, 0x08000838}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000000}, + {0x0000a8d0, 0x004b6a8e}, + {0x0000a8d4, 0x00000820}, + {0x0000a8dc, 0x00000000}, + {0x0000a8f0, 0x00000000}, + {0x0000a8f4, 0x00000000}, + {0x0000b2d0, 0x00000080}, + {0x0000b2d4, 0x00000000}, + {0x0000b2ec, 0x00000000}, + {0x0000b2f0, 0x00000000}, + {0x0000b2f4, 0x00000000}, + {0x0000b2f8, 0x00000000}, + {0x0000b408, 0x0e79e5c0}, + {0x0000b40c, 0x00820820}, + {0x0000b420, 0x00000000}, +}; + +static const u32 qca953x_1p0_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, + {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822}, + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, + {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e}, + {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, + {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, + {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, + {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce}, + {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, +}; + +static const u32 qca953x_1p0_radio_core[][2] = { + /* Addr allmodes */ + {0x00016000, 0x36db6db6}, + {0x00016004, 0x6db6db40}, + {0x00016008, 0x73f00000}, + {0x0001600c, 0x00000000}, + {0x00016040, 0x3f80fff8}, + {0x0001604c, 0x000f0278}, + {0x00016050, 0x8036db6c}, + {0x00016054, 0x6db60000}, + {0x00016080, 0x00080000}, + {0x00016084, 0x0e48048c}, + {0x00016088, 0x14214514}, + {0x0001608c, 0x119f080a}, + {0x00016090, 0x24926490}, + {0x00016094, 0x00000000}, + {0x000160a0, 0xc2108ffe}, + {0x000160a4, 0x812fc370}, + {0x000160a8, 0x423c8000}, + {0x000160b4, 0x92480080}, + {0x000160c0, 0x006db6d8}, + {0x000160c4, 0x24b6db6c}, + {0x000160c8, 0x6db6db6c}, + {0x000160cc, 0x6db6fb7c}, + {0x000160d0, 0x6db6da44}, + {0x00016100, 0x07ff8001}, + {0x00016108, 0x00080010}, + {0x00016144, 0x01884080}, + {0x00016148, 0x000080d8}, + {0x00016280, 0x01000901}, + {0x00016284, 0x15d30000}, + {0x00016288, 0x00318000}, + {0x0001628c, 0x50000000}, + {0x00016380, 0x00000000}, + {0x00016384, 0x00000000}, + {0x00016388, 0x00800700}, + {0x0001638c, 0x00800700}, + {0x00016390, 0x00800700}, + {0x00016394, 0x00000000}, + {0x00016398, 0x00000000}, + {0x0001639c, 0x00000000}, + {0x000163a0, 0x00000001}, + {0x000163a4, 0x00000001}, + {0x000163a8, 0x00000000}, + {0x000163ac, 0x00000000}, + {0x000163b0, 0x00000000}, + {0x000163b4, 0x00000000}, + {0x000163b8, 0x00000000}, + {0x000163bc, 0x00000000}, + {0x000163c0, 0x000000a0}, + {0x000163c4, 0x000c0000}, + {0x000163c8, 0x14021402}, + {0x000163cc, 0x00001402}, + {0x000163d0, 0x00000000}, + {0x000163d4, 0x00000000}, + {0x00016400, 0x36db6db6}, + {0x00016404, 0x6db6db40}, + {0x00016408, 0x73f00000}, + {0x0001640c, 0x00000000}, + {0x00016440, 0x3f80fff8}, + {0x0001644c, 0x000f0278}, + {0x00016450, 0x8036db6c}, + {0x00016454, 0x6db60000}, + {0x00016500, 0x07ff8001}, + {0x00016508, 0x00080010}, + {0x00016544, 0x01884080}, + {0x00016548, 0x000080d8}, + {0x00016780, 0x00000000}, + {0x00016784, 0x00000000}, + {0x00016788, 0x00800700}, + {0x0001678c, 0x00800700}, + {0x00016790, 0x00800700}, + {0x00016794, 0x00000000}, + {0x00016798, 0x00000000}, + {0x0001679c, 0x00000000}, + {0x000167a0, 0x00000001}, + {0x000167a4, 0x00000001}, + {0x000167a8, 0x00000000}, + {0x000167ac, 0x00000000}, + {0x000167b0, 0x00000000}, + {0x000167b4, 0x00000000}, + {0x000167b8, 0x00000000}, + {0x000167bc, 0x00000000}, + {0x000167c0, 0x000000a0}, + {0x000167c4, 0x000c0000}, + {0x000167c8, 0x14021402}, + {0x000167cc, 0x00001402}, + {0x000167d0, 0x00000000}, + {0x000167d4, 0x00000000}, +}; + +static const u32 qca953x_1p0_radio_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xc4128f5c, 0xc4128f5c}, + {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x0fd08f25, 0x0fd08f25}, + {0x000160ac, 0xa4647c00, 0xa4647c00, 0x24646800, 0x24646800}, + {0x000160b0, 0x01885f52, 0x01885f52, 0x00fe7f46, 0x00fe7f46}, + {0x00016104, 0xb7a00001, 0xb7a00001, 0xfff80005, 0xfff80005}, + {0x0001610c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000}, + {0x00016140, 0x10804008, 0x10804008, 0x50804000, 0x50804000}, + {0x00016504, 0xb7a00001, 0xb7a00001, 0xfff80001, 0xfff80001}, + {0x0001650c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000}, + {0x00016540, 0x10804008, 0x10804008, 0x50804000, 0x50804000}, +}; + +static const u32 qca953x_1p0_soc_preamble[][2] = { + /* Addr allmodes */ + {0x00007000, 0x00000000}, + {0x00007004, 0x00000000}, + {0x00007008, 0x00000000}, + {0x0000700c, 0x00000000}, + {0x0000701c, 0x00000000}, + {0x00007020, 0x00000000}, + {0x00007024, 0x00000000}, + {0x00007028, 0x00000000}, + {0x0000702c, 0x00000000}, + {0x00007030, 0x00000000}, + {0x00007034, 0x00000002}, + {0x00007038, 0x000004c2}, + {0x00007048, 0x00000000}, +}; + +static const u32 qca953x_1p0_common_rx_gain_bounds[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302018, 0x50302018}, +}; + +static const u32 qca953x_1p0_common_wo_xlna_rx_gain_bounds[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, +}; + +static const u32 qca953x_1p0_modes_xpa_tx_gain_table[][2] = { + /* Addr allmodes */ + {0x0000a2dc, 0xfffd5aaa}, + {0x0000a2e0, 0xfffe9ccc}, + {0x0000a2e4, 0xffffe0f0}, + {0x0000a2e8, 0xfffcff00}, + {0x0000a410, 0x000050da}, + {0x0000a500, 0x00000000}, + {0x0000a504, 0x04000002}, + {0x0000a508, 0x08000004}, + {0x0000a50c, 0x0c000006}, + {0x0000a510, 0x0f00000a}, + {0x0000a514, 0x1300000c}, + {0x0000a518, 0x1700000e}, + {0x0000a51c, 0x1b000064}, + {0x0000a520, 0x1f000242}, + {0x0000a524, 0x23000229}, + {0x0000a528, 0x270002a2}, + {0x0000a52c, 0x2c001203}, + {0x0000a530, 0x30001803}, + {0x0000a534, 0x33000881}, + {0x0000a538, 0x38001809}, + {0x0000a53c, 0x3a000814}, + {0x0000a540, 0x3f001a0c}, + {0x0000a544, 0x43001a0e}, + {0x0000a548, 0x46001812}, + {0x0000a54c, 0x49001884}, + {0x0000a550, 0x4d001e84}, + {0x0000a554, 0x50001e69}, + {0x0000a558, 0x550006f4}, + {0x0000a55c, 0x59000ad3}, + {0x0000a560, 0x5e000ad5}, + {0x0000a564, 0x61001ced}, + {0x0000a568, 0x660018d4}, + {0x0000a56c, 0x660018d4}, + {0x0000a570, 0x660018d4}, + {0x0000a574, 0x660018d4}, + {0x0000a578, 0x660018d4}, + {0x0000a57c, 0x660018d4}, + {0x0000a600, 0x00000000}, + {0x0000a604, 0x00000000}, + {0x0000a608, 0x00000000}, + {0x0000a60c, 0x03804000}, + {0x0000a610, 0x0300ca02}, + {0x0000a614, 0x00000e04}, + {0x0000a618, 0x03014000}, + {0x0000a61c, 0x00000000}, + {0x0000a620, 0x00000000}, + {0x0000a624, 0x03014000}, + {0x0000a628, 0x03804c05}, + {0x0000a62c, 0x0701de06}, + {0x0000a630, 0x07819c07}, + {0x0000a634, 0x0701dc07}, + {0x0000a638, 0x0701dc07}, + {0x0000a63c, 0x0701dc07}, + {0x0000b2dc, 0xfffd5aaa}, + {0x0000b2e0, 0xfffe9ccc}, + {0x0000b2e4, 0xffffe0f0}, + {0x0000b2e8, 0xfffcff00}, + {0x00016044, 0x010002d4}, + {0x00016048, 0x66482400}, + {0x00016280, 0x01000015}, + {0x00016444, 0x010002d4}, + {0x00016448, 0x66482400}, +}; + +static const u32 qca953x_1p0_modes_no_xpa_tx_gain_table[][2] = { + /* Addr allmodes */ + {0x0000a2dc, 0xffd5f552}, + {0x0000a2e0, 0xffe60664}, + {0x0000a2e4, 0xfff80780}, + {0x0000a2e8, 0xfffff800}, + {0x0000a410, 0x000050d6}, + {0x0000a500, 0x00060020}, + {0x0000a504, 0x04060060}, + {0x0000a508, 0x080600a0}, + {0x0000a50c, 0x0c068020}, + {0x0000a510, 0x10068060}, + {0x0000a514, 0x140680a0}, + {0x0000a518, 0x18090040}, + {0x0000a51c, 0x1b090080}, + {0x0000a520, 0x1f0900c0}, + {0x0000a524, 0x240c0041}, + {0x0000a528, 0x280d0021}, + {0x0000a52c, 0x2d0f0061}, + {0x0000a530, 0x310f00a1}, + {0x0000a534, 0x350e00a2}, + {0x0000a538, 0x360e80a2}, + {0x0000a53c, 0x380f00a2}, + {0x0000a540, 0x3b0e00a3}, + {0x0000a544, 0x3d110083}, + {0x0000a548, 0x3e1100a3}, + {0x0000a54c, 0x401100e3}, + {0x0000a550, 0x421380e3}, + {0x0000a554, 0x431780e3}, + {0x0000a558, 0x461f80e3}, + {0x0000a55c, 0x461f80e3}, + {0x0000a560, 0x461f80e3}, + {0x0000a564, 0x461f80e3}, + {0x0000a568, 0x461f80e3}, + {0x0000a56c, 0x461f80e3}, + {0x0000a570, 0x461f80e3}, + {0x0000a574, 0x461f80e3}, + {0x0000a578, 0x461f80e3}, + {0x0000a57c, 0x461f80e3}, + {0x0000a600, 0x00000000}, + {0x0000a604, 0x00000000}, + {0x0000a608, 0x00000000}, + {0x0000a60c, 0x00804201}, + {0x0000a610, 0x01008201}, + {0x0000a614, 0x0180c402}, + {0x0000a618, 0x0180c603}, + {0x0000a61c, 0x0180c603}, + {0x0000a620, 0x01c10603}, + {0x0000a624, 0x01c10704}, + {0x0000a628, 0x02c18b05}, + {0x0000a62c, 0x0301cc07}, + {0x0000a630, 0x0301cc07}, + {0x0000a634, 0x0301cc07}, + {0x0000a638, 0x0301cc07}, + {0x0000a63c, 0x0301cc07}, + {0x0000b2dc, 0xffd5f552}, + {0x0000b2e0, 0xffe60664}, + {0x0000b2e4, 0xfff80780}, + {0x0000b2e8, 0xfffff800}, + {0x00016044, 0x049242db}, + {0x00016048, 0x6c927a70}, + {0x00016444, 0x049242db}, + {0x00016448, 0x6c927a70}, +}; + +static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = { + /* Addr allmodes */ + {0x0000a2dc, 0xffd5f552}, + {0x0000a2e0, 0xffe60664}, + {0x0000a2e4, 0xfff80780}, + {0x0000a2e8, 0xfffff800}, + {0x0000a410, 0x000050de}, + {0x0000a500, 0x00000061}, + {0x0000a504, 0x04000063}, + {0x0000a508, 0x08000065}, + {0x0000a50c, 0x0c000261}, + {0x0000a510, 0x10000263}, + {0x0000a514, 0x14000265}, + {0x0000a518, 0x18000482}, + {0x0000a51c, 0x1b000484}, + {0x0000a520, 0x1f000486}, + {0x0000a524, 0x240008c2}, + {0x0000a528, 0x28000cc1}, + {0x0000a52c, 0x2d000ce3}, + {0x0000a530, 0x31000ce5}, + {0x0000a534, 0x350010e5}, + {0x0000a538, 0x360012e5}, + {0x0000a53c, 0x380014e5}, + {0x0000a540, 0x3b0018e5}, + {0x0000a544, 0x3d001d04}, + {0x0000a548, 0x3e001d05}, + {0x0000a54c, 0x40001d07}, + {0x0000a550, 0x42001f27}, + {0x0000a554, 0x43001f67}, + {0x0000a558, 0x46001fe7}, + {0x0000a55c, 0x47001f2b}, + {0x0000a560, 0x49001f0d}, + {0x0000a564, 0x4b001ed2}, + {0x0000a568, 0x4c001ed4}, + {0x0000a56c, 0x4e001f15}, + {0x0000a570, 0x4f001ff6}, + {0x0000a574, 0x4f001ff6}, + {0x0000a578, 0x4f001ff6}, + {0x0000a57c, 0x4f001ff6}, + {0x0000a600, 0x00000000}, + {0x0000a604, 0x00000000}, + {0x0000a608, 0x00000000}, + {0x0000a60c, 0x00804201}, + {0x0000a610, 0x01008201}, + {0x0000a614, 0x0180c402}, + {0x0000a618, 0x0180c603}, + {0x0000a61c, 0x0180c603}, + {0x0000a620, 0x01c10603}, + {0x0000a624, 0x01c10704}, + {0x0000a628, 0x02c18b05}, + {0x0000a62c, 0x02c14c07}, + {0x0000a630, 0x01008704}, + {0x0000a634, 0x01c10402}, + {0x0000a638, 0x0301cc07}, + {0x0000a63c, 0x0301cc07}, + {0x0000b2dc, 0xffd5f552}, + {0x0000b2e0, 0xffe60664}, + {0x0000b2e4, 0xfff80780}, + {0x0000b2e8, 0xfffff800}, + {0x00016044, 0x049242db}, + {0x00016048, 0x6c927a70}, + {0x00016444, 0x049242db}, + {0x00016448, 0x6c927a70}, +}; + +#endif /* INITVALS_953X_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h index ccc5b6c99ad..74d8bc05b31 100644 --- a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h @@ -20,6 +20,14 @@ /* AR955X 1.0 */ +#define ar955x_1p0_soc_postamble ar9300_2p2_soc_postamble + +#define ar955x_1p0_common_rx_gain_table ar9300Common_rx_gain_table_2p2 + +#define ar955x_1p0_common_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 + +#define ar955x_1p0_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 + static const u32 ar955x_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xd28b3330, 0xd28b3330}, @@ -37,13 +45,6 @@ static const u32 ar955x_1p0_radio_postamble[][5] = { {0x00016940, 0x10804008, 0x10804008, 0x10804008, 0x10804008}, }; -static const u32 ar955x_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { - /* Addr allmodes */ - {0x0000a398, 0x00000000}, - {0x0000a39c, 0x6f7f0301}, - {0x0000a3a0, 0xca9228ee}, -}; - static const u32 ar955x_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, @@ -473,266 +474,6 @@ static const u32 ar955x_1p0_mac_core[][2] = { {0x000083d0, 0x8c7901ff}, }; -static const u32 ar955x_1p0_common_rx_gain_table[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x01910190}, - {0x0000a030, 0x01930192}, - {0x0000a034, 0x01950194}, - {0x0000a038, 0x038a0196}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, - {0x0000a094, 0x11111717}, - {0x0000a098, 0x00030311}, - {0x0000a09c, 0x00000000}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x23232323}, - {0x0000b084, 0x21232323}, - {0x0000b088, 0x19191c1e}, - {0x0000b08c, 0x12141417}, - {0x0000b090, 0x07070e0e}, - {0x0000b094, 0x03030305}, - {0x0000b098, 0x00000003}, - {0x0000b09c, 0x00000000}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - static const u32 ar955x_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, @@ -891,266 +632,6 @@ static const u32 ar955x_1p0_baseband_core[][2] = { {0x0000c420, 0x00000000}, }; -static const u32 ar955x_1p0_common_wo_xlna_rx_gain_table[][2] = { - /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x03820190}, - {0x0000a030, 0x03840383}, - {0x0000a034, 0x03880385}, - {0x0000a038, 0x038a0389}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, - {0x0000a050, 0x00000000}, - {0x0000a054, 0x00000000}, - {0x0000a058, 0x00000000}, - {0x0000a05c, 0x00000000}, - {0x0000a060, 0x00000000}, - {0x0000a064, 0x00000000}, - {0x0000a068, 0x00000000}, - {0x0000a06c, 0x00000000}, - {0x0000a070, 0x00000000}, - {0x0000a074, 0x00000000}, - {0x0000a078, 0x00000000}, - {0x0000a07c, 0x00000000}, - {0x0000a080, 0x29292929}, - {0x0000a084, 0x29292929}, - {0x0000a088, 0x29292929}, - {0x0000a08c, 0x29292929}, - {0x0000a090, 0x22292929}, - {0x0000a094, 0x1d1d2222}, - {0x0000a098, 0x0c111117}, - {0x0000a09c, 0x00030303}, - {0x0000a0a0, 0x00000000}, - {0x0000a0a4, 0x00000000}, - {0x0000a0a8, 0x00000000}, - {0x0000a0ac, 0x00000000}, - {0x0000a0b0, 0x00000000}, - {0x0000a0b4, 0x00000000}, - {0x0000a0b8, 0x00000000}, - {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, - {0x0000a114, 0x00000000}, - {0x0000a118, 0x00000000}, - {0x0000a11c, 0x00000000}, - {0x0000a120, 0x00000000}, - {0x0000a124, 0x00000000}, - {0x0000a128, 0x00000000}, - {0x0000a12c, 0x00000000}, - {0x0000a130, 0x00000000}, - {0x0000a134, 0x00000000}, - {0x0000a138, 0x00000000}, - {0x0000a13c, 0x00000000}, - {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, - {0x0000a194, 0x00000000}, - {0x0000a198, 0x00000000}, - {0x0000a19c, 0x00000000}, - {0x0000a1a0, 0x00000000}, - {0x0000a1a4, 0x00000000}, - {0x0000a1a8, 0x00000000}, - {0x0000a1ac, 0x00000000}, - {0x0000a1b0, 0x00000000}, - {0x0000a1b4, 0x00000000}, - {0x0000a1b8, 0x00000000}, - {0x0000a1bc, 0x00000000}, - {0x0000a1c0, 0x00000000}, - {0x0000a1c4, 0x00000000}, - {0x0000a1c8, 0x00000000}, - {0x0000a1cc, 0x00000000}, - {0x0000a1d0, 0x00000000}, - {0x0000a1d4, 0x00000000}, - {0x0000a1d8, 0x00000000}, - {0x0000a1dc, 0x00000000}, - {0x0000a1e0, 0x00000000}, - {0x0000a1e4, 0x00000000}, - {0x0000a1e8, 0x00000000}, - {0x0000a1ec, 0x00000000}, - {0x0000a1f0, 0x00000396}, - {0x0000a1f4, 0x00000396}, - {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, - {0x0000b000, 0x00010000}, - {0x0000b004, 0x00030002}, - {0x0000b008, 0x00050004}, - {0x0000b00c, 0x00810080}, - {0x0000b010, 0x00830082}, - {0x0000b014, 0x01810180}, - {0x0000b018, 0x01830182}, - {0x0000b01c, 0x01850184}, - {0x0000b020, 0x02810280}, - {0x0000b024, 0x02830282}, - {0x0000b028, 0x02850284}, - {0x0000b02c, 0x02890288}, - {0x0000b030, 0x028b028a}, - {0x0000b034, 0x0388028c}, - {0x0000b038, 0x038a0389}, - {0x0000b03c, 0x038c038b}, - {0x0000b040, 0x0390038d}, - {0x0000b044, 0x03920391}, - {0x0000b048, 0x03940393}, - {0x0000b04c, 0x03960395}, - {0x0000b050, 0x00000000}, - {0x0000b054, 0x00000000}, - {0x0000b058, 0x00000000}, - {0x0000b05c, 0x00000000}, - {0x0000b060, 0x00000000}, - {0x0000b064, 0x00000000}, - {0x0000b068, 0x00000000}, - {0x0000b06c, 0x00000000}, - {0x0000b070, 0x00000000}, - {0x0000b074, 0x00000000}, - {0x0000b078, 0x00000000}, - {0x0000b07c, 0x00000000}, - {0x0000b080, 0x32323232}, - {0x0000b084, 0x2f2f3232}, - {0x0000b088, 0x23282a2d}, - {0x0000b08c, 0x1c1e2123}, - {0x0000b090, 0x14171919}, - {0x0000b094, 0x0e0e1214}, - {0x0000b098, 0x03050707}, - {0x0000b09c, 0x00030303}, - {0x0000b0a0, 0x00000000}, - {0x0000b0a4, 0x00000000}, - {0x0000b0a8, 0x00000000}, - {0x0000b0ac, 0x00000000}, - {0x0000b0b0, 0x00000000}, - {0x0000b0b4, 0x00000000}, - {0x0000b0b8, 0x00000000}, - {0x0000b0bc, 0x00000000}, - {0x0000b0c0, 0x003f0020}, - {0x0000b0c4, 0x00400041}, - {0x0000b0c8, 0x0140005f}, - {0x0000b0cc, 0x0160015f}, - {0x0000b0d0, 0x017e017f}, - {0x0000b0d4, 0x02410242}, - {0x0000b0d8, 0x025f0240}, - {0x0000b0dc, 0x027f0260}, - {0x0000b0e0, 0x0341027e}, - {0x0000b0e4, 0x035f0340}, - {0x0000b0e8, 0x037f0360}, - {0x0000b0ec, 0x04400441}, - {0x0000b0f0, 0x0460045f}, - {0x0000b0f4, 0x0541047f}, - {0x0000b0f8, 0x055f0540}, - {0x0000b0fc, 0x057f0560}, - {0x0000b100, 0x06400641}, - {0x0000b104, 0x0660065f}, - {0x0000b108, 0x067e067f}, - {0x0000b10c, 0x07410742}, - {0x0000b110, 0x075f0740}, - {0x0000b114, 0x077f0760}, - {0x0000b118, 0x07800781}, - {0x0000b11c, 0x07a0079f}, - {0x0000b120, 0x07c107bf}, - {0x0000b124, 0x000007c0}, - {0x0000b128, 0x00000000}, - {0x0000b12c, 0x00000000}, - {0x0000b130, 0x00000000}, - {0x0000b134, 0x00000000}, - {0x0000b138, 0x00000000}, - {0x0000b13c, 0x00000000}, - {0x0000b140, 0x003f0020}, - {0x0000b144, 0x00400041}, - {0x0000b148, 0x0140005f}, - {0x0000b14c, 0x0160015f}, - {0x0000b150, 0x017e017f}, - {0x0000b154, 0x02410242}, - {0x0000b158, 0x025f0240}, - {0x0000b15c, 0x027f0260}, - {0x0000b160, 0x0341027e}, - {0x0000b164, 0x035f0340}, - {0x0000b168, 0x037f0360}, - {0x0000b16c, 0x04400441}, - {0x0000b170, 0x0460045f}, - {0x0000b174, 0x0541047f}, - {0x0000b178, 0x055f0540}, - {0x0000b17c, 0x057f0560}, - {0x0000b180, 0x06400641}, - {0x0000b184, 0x0660065f}, - {0x0000b188, 0x067e067f}, - {0x0000b18c, 0x07410742}, - {0x0000b190, 0x075f0740}, - {0x0000b194, 0x077f0760}, - {0x0000b198, 0x07800781}, - {0x0000b19c, 0x07a0079f}, - {0x0000b1a0, 0x07c107bf}, - {0x0000b1a4, 0x000007c0}, - {0x0000b1a8, 0x00000000}, - {0x0000b1ac, 0x00000000}, - {0x0000b1b0, 0x00000000}, - {0x0000b1b4, 0x00000000}, - {0x0000b1b8, 0x00000000}, - {0x0000b1bc, 0x00000000}, - {0x0000b1c0, 0x00000000}, - {0x0000b1c4, 0x00000000}, - {0x0000b1c8, 0x00000000}, - {0x0000b1cc, 0x00000000}, - {0x0000b1d0, 0x00000000}, - {0x0000b1d4, 0x00000000}, - {0x0000b1d8, 0x00000000}, - {0x0000b1dc, 0x00000000}, - {0x0000b1e0, 0x00000000}, - {0x0000b1e4, 0x00000000}, - {0x0000b1e8, 0x00000000}, - {0x0000b1ec, 0x00000000}, - {0x0000b1f0, 0x00000396}, - {0x0000b1f4, 0x00000396}, - {0x0000b1f8, 0x00000396}, - {0x0000b1fc, 0x00000196}, -}; - static const u32 ar955x_1p0_soc_preamble[][2] = { /* Addr allmodes */ {0x00007000, 0x00000000}, @@ -1263,11 +744,6 @@ static const u32 ar955x_1p0_modes_no_xpa_tx_gain_table[][9] = { {0x00016848, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401, 0x66482401}, }; -static const u32 ar955x_1p0_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00007010, 0x00000023, 0x00000023, 0x00000023, 0x00000023}, -}; - static const u32 ar955x_1p0_modes_fast_clock[][3] = { /* Addr 5G_HT20 5G_HT40 */ {0x00001030, 0x00000268, 0x000004d0}, diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h index e85a8b076c2..10d4a6cb1c3 100644 --- a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h @@ -20,6 +20,12 @@ /* AR9565 1.0 */ +#define ar9565_1p0_mac_postamble ar9331_1p1_mac_postamble + +#define ar9565_1p0_Modes_lowest_ob_db_tx_gain_table ar9565_1p0_modes_low_ob_db_tx_gain_table + +#define ar9565_1p0_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 + static const u32 ar9565_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, @@ -182,18 +188,6 @@ static const u32 ar9565_1p0_mac_core[][2] = { {0x000083d0, 0x800301ff}, }; -static const u32 ar9565_1p0_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, - {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, - {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, - {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, - {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, - {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, - {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, - {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, -}; - static const u32 ar9565_1p0_baseband_core[][2] = { /* Addr allmodes */ {0x00009800, 0xafe68e30}, @@ -272,9 +266,9 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, - {0x0000a3a4, 0x00000000}, - {0x0000a3a8, 0xaaaaaaaa}, - {0x0000a3ac, 0x3c466478}, + {0x0000a3a4, 0x00000011}, + {0x0000a3a8, 0xaaaaaa6e}, + {0x0000a3ac, 0x3c466455}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, @@ -295,11 +289,11 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, - {0x0000a414, 0x1ce739ce}, + {0x0000a414, 0x1ce739c5}, {0x0000a418, 0x2d001dce}, - {0x0000a41c, 0x1ce739ce}, + {0x0000a41c, 0x1ce739c5}, {0x0000a420, 0x000001ce}, - {0x0000a424, 0x1ce739ce}, + {0x0000a424, 0x1ce739c5}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, @@ -351,9 +345,9 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = { {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003a4, 0x000003a4}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, + {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946220, 0xcf946220}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, @@ -452,6 +446,7 @@ static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { /* Addr allmodes */ {0x00004050, 0x00300300}, {0x0000406c, 0x00100000}, + {0x00009e20, 0x000003b6}, {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, @@ -710,66 +705,6 @@ static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9565_1p0_Modes_lowest_ob_db_tx_gain_table[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ - {0x0000a2dc, 0xfc0a9380, 0xfc0a9380, 0xfdab5b52, 0xfdab5b52}, - {0x0000a2e0, 0xffecec00, 0xffecec00, 0xfd339c84, 0xfd339c84}, - {0x0000a2e4, 0xfc0f0000, 0xfc0f0000, 0xfec3e000, 0xfec3e000}, - {0x0000a2e8, 0xfc100000, 0xfc100000, 0xfffc0000, 0xfffc0000}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, - {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, - {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, - {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, - {0x0000a510, 0x16000220, 0x16000220, 0x0f000202, 0x0f000202}, - {0x0000a514, 0x1c000223, 0x1c000223, 0x12000400, 0x12000400}, - {0x0000a518, 0x21020220, 0x21020220, 0x16000402, 0x16000402}, - {0x0000a51c, 0x27020223, 0x27020223, 0x19000404, 0x19000404}, - {0x0000a520, 0x2b022220, 0x2b022220, 0x1c000603, 0x1c000603}, - {0x0000a524, 0x2f022222, 0x2f022222, 0x21000a02, 0x21000a02}, - {0x0000a528, 0x34022225, 0x34022225, 0x25000a04, 0x25000a04}, - {0x0000a52c, 0x3a02222a, 0x3a02222a, 0x28000a20, 0x28000a20}, - {0x0000a530, 0x3e02222c, 0x3e02222c, 0x2c000e20, 0x2c000e20}, - {0x0000a534, 0x4202242a, 0x4202242a, 0x30000e22, 0x30000e22}, - {0x0000a538, 0x4702244a, 0x4702244a, 0x34000e24, 0x34000e24}, - {0x0000a53c, 0x4b02244c, 0x4b02244c, 0x38001640, 0x38001640}, - {0x0000a540, 0x4e02246c, 0x4e02246c, 0x3c001660, 0x3c001660}, - {0x0000a544, 0x5302266c, 0x5302266c, 0x3f001861, 0x3f001861}, - {0x0000a548, 0x5702286c, 0x5702286c, 0x43001a81, 0x43001a81}, - {0x0000a54c, 0x5c04286b, 0x5c04286b, 0x47001a83, 0x47001a83}, - {0x0000a550, 0x61042a6c, 0x61042a6c, 0x4a001c84, 0x4a001c84}, - {0x0000a554, 0x66062a6c, 0x66062a6c, 0x4e001ce3, 0x4e001ce3}, - {0x0000a558, 0x6b062e6c, 0x6b062e6c, 0x52001ce5, 0x52001ce5}, - {0x0000a55c, 0x7006308c, 0x7006308c, 0x56001ce9, 0x56001ce9}, - {0x0000a560, 0x730a308a, 0x730a308a, 0x5a001ceb, 0x5a001ceb}, - {0x0000a564, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a568, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a56c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a570, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a574, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a578, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a57c, 0x770a308c, 0x770a308c, 0x5d001eec, 0x5d001eec}, - {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a608, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a60c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a610, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a614, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a618, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a61c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a620, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a624, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a628, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a62c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a630, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a634, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a638, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a63c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016044, 0x012482d4, 0x012482d4, 0x012482d4, 0x012482d4}, - {0x00016048, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, -}; - static const u32 ar9565_1p0_pciephy_clkreq_disable_L1[][2] = { /* Addr allmodes */ {0x00018c00, 0x18212ede}, diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p1_initvals.h new file mode 100644 index 00000000000..56810539971 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar9565_1p1_initvals.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INITVALS_9565_1P1_H +#define INITVALS_9565_1P1_H + +/* AR9565 1.1 */ + +#define ar9565_1p1_mac_core ar9565_1p0_mac_core + +#define ar9565_1p1_mac_postamble ar9565_1p0_mac_postamble + +#define ar9565_1p1_baseband_core ar9565_1p0_baseband_core + +#define ar9565_1p1_baseband_postamble ar9565_1p0_baseband_postamble + +#define ar9565_1p1_radio_core ar9565_1p0_radio_core + +#define ar9565_1p1_soc_preamble ar9565_1p0_soc_preamble + +#define ar9565_1p1_soc_postamble ar9565_1p0_soc_postamble + +#define ar9565_1p1_Common_rx_gain_table ar9565_1p0_Common_rx_gain_table + +#define ar9565_1p1_Modes_lowest_ob_db_tx_gain_table ar9565_1p0_Modes_lowest_ob_db_tx_gain_table + +#define ar9565_1p1_pciephy_clkreq_disable_L1 ar9565_1p0_pciephy_clkreq_disable_L1 + +#define ar9565_1p1_modes_fast_clock ar9565_1p0_modes_fast_clock + +#define ar9565_1p1_common_wo_xlna_rx_gain_table ar9565_1p0_common_wo_xlna_rx_gain_table + +#define ar9565_1p1_modes_low_ob_db_tx_gain_table ar9565_1p0_modes_low_ob_db_tx_gain_table + +#define ar9565_1p1_modes_high_ob_db_tx_gain_table ar9565_1p0_modes_high_ob_db_tx_gain_table + +#define ar9565_1p1_modes_high_power_tx_gain_table ar9565_1p0_modes_high_power_tx_gain_table + +#define ar9565_1p1_baseband_core_txfir_coeff_japan_2484 ar9565_1p0_baseband_core_txfir_coeff_japan_2484 + +static const u32 ar9565_1p1_radio_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0001609c, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524, 0x0b8ee524}, + {0x000160ac, 0xa4646c08, 0xa4646c08, 0x24645808, 0x24645808}, + {0x000160b0, 0x01d67f70, 0x01d67f70, 0x01d67f70, 0x01d67f70}, + {0x0001610c, 0x40000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, +}; + +#endif /* INITVALS_9565_1P1_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h index bdee2ed6721..a5ca65240af 100644 --- a/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9580_1p0_initvals.h @@ -20,18 +20,34 @@ /* AR9580 1.0 */ +#define ar9580_1p0_soc_preamble ar9300_2p2_soc_preamble + +#define ar9580_1p0_soc_postamble ar9300_2p2_soc_postamble + +#define ar9580_1p0_radio_core ar9300_2p2_radio_core + +#define ar9580_1p0_mac_postamble ar9300_2p2_mac_postamble + +#define ar9580_1p0_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 + +#define ar9580_1p0_type5_tx_gain_table ar9300Modes_type5_tx_gain_table_2p2 + +#define ar9580_1p0_high_ob_db_tx_gain_table ar9300Modes_high_ob_db_tx_gain_table_2p2 + #define ar9580_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2 +#define ar9580_1p0_baseband_core_txfir_coeff_japan_2484 ar9300_2p2_baseband_core_txfir_coeff_japan_2484 + static const u32 ar9580_1p0_radio_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0001609c, 0x0dd08f29, 0x0dd08f29, 0x0b283f31, 0x0b283f31}, {0x000160ac, 0xa4653c00, 0xa4653c00, 0x24652800, 0x24652800}, {0x000160b0, 0x03284f3e, 0x03284f3e, 0x05d08f20, 0x05d08f20}, - {0x0001610c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0001610c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016140, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, - {0x0001650c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0001650c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016540, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, - {0x0001690c, 0x08000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0001690c, 0xc8000000, 0xc0000000, 0xc0000000, 0xc0000000}, {0x00016940, 0x10804008, 0x10804008, 0x50804008, 0x50804008}, }; @@ -41,12 +57,10 @@ static const u32 ar9580_1p0_baseband_core[][2] = { {0x00009804, 0xfd14e000}, {0x00009808, 0x9c0a9f6b}, {0x0000980c, 0x04900000}, - {0x00009814, 0x3280c00a}, - {0x00009818, 0x00000000}, {0x0000981c, 0x00020028}, - {0x00009834, 0x6400a290}, + {0x00009834, 0x6400a190}, {0x00009838, 0x0108ecff}, - {0x0000983c, 0x0d000600}, + {0x0000983c, 0x14000600}, {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, @@ -67,7 +81,7 @@ static const u32 ar9580_1p0_baseband_core[][2] = { {0x00009d04, 0x40206c10}, {0x00009d08, 0x009c4060}, {0x00009d0c, 0x9883800a}, - {0x00009d10, 0x01834061}, + {0x00009d10, 0x01884061}, {0x00009d14, 0x00c0040b}, {0x00009d18, 0x00000000}, {0x00009e08, 0x0038230c}, @@ -76,7 +90,7 @@ static const u32 ar9580_1p0_baseband_core[][2] = { {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, {0x00009e38, 0x0cc80c00}, - {0x00009e40, 0x0d261820}, + {0x00009e40, 0x0d261800}, {0x00009e4c, 0x00001004}, {0x00009e50, 0x00ff03f1}, {0x00009e54, 0x00000000}, @@ -198,8 +212,6 @@ static const u32 ar9580_1p0_baseband_core[][2] = { {0x0000c420, 0x00000000}, }; -#define ar9580_1p0_mac_postamble ar9300_2p2_mac_postamble - static const u32 ar9580_1p0_low_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x0380c7fc, 0x0380c7fc, 0x03aaa352, 0x03aaa352}, @@ -306,7 +318,112 @@ static const u32 ar9580_1p0_low_ob_db_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -#define ar9580_1p0_high_power_tx_gain_table ar9580_1p0_low_ob_db_tx_gain_table +static const u32 ar9580_1p0_high_power_tx_gain_table[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000a2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000a2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000a2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, + {0x0000a500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a504, 0x06000003, 0x06000003, 0x04000002, 0x04000002}, + {0x0000a508, 0x0a000020, 0x0a000020, 0x08000004, 0x08000004}, + {0x0000a50c, 0x10000023, 0x10000023, 0x0b000200, 0x0b000200}, + {0x0000a510, 0x15000028, 0x15000028, 0x0f000202, 0x0f000202}, + {0x0000a514, 0x1b00002b, 0x1b00002b, 0x12000400, 0x12000400}, + {0x0000a518, 0x1f020028, 0x1f020028, 0x16000402, 0x16000402}, + {0x0000a51c, 0x2502002b, 0x2502002b, 0x19000404, 0x19000404}, + {0x0000a520, 0x2a04002a, 0x2a04002a, 0x1c000603, 0x1c000603}, + {0x0000a524, 0x2e06002a, 0x2e06002a, 0x21000a02, 0x21000a02}, + {0x0000a528, 0x3302202d, 0x3302202d, 0x25000a04, 0x25000a04}, + {0x0000a52c, 0x3804202c, 0x3804202c, 0x28000a20, 0x28000a20}, + {0x0000a530, 0x3c06202c, 0x3c06202c, 0x2c000e20, 0x2c000e20}, + {0x0000a534, 0x4108202d, 0x4108202d, 0x30000e22, 0x30000e22}, + {0x0000a538, 0x4506402d, 0x4506402d, 0x34000e24, 0x34000e24}, + {0x0000a53c, 0x4906222d, 0x4906222d, 0x38001640, 0x38001640}, + {0x0000a540, 0x4d062231, 0x4d062231, 0x3c001660, 0x3c001660}, + {0x0000a544, 0x50082231, 0x50082231, 0x3f001861, 0x3f001861}, + {0x0000a548, 0x5608422e, 0x5608422e, 0x43001a81, 0x43001a81}, + {0x0000a54c, 0x5e08442e, 0x5e08442e, 0x47001a83, 0x47001a83}, + {0x0000a550, 0x620a4431, 0x620a4431, 0x4a001c84, 0x4a001c84}, + {0x0000a554, 0x640a4432, 0x640a4432, 0x4e001ce3, 0x4e001ce3}, + {0x0000a558, 0x680a4434, 0x680a4434, 0x52001ce5, 0x52001ce5}, + {0x0000a55c, 0x6c0a6434, 0x6c0a6434, 0x56001ce9, 0x56001ce9}, + {0x0000a560, 0x6f0a6633, 0x6f0a6633, 0x5a001ceb, 0x5a001ceb}, + {0x0000a564, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a568, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a56c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a570, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a574, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a578, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a57c, 0x730c6634, 0x730c6634, 0x5d001eec, 0x5d001eec}, + {0x0000a580, 0x00800000, 0x00800000, 0x00800000, 0x00800000}, + {0x0000a584, 0x06800003, 0x06800003, 0x04800002, 0x04800002}, + {0x0000a588, 0x0a800020, 0x0a800020, 0x08800004, 0x08800004}, + {0x0000a58c, 0x10800023, 0x10800023, 0x0b800200, 0x0b800200}, + {0x0000a590, 0x15800028, 0x15800028, 0x0f800202, 0x0f800202}, + {0x0000a594, 0x1b80002b, 0x1b80002b, 0x12800400, 0x12800400}, + {0x0000a598, 0x1f820028, 0x1f820028, 0x16800402, 0x16800402}, + {0x0000a59c, 0x2582002b, 0x2582002b, 0x19800404, 0x19800404}, + {0x0000a5a0, 0x2a84002a, 0x2a84002a, 0x1c800603, 0x1c800603}, + {0x0000a5a4, 0x2e86002a, 0x2e86002a, 0x21800a02, 0x21800a02}, + {0x0000a5a8, 0x3382202d, 0x3382202d, 0x25800a04, 0x25800a04}, + {0x0000a5ac, 0x3884202c, 0x3884202c, 0x28800a20, 0x28800a20}, + {0x0000a5b0, 0x3c86202c, 0x3c86202c, 0x2c800e20, 0x2c800e20}, + {0x0000a5b4, 0x4188202d, 0x4188202d, 0x30800e22, 0x30800e22}, + {0x0000a5b8, 0x4586402d, 0x4586402d, 0x34800e24, 0x34800e24}, + {0x0000a5bc, 0x4986222d, 0x4986222d, 0x38801640, 0x38801640}, + {0x0000a5c0, 0x4d862231, 0x4d862231, 0x3c801660, 0x3c801660}, + {0x0000a5c4, 0x50882231, 0x50882231, 0x3f801861, 0x3f801861}, + {0x0000a5c8, 0x5688422e, 0x5688422e, 0x43801a81, 0x43801a81}, + {0x0000a5cc, 0x5a88442e, 0x5a88442e, 0x47801a83, 0x47801a83}, + {0x0000a5d0, 0x5e8a4431, 0x5e8a4431, 0x4a801c84, 0x4a801c84}, + {0x0000a5d4, 0x648a4432, 0x648a4432, 0x4e801ce3, 0x4e801ce3}, + {0x0000a5d8, 0x688a4434, 0x688a4434, 0x52801ce5, 0x52801ce5}, + {0x0000a5dc, 0x6c8a6434, 0x6c8a6434, 0x56801ce9, 0x56801ce9}, + {0x0000a5e0, 0x6f8a6633, 0x6f8a6633, 0x5a801ceb, 0x5a801ceb}, + {0x0000a5e4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5e8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5ec, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5f0, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5f4, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5f8, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a5fc, 0x738c6634, 0x738c6634, 0x5d801eec, 0x5d801eec}, + {0x0000a600, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a608, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a60c, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a610, 0x01804601, 0x01804601, 0x00000000, 0x00000000}, + {0x0000a614, 0x01804601, 0x01804601, 0x01404000, 0x01404000}, + {0x0000a618, 0x01804601, 0x01804601, 0x01404501, 0x01404501}, + {0x0000a61c, 0x01804601, 0x01804601, 0x02008501, 0x02008501}, + {0x0000a620, 0x03408d02, 0x03408d02, 0x0280ca03, 0x0280ca03}, + {0x0000a624, 0x0300cc03, 0x0300cc03, 0x03010c04, 0x03010c04}, + {0x0000a628, 0x03410d04, 0x03410d04, 0x04014c04, 0x04014c04}, + {0x0000a62c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a630, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a634, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a638, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000a63c, 0x03410d04, 0x03410d04, 0x04015005, 0x04015005}, + {0x0000b2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000b2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000b2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000b2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x0000c2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, + {0x0000c2e0, 0x000f0000, 0x000f0000, 0x03ccc584, 0x03ccc584}, + {0x0000c2e4, 0x03f00000, 0x03f00000, 0x03f0f800, 0x03f0f800}, + {0x0000c2e8, 0x00000000, 0x00000000, 0x03ff0000, 0x03ff0000}, + {0x00016044, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016048, 0x65240001, 0x65240001, 0x66480001, 0x66480001}, + {0x00016068, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016288, 0x05a2040a, 0x05a2040a, 0x05a20408, 0x05a20408}, + {0x00016444, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016448, 0x65240001, 0x65240001, 0x66480001, 0x66480001}, + {0x00016468, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, + {0x00016844, 0x012492d4, 0x012492d4, 0x012492d4, 0x012492d4}, + {0x00016848, 0x65240001, 0x65240001, 0x66480001, 0x66480001}, + {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, +}; static const u32 ar9580_1p0_lowest_ob_db_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ @@ -414,8 +531,6 @@ static const u32 ar9580_1p0_lowest_ob_db_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -#define ar9580_1p0_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 - static const u32 ar9580_1p0_mac_core[][2] = { /* Addr allmodes */ {0x00000008, 0x00000000}, @@ -679,14 +794,6 @@ static const u32 ar9580_1p0_mixed_ob_db_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -#define ar9580_1p0_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 - -#define ar9580_1p0_soc_postamble ar9300_2p2_soc_postamble - -#define ar9580_1p0_high_ob_db_tx_gain_table ar9300Modes_high_ob_db_tx_gain_table_2p2 - -#define ar9580_1p0_type5_tx_gain_table ar9300Modes_type5_tx_gain_table_2p2 - static const u32 ar9580_1p0_type6_tx_gain_table[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2dc, 0x000cfff0, 0x000cfff0, 0x03aaa352, 0x03aaa352}, @@ -761,165 +868,271 @@ static const u32 ar9580_1p0_type6_tx_gain_table[][5] = { {0x00016868, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c, 0x6db6db6c}, }; -static const u32 ar9580_1p0_soc_preamble[][2] = { +static const u32 ar9580_1p0_rx_gain_table[][2] = { /* Addr allmodes */ - {0x000040a4, 0x00a0c1c9}, - {0x00007008, 0x00000000}, - {0x00007020, 0x00000000}, - {0x00007034, 0x00000002}, - {0x00007038, 0x000004c2}, - {0x00007048, 0x00000008}, -}; - -#define ar9580_1p0_rx_gain_table ar9462_common_rx_gain_table_2p0 - -static const u32 ar9580_1p0_radio_core[][2] = { - /* Addr allmodes */ - {0x00016000, 0x36db6db6}, - {0x00016004, 0x6db6db40}, - {0x00016008, 0x73f00000}, - {0x0001600c, 0x00000000}, - {0x00016040, 0x7f80fff8}, - {0x0001604c, 0x76d005b5}, - {0x00016050, 0x556cf031}, - {0x00016054, 0x13449440}, - {0x00016058, 0x0c51c92c}, - {0x0001605c, 0x3db7fffc}, - {0x00016060, 0xfffffffc}, - {0x00016064, 0x000f0278}, - {0x0001606c, 0x6db60000}, - {0x00016080, 0x00000000}, - {0x00016084, 0x0e48048c}, - {0x00016088, 0x54214514}, - {0x0001608c, 0x119f481e}, - {0x00016090, 0x24926490}, - {0x00016098, 0xd2888888}, - {0x000160a0, 0x0a108ffe}, - {0x000160a4, 0x812fc370}, - {0x000160a8, 0x423c8000}, - {0x000160b4, 0x92480080}, - {0x000160c0, 0x00adb6d0}, - {0x000160c4, 0x6db6db60}, - {0x000160c8, 0x6db6db6c}, - {0x000160cc, 0x01e6c000}, - {0x00016100, 0x3fffbe01}, - {0x00016104, 0xfff80000}, - {0x00016108, 0x00080010}, - {0x00016144, 0x02084080}, - {0x00016148, 0x00000000}, - {0x00016280, 0x058a0001}, - {0x00016284, 0x3d840208}, - {0x00016288, 0x05a20408}, - {0x0001628c, 0x00038c07}, - {0x00016290, 0x00000004}, - {0x00016294, 0x458aa14f}, - {0x00016380, 0x00000000}, - {0x00016384, 0x00000000}, - {0x00016388, 0x00800700}, - {0x0001638c, 0x00800700}, - {0x00016390, 0x00800700}, - {0x00016394, 0x00000000}, - {0x00016398, 0x00000000}, - {0x0001639c, 0x00000000}, - {0x000163a0, 0x00000001}, - {0x000163a4, 0x00000001}, - {0x000163a8, 0x00000000}, - {0x000163ac, 0x00000000}, - {0x000163b0, 0x00000000}, - {0x000163b4, 0x00000000}, - {0x000163b8, 0x00000000}, - {0x000163bc, 0x00000000}, - {0x000163c0, 0x000000a0}, - {0x000163c4, 0x000c0000}, - {0x000163c8, 0x14021402}, - {0x000163cc, 0x00001402}, - {0x000163d0, 0x00000000}, - {0x000163d4, 0x00000000}, - {0x00016400, 0x36db6db6}, - {0x00016404, 0x6db6db40}, - {0x00016408, 0x73f00000}, - {0x0001640c, 0x00000000}, - {0x00016440, 0x7f80fff8}, - {0x0001644c, 0x76d005b5}, - {0x00016450, 0x556cf031}, - {0x00016454, 0x13449440}, - {0x00016458, 0x0c51c92c}, - {0x0001645c, 0x3db7fffc}, - {0x00016460, 0xfffffffc}, - {0x00016464, 0x000f0278}, - {0x0001646c, 0x6db60000}, - {0x00016500, 0x3fffbe01}, - {0x00016504, 0xfff80000}, - {0x00016508, 0x00080010}, - {0x00016544, 0x02084080}, - {0x00016548, 0x00000000}, - {0x00016780, 0x00000000}, - {0x00016784, 0x00000000}, - {0x00016788, 0x00800700}, - {0x0001678c, 0x00800700}, - {0x00016790, 0x00800700}, - {0x00016794, 0x00000000}, - {0x00016798, 0x00000000}, - {0x0001679c, 0x00000000}, - {0x000167a0, 0x00000001}, - {0x000167a4, 0x00000001}, - {0x000167a8, 0x00000000}, - {0x000167ac, 0x00000000}, - {0x000167b0, 0x00000000}, - {0x000167b4, 0x00000000}, - {0x000167b8, 0x00000000}, - {0x000167bc, 0x00000000}, - {0x000167c0, 0x000000a0}, - {0x000167c4, 0x000c0000}, - {0x000167c8, 0x14021402}, - {0x000167cc, 0x00001402}, - {0x000167d0, 0x00000000}, - {0x000167d4, 0x00000000}, - {0x00016800, 0x36db6db6}, - {0x00016804, 0x6db6db40}, - {0x00016808, 0x73f00000}, - {0x0001680c, 0x00000000}, - {0x00016840, 0x7f80fff8}, - {0x0001684c, 0x76d005b5}, - {0x00016850, 0x556cf031}, - {0x00016854, 0x13449440}, - {0x00016858, 0x0c51c92c}, - {0x0001685c, 0x3db7fffc}, - {0x00016860, 0xfffffffc}, - {0x00016864, 0x000f0278}, - {0x0001686c, 0x6db60000}, - {0x00016900, 0x3fffbe01}, - {0x00016904, 0xfff80000}, - {0x00016908, 0x00080010}, - {0x00016944, 0x02084080}, - {0x00016948, 0x00000000}, - {0x00016b80, 0x00000000}, - {0x00016b84, 0x00000000}, - {0x00016b88, 0x00800700}, - {0x00016b8c, 0x00800700}, - {0x00016b90, 0x00800700}, - {0x00016b94, 0x00000000}, - {0x00016b98, 0x00000000}, - {0x00016b9c, 0x00000000}, - {0x00016ba0, 0x00000001}, - {0x00016ba4, 0x00000001}, - {0x00016ba8, 0x00000000}, - {0x00016bac, 0x00000000}, - {0x00016bb0, 0x00000000}, - {0x00016bb4, 0x00000000}, - {0x00016bb8, 0x00000000}, - {0x00016bbc, 0x00000000}, - {0x00016bc0, 0x000000a0}, - {0x00016bc4, 0x000c0000}, - {0x00016bc8, 0x14021402}, - {0x00016bcc, 0x00001402}, - {0x00016bd0, 0x00000000}, - {0x00016bd4, 0x00000000}, + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x01910190}, + {0x0000a030, 0x01930192}, + {0x0000a034, 0x01950194}, + {0x0000a038, 0x038a0196}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x22222229}, + {0x0000a084, 0x1d1d1d1d}, + {0x0000a088, 0x1d1d1d1d}, + {0x0000a08c, 0x1d1d1d1d}, + {0x0000a090, 0x171d1d1d}, + {0x0000a094, 0x11111717}, + {0x0000a098, 0x00030311}, + {0x0000a09c, 0x00000000}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x23232323}, + {0x0000b084, 0x21232323}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, }; static const u32 ar9580_1p0_baseband_postamble[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009814, 0x3280c00a, 0x3280c00a, 0x3280c00a, 0x3280c00a}, + {0x00009818, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881}, @@ -956,7 +1169,7 @@ static const u32 ar9580_1p0_baseband_postamble[][5] = { {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, + {0x0000a2d0, 0x00041983, 0x00041983, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, @@ -994,4 +1207,13 @@ static const u32 ar9580_1p0_pcie_phy_pll_on_clkreq[][2] = { {0x00004044, 0x00000000}, }; +static const u32 ar9580_1p0_baseband_postamble_dfs_channel[][3] = { + /* Addr 5G 2G */ + {0x00009814, 0x3400c00f, 0x3400c00f}, + {0x00009824, 0x5ac668d0, 0x5ac668d0}, + {0x00009828, 0x06903080, 0x06903080}, + {0x00009e0c, 0x6d4000e2, 0x6d4000e2}, + {0x00009e14, 0x37b9625e, 0x37b9625e}, +}; + #endif /* INITVALS_9580_1P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2ee35f677c0..2ca8f7e0617 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -23,54 +23,38 @@ #include <linux/leds.h> #include <linux/completion.h> -#include "debug.h" #include "common.h" +#include "debug.h" #include "mci.h" #include "dfs.h" - -/* - * Header for the ath9k.ko driver core *only* -- hw code nor any other driver - * should rely on this file or its contents. - */ +#include "spectral.h" struct ath_node; -/* Macro to expand scalars to 64-bit objects */ - -#define ito64(x) (sizeof(x) == 1) ? \ - (((unsigned long long int)(x)) & (0xff)) : \ - (sizeof(x) == 2) ? \ - (((unsigned long long int)(x)) & 0xffff) : \ - ((sizeof(x) == 4) ? \ - (((unsigned long long int)(x)) & 0xffffffff) : \ - (unsigned long long int)(x)) - -/* increment with wrap-around */ -#define INCR(_l, _sz) do { \ - (_l)++; \ - (_l) &= ((_sz) - 1); \ - } while (0) - -/* decrement with wrap-around */ -#define DECR(_l, _sz) do { \ - (_l)--; \ - (_l) &= ((_sz) - 1); \ - } while (0) - -#define TSF_TO_TU(_h,_l) \ - ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) - -#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i)) +extern struct ieee80211_ops ath9k_ops; +extern int ath9k_modparam_nohwcrypt; +extern int led_blink; +extern bool is_ath9k_unloaded; struct ath_config { u16 txpowlimit; - u8 cabqReadytime; }; /*************************/ /* Descriptor Management */ /*************************/ +#define ATH_TXSTATUS_RING_SIZE 512 + +/* Macro to expand scalars to 64-bit objects */ +#define ito64(x) (sizeof(x) == 1) ? \ + (((unsigned long long int)(x)) & (0xff)) : \ + (sizeof(x) == 2) ? \ + (((unsigned long long int)(x)) & 0xffff) : \ + ((sizeof(x) == 4) ? \ + (((unsigned long long int)(x)) & 0xffffffff) : \ + (unsigned long long int)(x)) + #define ATH_TXBUF_RESET(_bf) do { \ (_bf)->bf_lastbf = NULL; \ (_bf)->bf_next = NULL; \ @@ -78,23 +62,6 @@ struct ath_config { sizeof(struct ath_buf_state)); \ } while (0) -/** - * enum buffer_type - Buffer type flags - * - * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX) - * @BUF_AGGR: Indicates whether the buffer can be aggregated - * (used in aggregation scheduling) - */ -enum buffer_type { - BUF_AMPDU = BIT(0), - BUF_AGGR = BIT(1), -}; - -#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) -#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) - -#define ATH_TXSTATUS_RING_SIZE 512 - #define DS2PHYS(_dd, _ds) \ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) #define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) @@ -114,11 +81,20 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, /* RX / TX */ /***********/ +#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i)) + +/* increment with wrap-around */ +#define INCR(_l, _sz) do { \ + (_l)++; \ + (_l) &= ((_sz) - 1); \ + } while (0) + #define ATH_RXBUF 512 #define ATH_TXBUF 512 #define ATH_TXBUF_RESERVE 5 #define ATH_MAX_QDEPTH (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE) #define ATH_TXMAXTRY 13 +#define ATH_MAX_SW_RETRIES 30 #define TID_TO_WME_AC(_tid) \ ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE : \ @@ -134,6 +110,12 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_AGGR_MIN_QDEPTH 2 /* minimum h/w qdepth for non-aggregated traffic */ #define ATH_NON_AGGR_MIN_QDEPTH 8 +#define ATH_TX_COMPLETE_POLL_INT 1000 +#define ATH_TXFIFO_DEPTH 8 +#define ATH_TX_ERROR 0x01 + +/* Stop tx traffic 1ms before the GO goes away */ +#define ATH_P2P_PS_STOP_TIME 1000 #define IEEE80211_SEQ_SEQ_SHIFT 4 #define IEEE80211_SEQ_MAX 4096 @@ -166,11 +148,15 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_AN_2_TID(_an, _tidno) (&(_an)->tid[(_tidno)]) -#define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e)) +#define IS_HT_RATE(rate) (rate & 0x80) +#define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e)) +#define IS_OFDM_RATE(rate) ((rate >= 0x8) && (rate <= 0xf)) -#define ATH_TX_COMPLETE_POLL_INT 1000 +enum { + WLAN_RC_PHY_OFDM, + WLAN_RC_PHY_CCK, +}; -#define ATH_TXFIFO_DEPTH 8 struct ath_txq { int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ u32 axq_qnum; /* ath9k hardware queue number */ @@ -207,6 +193,29 @@ struct ath_frame_info { u8 baw_tracked : 1; }; +struct ath_rxbuf { + struct list_head list; + struct sk_buff *bf_mpdu; + void *bf_desc; + dma_addr_t bf_daddr; + dma_addr_t bf_buf_addr; +}; + +/** + * enum buffer_type - Buffer type flags + * + * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX) + * @BUF_AGGR: Indicates whether the buffer can be aggregated + * (used in aggregation scheduling) + */ +enum buffer_type { + BUF_AMPDU = BIT(0), + BUF_AGGR = BIT(1), +}; + +#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) +#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) + struct ath_buf_state { u8 bf_type; u8 bfs_paprd; @@ -245,7 +254,6 @@ struct ath_atx_tid { s8 bar_index; bool sched; - bool paused; bool active; }; @@ -262,6 +270,11 @@ struct ath_node { bool sleeping; bool no_ps_filter; + +#ifdef CONFIG_ATH9K_STATION_STATISTICS + struct ath_rx_rate_stats rx_rate_stats; +#endif + u8 key_idx[4]; }; struct ath_tx_control { @@ -271,7 +284,6 @@ struct ath_tx_control { struct ieee80211_sta *sta; }; -#define ATH_TX_ERROR 0x01 /** * @txq_map: Index is mac80211 queue number. This is @@ -307,7 +319,7 @@ struct ath_rx { struct ath_descdma rxdma; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; - struct ath_buf *buf_hold; + struct ath_rxbuf *buf_hold; struct sk_buff *frag; u32 ampdu_ref; @@ -358,13 +370,33 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, /********/ struct ath_vif { + struct ieee80211_vif *vif; struct ath_node mcast_node; int av_bslot; bool primary_sta_vif; __le64 tsf_adjust; /* TSF adjustment for staggered beacons */ struct ath_buf *av_bcbuf; + + /* P2P Client */ + struct ieee80211_noa_data noa; }; +struct ath9k_vif_iter_data { + u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */ + u8 mask[ETH_ALEN]; /* bssid mask */ + bool has_hw_macaddr; + + int naps; /* number of AP vifs */ + int nmeshes; /* number of mesh vifs */ + int nstations; /* number of station vifs */ + int nwds; /* number of WDS vifs */ + int nadhocs; /* number of adhoc vifs */ +}; + +void ath9k_calculate_iter_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ath9k_vif_iter_data *iter_data); + /*******************/ /* Beacon Handling */ /*******************/ @@ -378,17 +410,9 @@ struct ath_vif { #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) - -struct ath_beacon_config { - int beacon_interval; - u16 listen_interval; - u16 dtim_period; - u16 bmiss_timeout; - u8 dtim_count; - bool enable_beacon; - bool ibss_creator; -}; + +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) struct ath_beacon { enum { @@ -399,11 +423,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; @@ -413,14 +435,13 @@ struct ath_beacon { }; void ath9k_beacon_tasklet(unsigned long data); -bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed); void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif); -void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_set_beacon(struct ath_softc *sc); -bool ath9k_csa_is_finished(struct ath_softc *sc); +bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif); +void ath9k_csa_update(struct ath_softc *sc); /*******************/ /* Link Monitoring */ @@ -433,17 +454,14 @@ bool ath9k_csa_is_finished(struct ath_softc *sc); #define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ -#define ATH_ANI_MAX_SKIP_COUNT 10 - -#define ATH_PAPRD_TIMEOUT 100 /* msecs */ -#define ATH_PLL_WORK_INTERVAL 100 +#define ATH_ANI_MAX_SKIP_COUNT 10 +#define ATH_PAPRD_TIMEOUT 100 /* msecs */ +#define ATH_PLL_WORK_INTERVAL 100 void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); -void ath_hw_check(struct work_struct *work); +bool ath_hw_check(struct ath_softc *sc); void ath_hw_pll_work(struct work_struct *work); -void ath_rx_poll(unsigned long data); -void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon); void ath_paprd_calibrate(struct work_struct *work); void ath_ani_calibrate(unsigned long data); void ath_start_ani(struct ath_softc *sc); @@ -452,6 +470,9 @@ void ath_check_ani(struct ath_softc *sc); int ath_update_survey_stats(struct ath_softc *sc); void ath_update_survey_nf(struct ath_softc *sc, int channel); void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); +void ath_ps_full_sleep(unsigned long data); +void ath9k_p2p_ps_timer(void *priv); +void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif); /**********/ /* BTCOEX */ @@ -459,8 +480,8 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); #define ATH_DUMP_BTCOEX(_s, _val) \ do { \ - len += snprintf(buf + len, size - len, \ - "%20s : %10d\n", _s, (_val)); \ + len += scnprintf(buf + len, size - len, \ + "%20s : %10d\n", _s, (_val)); \ } while (0) enum bt_op_flags { @@ -469,20 +490,19 @@ enum bt_op_flags { }; struct ath_btcoex { - bool hw_timer_enabled; spinlock_t btcoex_lock; struct timer_list period_timer; /* Timer for BT period */ + struct timer_list no_stomp_timer; u32 bt_priority_cnt; unsigned long bt_priority_time; unsigned long op_flags; int bt_stomp_type; /* Types of BT stomping */ - u32 btcoex_no_stomp; /* in usec */ + u32 btcoex_no_stomp; /* in msec */ u32 btcoex_period; /* in msec */ - u32 btscan_no_stomp; /* in usec */ + u32 btscan_no_stomp; /* in msec */ u32 duty_cycle; u32 bt_wait_time; int rssi_count; - struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; u8 stomp_audio; }; @@ -530,12 +550,6 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size) } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ -struct ath9k_wow_pattern { - u8 pattern_bytes[MAX_PATTERN_SIZE]; - u8 mask_bytes[MAX_PATTERN_SIZE]; - u32 pattern_len; -}; - /********************/ /* LED Control */ /********************/ @@ -563,6 +577,40 @@ static inline void ath_fill_led_pin(struct ath_softc *sc) } #endif +/************************/ +/* Wake on Wireless LAN */ +/************************/ + +struct ath9k_wow_pattern { + u8 pattern_bytes[MAX_PATTERN_SIZE]; + u8 mask_bytes[MAX_PATTERN_SIZE]; + u32 pattern_len; +}; + +#ifdef CONFIG_ATH9K_WOW +void ath9k_init_wow(struct ieee80211_hw *hw); +int ath9k_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan); +int ath9k_resume(struct ieee80211_hw *hw); +void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled); +#else +static inline void ath9k_init_wow(struct ieee80211_hw *hw) +{ +} +static inline int ath9k_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + return 0; +} +static inline int ath9k_resume(struct ieee80211_hw *hw) +{ + return 0; +} +static inline void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ +} +#endif /* CONFIG_ATH9K_WOW */ + /*******************************/ /* Antenna diversity/combining */ /*******************************/ @@ -581,7 +629,6 @@ static inline void ath_fill_led_pin(struct ath_softc *sc) #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI 50 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI 50 -#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 #define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 #define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 #define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 @@ -626,34 +673,26 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); /* Main driver core */ /********************/ -#define ATH9K_PCI_CUS198 0x0001 -#define ATH9K_PCI_CUS230 0x0002 -#define ATH9K_PCI_CUS217 0x0004 -#define ATH9K_PCI_WOW 0x0008 -#define ATH9K_PCI_BT_ANT_DIV 0x0010 -#define ATH9K_PCI_D3_L1_WAR 0x0020 +#define ATH9K_PCI_CUS198 0x0001 +#define ATH9K_PCI_CUS230 0x0002 +#define ATH9K_PCI_CUS217 0x0004 +#define ATH9K_PCI_CUS252 0x0008 +#define ATH9K_PCI_WOW 0x0010 +#define ATH9K_PCI_BT_ANT_DIV 0x0020 +#define ATH9K_PCI_D3_L1_WAR 0x0040 +#define ATH9K_PCI_AR9565_1ANT 0x0080 +#define ATH9K_PCI_AR9565_2ANT 0x0100 +#define ATH9K_PCI_NO_PLL_PWRSAVE 0x0200 +#define ATH9K_PCI_KILLER 0x0400 /* * Default cache line size, in bytes. * Used when PCI device not fully initialized by bootrom/BIOS */ #define DEFAULT_CACHELINE 32 -#define ATH_REGCLASSIDS_MAX 10 #define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ -#define ATH_MAX_SW_RETRIES 30 -#define ATH_CHAN_MAX 255 - #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ -#define ATH_RATE_DUMMY_MARKER 0 - -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, -}; +#define MAX_GTT_CNT 5 /* Powersave flags */ #define PS_WAIT_FOR_BEACON BIT(0) @@ -663,37 +702,6 @@ enum sc_op_flags { #define PS_BEACON_SYNC BIT(4) #define PS_WAIT_FOR_ANI BIT(5) -struct ath_rate_table; - -struct ath9k_vif_iter_data { - u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */ - u8 mask[ETH_ALEN]; /* bssid mask */ - bool has_hw_macaddr; - - int naps; /* number of AP vifs */ - int nmeshes; /* number of mesh vifs */ - int nstations; /* number of station vifs */ - int nwds; /* number of WDS vifs */ - int nadhocs; /* number of adhoc vifs */ -}; - -/* enum spectral_mode: - * - * @SPECTRAL_DISABLED: spectral mode is disabled - * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with - * something else. - * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples - * is performed manually. - * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels - * during a channel scan. - */ -enum spectral_mode { - SPECTRAL_DISABLED = 0, - SPECTRAL_BACKGROUND, - SPECTRAL_MANUAL, - SPECTRAL_CHANSCAN, -}; - struct ath_softc { struct ieee80211_hw *hw; struct device *dev; @@ -711,14 +719,16 @@ struct ath_softc { spinlock_t sc_pcu_lock; struct mutex mutex; struct work_struct paprd_work; - struct work_struct hw_check_work; struct work_struct hw_reset_work; struct completion paprd_complete; + wait_queue_head_t tx_wait; + + struct ath_gen_timer *p2p_ps_timer; + struct ath_vif *p2p_ps_vif; - unsigned int hw_busy_count; - unsigned long sc_flags; unsigned long driver_data; + u8 gtt_cnt; u32 intrstatus; u16 ps_flags; /* PS_* */ u16 curtxpow; @@ -732,7 +742,6 @@ struct ath_softc { struct ath_rx rx; struct ath_tx tx; struct ath_beacon beacon; - struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; #ifdef CONFIG_MAC80211_LEDS bool led_registered; @@ -741,7 +750,6 @@ struct ath_softc { #endif struct ath9k_hw_cal_data caldata; - int last_rssi; #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; @@ -749,7 +757,7 @@ struct ath_softc { struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; - struct timer_list rx_poll_timer; + struct timer_list sleep_timer; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; @@ -758,177 +766,70 @@ struct ath_softc { #endif struct ath_descdma txsdma; - struct ieee80211_vif *csa_vif; struct ath_ant_comb ant_comb; u8 ant_tx, ant_rx; struct dfs_pattern_detector *dfs_detector; + u64 dfs_prev_pulse_ts; u32 wow_enabled; /* relay(fs) channel for spectral scan */ struct rchan *rfs_chan_spec_scan; enum spectral_mode spectral_mode; struct ath_spec_scan spec_config; -#ifdef CONFIG_PM_SLEEP + struct ieee80211_vif *tx99_vif; + struct sk_buff *tx99_skb; + bool tx99_state; + s16 tx99_power; + +#ifdef CONFIG_ATH9K_WOW atomic_t wow_got_bmiss_intr; atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ u32 wow_intr_before_sleep; #endif }; -#define SPECTRAL_SCAN_BITMASK 0x10 -/* Radar info packet format, used for DFS and spectral formats. */ -struct ath_radar_info { - u8 pulse_length_pri; - u8 pulse_length_ext; - u8 pulse_bw_info; -} __packed; - -/* The HT20 spectral data has 4 bytes of additional information at it's end. - * - * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]} - * [7:0]: all bins max_magnitude[9:2] - * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]} - * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) - */ -struct ath_ht20_mag_info { - u8 all_bins[3]; - u8 max_exp; -} __packed; - -#define SPECTRAL_HT20_NUM_BINS 56 - -/* WARNING: don't actually use this struct! MAC may vary the amount of - * data by -1/+2. This struct is for reference only. - */ -struct ath_ht20_fft_packet { - u8 data[SPECTRAL_HT20_NUM_BINS]; - struct ath_ht20_mag_info mag_info; - struct ath_radar_info radar_info; -} __packed; - -#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet)) - -/* Dynamic 20/40 mode: - * - * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]} - * [7:0]: lower bins max_magnitude[9:2] - * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]} - * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]} - * [7:0]: upper bins max_magnitude[9:2] - * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]} - * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) - */ -struct ath_ht20_40_mag_info { - u8 lower_bins[3]; - u8 upper_bins[3]; - u8 max_exp; -} __packed; - -#define SPECTRAL_HT20_40_NUM_BINS 128 - -/* WARNING: don't actually use this struct! MAC may vary the amount of - * data. This struct is for reference only. - */ -struct ath_ht20_40_fft_packet { - u8 data[SPECTRAL_HT20_40_NUM_BINS]; - struct ath_ht20_40_mag_info mag_info; - struct ath_radar_info radar_info; -} __packed; - - -#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet)) - -/* grabs the max magnitude from the all/upper/lower bins */ -static inline u16 spectral_max_magnitude(u8 *bins) -{ - return (bins[0] & 0xc0) >> 6 | - (bins[1] & 0xff) << 2 | - (bins[2] & 0x03) << 10; -} +/********/ +/* TX99 */ +/********/ -/* return the max magnitude from the all/upper/lower bins */ -static inline u8 spectral_max_index(u8 *bins) +#ifdef CONFIG_ATH9K_TX99 +void ath9k_tx99_init_debug(struct ath_softc *sc); +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl); +#else +static inline void ath9k_tx99_init_debug(struct ath_softc *sc) { - s8 m = (bins[2] & 0xfc) >> 2; - - /* TODO: this still doesn't always report the right values ... */ - if (m > 32) - m |= 0xe0; - else - m &= ~0xe0; - - return m + 29; } - -/* return the bitmap weight from the all/upper/lower bins */ -static inline u8 spectral_bitmap_weight(u8 *bins) +static inline int ath9k_tx99_send(struct ath_softc *sc, + struct sk_buff *skb, + struct ath_tx_control *txctl) { - return bins[0] & 0x3f; + return 0; } - -/* FFT sample format given to userspace via debugfs. - * - * Please keep the type/length at the front position and change - * other fields after adding another sample type - * - * TODO: this might need rework when switching to nl80211-based - * interface. - */ -enum ath_fft_sample_type { - ATH_FFT_SAMPLE_HT20 = 1, -}; - -struct fft_sample_tlv { - u8 type; /* see ath_fft_sample */ - __be16 length; - /* type dependent data follows */ -} __packed; - -struct fft_sample_ht20 { - struct fft_sample_tlv tlv; - - u8 max_exp; - - __be16 freq; - s8 rssi; - s8 noise; - - __be16 max_magnitude; - u8 max_index; - u8 bitmap_weight; - - __be64 tsf; - - u8 data[SPECTRAL_HT20_NUM_BINS]; -} __packed; - -void ath9k_tasklet(unsigned long data); -int ath_cabq_update(struct ath_softc *); +#endif /* CONFIG_ATH9K_TX99 */ static inline void ath_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); } -extern struct ieee80211_ops ath9k_ops; -extern int ath9k_modparam_nohwcrypt; -extern int led_blink; -extern bool is_ath9k_unloaded; - +void ath9k_tasklet(unsigned long data); +int ath_cabq_update(struct ath_softc *); u8 ath9k_parse_mpdudensity(u8 mpdudensity); irqreturn_t ath_isr(int irq, void *dev); +int ath_reset(struct ath_softc *sc); +void ath_cancel_work(struct ath_softc *sc); +void ath_restart_work(struct ath_softc *sc); int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops); void ath9k_deinit_device(struct ath_softc *sc); -void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_reload_chainmask_settings(struct ath_softc *sc); - -bool ath9k_uses_beacons(int type); -void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); -int ath9k_spectral_scan_config(struct ieee80211_hw *hw, - enum spectral_mode spectral_mode); - +u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate); +void ath_start_rfkill_poll(struct ath_softc *sc); +void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); +void ath9k_ps_wakeup(struct ath_softc *sc); +void ath9k_ps_restore(struct ath_softc *sc); #ifdef CONFIG_ATH9K_PCI int ath_pci_init(void); @@ -946,15 +847,4 @@ static inline int ath_ahb_init(void) { return 0; }; static inline void ath_ahb_exit(void) {}; #endif -void ath9k_ps_wakeup(struct ath_softc *sc); -void ath9k_ps_restore(struct ath_softc *sc); - -u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate); - -void ath_start_rfkill_poll(struct ath_softc *sc); -extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); -void ath9k_calculate_iter_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ath9k_vif_iter_data *iter_data); - #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index b5c16b3a37b..e387f0b2954 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, u8 chainmask = ah->txchainmask; u8 rate = 0; - sband = &sc->sbands[common->hw->conf.chandef.chan->band]; + sband = &common->sbands[common->hw->conf.chandef.chan->band]; rate = sband->bitrates[rateidx].hw_value; if (vif->bss_conf.use_short_preamble) rate |= sband->bitrates[rateidx].hw_value_short; @@ -274,28 +274,26 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) return slot; } -void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) +static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; struct ath_vif *avp = (void *)vif->drv_priv; - u64 tsfadjust; + u32 tsfadjust; if (avp->av_bslot == 0) return; - tsfadjust = cur_conf->beacon_interval * avp->av_bslot / ATH_BCBUF; - avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); + tsfadjust = cur_conf->beacon_interval * avp->av_bslot; + tsfadjust = TU_TO_USEC(tsfadjust) / ATH_BCBUF; + avp->tsf_adjust = cpu_to_le64(tsfadjust); ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", (unsigned long long)tsfadjust, avp->av_bslot); } -bool ath9k_csa_is_finished(struct ath_softc *sc) +bool ath9k_csa_is_finished(struct ath_softc *sc, struct ieee80211_vif *vif) { - struct ieee80211_vif *vif; - - vif = sc->csa_vif; if (!vif || !vif->csa_active) return false; @@ -303,11 +301,22 @@ bool ath9k_csa_is_finished(struct ath_softc *sc) return false; ieee80211_csa_finish(vif); - - sc->csa_vif = NULL; return true; } +static void ath9k_csa_update_vif(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath_softc *sc = data; + ath9k_csa_is_finished(sc, vif); +} + +void ath9k_csa_update(struct ath_softc *sc) +{ + ieee80211_iterate_active_interfaces_atomic(sc->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath9k_csa_update_vif, sc); +} + void ath9k_beacon_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; @@ -318,7 +327,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; @@ -334,8 +343,16 @@ void ath9k_beacon_tasklet(unsigned long data) if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { sc->beacon.bmisscnt++; - if (!ath9k_hw_check_alive(ah)) - ieee80211_queue_work(sc->hw, &sc->hw_check_work); + ath9k_hw_check_nav(ah); + + /* + * If the previous beacon has not been transmitted + * and a MAC/BB hang has been identified, return + * here because a chip reset would have been + * initiated. + */ + if (!ath_hw_check(sc)) + return; if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { ath_dbg(common, BSTUCK, @@ -353,13 +370,13 @@ void ath9k_beacon_tasklet(unsigned long data) return; } - /* EDMA devices check that in the tx completion function. */ - if (!edma && ath9k_csa_is_finished(sc)) - return; - slot = ath9k_beacon_choose_slot(sc); vif = sc->beacon.bslot[slot]; + /* EDMA devices check that in the tx completion function. */ + if (!edma && ath9k_csa_is_finished(sc, vif)) + return; + if (!vif || !vif->bss_conf.enable_beacon) return; @@ -438,146 +455,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 = intval; - - 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, true); + 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 dtimperiod, dtimcount, sleepduration; - int cfpperiod, cfpcount; - u32 nexttbtt = 0, intval, tsftu; - u64 tsf; - int num_beacons, offset, dtim_dec_count, cfp_dec_count; - /* 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 and cfp parameters according to - * last beacon we received (which may be none). - */ - dtimperiod = conf->dtim_period; - dtimcount = conf->dtim_count; - if (dtimcount >= dtimperiod) /* NB: sanity check */ - dtimcount = 0; - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; - - sleepduration = conf->listen_interval * intval; - - /* - * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim+cfp state for the result. - */ - tsf = ath9k_hw_gettsf64(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; - /* CFP every cfpperiod DTIM Beacon */ - cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; - if (dtim_dec_count) - cfp_dec_count++; - - dtimcount -= dtim_dec_count; - if (dtimcount < 0) - dtimcount += dtimperiod; - - cfpcount -= cfp_dec_count; - if (cfpcount < 0) - cfpcount += cfpperiod; - - bs.bs_intval = intval; - bs.bs_nexttbtt = nexttbtt; - bs.bs_dtimperiod = dtimperiod*intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; - bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; - bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; - bs.bs_cfpmaxduration = 0; - - /* - * 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. - */ - if (sleepduration > intval) { - bs.bs_bmissthreshold = conf->listen_interval * - ATH_DEFAULT_BMISS_LIMIT / 2; - } else { - 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 = roundup(IEEE80211_MS_TO_TU(100), sleepduration); - 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, "tsf: %llu tsftu: %u\n", tsf, tsftu); - ath_dbg(common, BEACON, - "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration, - bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); - - /* Set the computed STA beacon timers */ ath9k_hw_disable_interrupts(ah); ath9k_hw_set_sta_beacon_timers(ah, &bs); @@ -592,53 +481,23 @@ 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); + ath9k_cmn_beacon_config_adhoc(ah, conf); - if (conf->ibss_creator) { - nexttbtt = intval; - } else { - u32 tbtt, offset, tsftu; - u64 tsf; - - /* - * Pull nexttbtt forward to reflect the current - * sync'd TSF. - */ - tsf = ath9k_hw_gettsf64(ah); - tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; - offset = tsftu % conf->beacon_interval; - tbtt = tsftu - offset; - if (offset) - tbtt += conf->beacon_interval; - - nexttbtt = TU_TO_USEC(tbtt); - } - - 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_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); } -bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) +static bool ath9k_allow_beacon_config(struct ath_softc *sc, + struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; @@ -654,7 +513,7 @@ bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) 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"); @@ -676,11 +535,8 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc, cur_conf->beacon_interval = bss_conf->beacon_int; cur_conf->dtim_period = bss_conf->dtim_period; - cur_conf->listen_interval = 1; cur_conf->dtim_count = 1; cur_conf->ibss_creator = bss_conf->ibss_creator; - cur_conf->bmiss_timeout = - ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; /* * It looks like mac80211 may end up using beacon interval of zero in @@ -691,6 +547,9 @@ static void ath9k_cache_beacon_config(struct ath_softc *sc, if (cur_conf->beacon_interval == 0) cur_conf->beacon_interval = 100; + cur_conf->bmiss_timeout = + ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; + /* * We don't parse dtim period from mac80211 during the driver * initialization as it breaks association with hidden-ssid @@ -706,15 +565,22 @@ 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; + if (vif->type == NL80211_IFTYPE_AP) + ath9k_set_tsfadjust(sc, vif); + + if (!ath9k_allow_beacon_config(sc, vif)) + return; + 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; - } /* @@ -752,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); } } @@ -776,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/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 9963b0bf9f7..3dfc2c7f1f0 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -66,7 +66,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) .bt_first_slot_time = 5, .bt_hold_rx_clear = true, }; - u32 i, idx; bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity; if (AR_SREV_9300_20_OR_LATER(ah)) @@ -88,11 +87,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | AR_BT_DISABLE_BT_ANT; - - for (i = 0; i < 32; i++) { - idx = (debruijn32 << i) >> 27; - ah->hw_gen_timers.gen_timer_index[idx] = i; - } } EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 5e8219a91e2..278365b8a89 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -63,13 +63,13 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, return ath9k_hw_get_nf_limits(ah, chan)->nominal; } -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf) { s8 noise = ATH_DEFAULT_NOISE_FLOOR; - if (chan && chan->noisefloor) { - s8 delta = chan->noisefloor - - ATH9K_NF_CAL_NOISE_THRESH - + if (nf) { + s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - ath9k_hw_get_default_nf(ah, chan); if (delta > 0) noise += delta; @@ -119,7 +119,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, ath_dbg(common, CALIBRATE, "NFmid[%d] (%d) > MAX (%d), %s\n", i, h[i].privNF, limit->max, - (cal->nfcal_interference ? + (test_bit(NFCAL_INTF, &cal->cal_flags) ? "not corrected (due to interference)" : "correcting to MAX")); @@ -130,7 +130,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, * we bypass this limit here in order to better deal * with our environment. */ - if (!cal->nfcal_interference) + if (!test_bit(NFCAL_INTF, &cal->cal_flags)) h[i].privNF = limit->max; } } @@ -141,7 +141,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, * Re-enable the enforcement of the NF maximum again. */ if (!high_nf_mid) - cal->nfcal_interference = false; + clear_bit(NFCAL_INTF, &cal->cal_flags); } static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, @@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; struct ath9k_cal_list *currCal = ah->cal_list_curr; if (!ah->caldata) @@ -208,7 +207,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) return true; ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", - currCal->calData->calType, conf->chandef.chan->center_freq); + currCal->calData->calType, ah->curchan->chan->center_freq); ah->caldata->CalValid &= ~currCal->calData->calType; currCal->calState = CAL_WAITING; @@ -220,7 +219,7 @@ EXPORT_SYMBOL(ath9k_hw_reset_calvalid); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) { if (ah->caldata) - ah->caldata->nfcal_pending = true; + set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); @@ -242,7 +241,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; s16 default_nf = ath9k_hw_get_default_nf(ah, chan); if (ah->caldata) @@ -252,7 +250,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if (chainmask & (1 << i)) { s16 nfval; - if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; if (h) @@ -314,7 +312,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { - if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; val = REG_READ(ah, ah->nf_regs[i]); @@ -391,10 +389,10 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) } h = caldata->nfCalHist; - caldata->nfcal_pending = false; + clear_bit(NFCAL_PENDING, &caldata->cal_flags); ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); chan->noisefloor = h[0].privNF; - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); return true; } EXPORT_SYMBOL(ath9k_hw_getnf); @@ -408,7 +406,6 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, ah->caldata->channel = chan->channel; ah->caldata->channelFlags = chan->channelFlags; - ah->caldata->chanmode = chan->chanmode; h = ah->caldata->nfCalHist; default_nf = ath9k_hw_get_default_nf(ah, chan); for (i = 0; i < NUM_NF_READINGS; i++) { @@ -437,12 +434,12 @@ void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) * the baseband update the internal NF value itself, similar to * what is being done after a full reset. */ - if (!caldata->nfcal_pending) + if (!test_bit(NFCAL_PENDING, &caldata->cal_flags)) ath9k_hw_start_nfcal(ah, true); else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) ath9k_hw_getnf(ah, ah->curchan); - caldata->nfcal_interference = true; + set_bit(NFCAL_INTF, &caldata->cal_flags); } EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 3d70b8c2bcd..b8ed95e9a33 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -116,7 +116,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf); #endif /* CALIB_H */ 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-debug.c b/drivers/net/wireless/ath/ath9k/common-debug.c new file mode 100644 index 00000000000..3b289f93340 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-debug.c @@ -0,0 +1,253 @@ +/* + * 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" + +static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_hw *ah = file->private_data; + u32 len = 0, size = 6000; + char *buf; + size_t retval; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_modal_eeprom = { + .read = read_file_modal_eeprom, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + + +void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah) +{ + debugfs_create_file("modal_eeprom", S_IRUSR, debugfs_phy, ah, + &fops_modal_eeprom); +} +EXPORT_SYMBOL(ath9k_cmn_debug_modal_eeprom); + +static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_hw *ah = file->private_data; + u32 len = 0, size = 1500; + ssize_t retval = 0; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_base_eeprom = { + .read = read_file_base_eeprom, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah) +{ + debugfs_create_file("base_eeprom", S_IRUSR, debugfs_phy, ah, + &fops_base_eeprom); +} +EXPORT_SYMBOL(ath9k_cmn_debug_base_eeprom); + +void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats, + struct ath_rx_status *rs) +{ +#define RX_PHY_ERR_INC(c) rxstats->phy_err_stats[c]++ +#define RX_CMN_STAT_INC(c) (rxstats->c++) + + RX_CMN_STAT_INC(rx_pkts_all); + rxstats->rx_bytes_all += rs->rs_datalen; + + if (rs->rs_status & ATH9K_RXERR_CRC) + RX_CMN_STAT_INC(crc_err); + if (rs->rs_status & ATH9K_RXERR_DECRYPT) + RX_CMN_STAT_INC(decrypt_crc_err); + if (rs->rs_status & ATH9K_RXERR_MIC) + RX_CMN_STAT_INC(mic_err); + if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE) + RX_CMN_STAT_INC(pre_delim_crc_err); + if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST) + RX_CMN_STAT_INC(post_delim_crc_err); + if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY) + RX_CMN_STAT_INC(decrypt_busy_err); + + if (rs->rs_status & ATH9K_RXERR_PHY) { + RX_CMN_STAT_INC(phy_err); + if (rs->rs_phyerr < ATH9K_PHYERR_MAX) + RX_PHY_ERR_INC(rs->rs_phyerr); + } + +#undef RX_CMN_STAT_INC +#undef RX_PHY_ERR_INC +} +EXPORT_SYMBOL(ath9k_cmn_debug_stat_rx); + +static ssize_t read_file_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define RXS_ERR(s, e) \ + do { \ + len += scnprintf(buf + len, size - len, \ + "%18s : %10u\n", s, \ + rxstats->e); \ + } while (0) + + struct ath_rx_stats *rxstats = file->private_data; + char *buf; + unsigned int len = 0, size = 1600; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + RXS_ERR("PKTS-ALL", rx_pkts_all); + RXS_ERR("BYTES-ALL", rx_bytes_all); + RXS_ERR("BEACONS", rx_beacons); + RXS_ERR("FRAGS", rx_frags); + RXS_ERR("SPECTRAL", rx_spectral); + + RXS_ERR("CRC ERR", crc_err); + RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); + RXS_ERR("PHY ERR", phy_err); + RXS_ERR("MIC ERR", mic_err); + RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); + RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); + RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); + RXS_ERR("LENGTH-ERR", rx_len_err); + RXS_ERR("OOM-ERR", rx_oom_err); + RXS_ERR("RATE-ERR", rx_rate_err); + RXS_ERR("TOO-MANY-FRAGS", rx_too_many_frags_err); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef RXS_ERR +} + +static const struct file_operations fops_recv = { + .read = read_file_recv, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_cmn_debug_recv(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats) +{ + debugfs_create_file("recv", S_IRUSR, debugfs_phy, rxstats, + &fops_recv); +} +EXPORT_SYMBOL(ath9k_cmn_debug_recv); + +static ssize_t read_file_phy_err(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PHY_ERR(s, p) \ + len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \ + rxstats->phy_err_stats[p]); + + struct ath_rx_stats *rxstats = file->private_data; + char *buf; + unsigned int len = 0, size = 1600; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); + PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); + PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); + PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); + PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); + PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); + PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); + PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); + PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); + PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); + PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); + PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); + PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); + PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); + PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); + PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); + PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); + PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); + PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); + PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); + PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef PHY_ERR +} + +static const struct file_operations fops_phy_err = { + .read = read_file_phy_err, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats) +{ + debugfs_create_file("phy_err", S_IRUSR, debugfs_phy, rxstats, + &fops_phy_err); +} +EXPORT_SYMBOL(ath9k_cmn_debug_phy_err); diff --git a/drivers/net/wireless/ath/ath9k/common-debug.h b/drivers/net/wireless/ath/ath9k/common-debug.h new file mode 100644 index 00000000000..7c9788490f7 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-debug.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + + + +/** + * struct ath_rx_stats - RX Statistics + * @rx_pkts_all: No. of total frames received, including ones that + may have had errors. + * @rx_bytes_all: No. of total bytes received, including ones that + may have had errors. + * @crc_err: No. of frames with incorrect CRC value + * @decrypt_crc_err: No. of frames whose CRC check failed after + decryption process completed + * @phy_err: No. of frames whose reception failed because the PHY + encountered an error + * @mic_err: No. of frames with incorrect TKIP MIC verification failure + * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections + * @post_delim_crc_err: Post-Frame delimiter CRC error detections + * @decrypt_busy_err: Decryption interruptions counter + * @phy_err_stats: Individual PHY error statistics + * @rx_len_err: No. of frames discarded due to bad length. + * @rx_oom_err: No. of frames dropped due to OOM issues. + * @rx_rate_err: No. of frames dropped due to rate errors. + * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. + * @rx_beacons: No. of beacons received. + * @rx_frags: No. of rx-fragements received. + * @rx_spectral: No of spectral packets received. + */ +struct ath_rx_stats { + u32 rx_pkts_all; + u32 rx_bytes_all; + u32 crc_err; + u32 decrypt_crc_err; + u32 phy_err; + u32 mic_err; + u32 pre_delim_crc_err; + u32 post_delim_crc_err; + u32 decrypt_busy_err; + u32 phy_err_stats[ATH9K_PHYERR_MAX]; + u32 rx_len_err; + u32 rx_oom_err; + u32 rx_rate_err; + u32 rx_too_many_frags_err; + u32 rx_beacons; + u32 rx_frags; + u32 rx_spectral; +}; + +void ath9k_cmn_debug_modal_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah); +void ath9k_cmn_debug_base_eeprom(struct dentry *debugfs_phy, + struct ath_hw *ah); +void ath9k_cmn_debug_stat_rx(struct ath_rx_stats *rxstats, + struct ath_rx_status *rs); +void ath9k_cmn_debug_recv(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats); +void ath9k_cmn_debug_phy_err(struct dentry *debugfs_phy, + struct ath_rx_stats *rxstats); diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c new file mode 100644 index 00000000000..a006c149972 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-init.c @@ -0,0 +1,244 @@ +/* + * 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. + */ + +/* We use the hw_value as an index into our private channel structure */ + +#include "common.h" + +#define CHAN2G(_freq, _idx) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +/* Some 2 GHz radios are actually tunable on 2312-2732 + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static const struct ieee80211_channel ath9k_2ghz_chantable[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Some 5 GHz radios are actually tunable on XXXX-YYYY + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static const struct ieee80211_channel ath9k_5ghz_chantable[] = { + /* _We_ call this UNII 1 */ + CHAN5G(5180, 14), /* Channel 36 */ + CHAN5G(5200, 15), /* Channel 40 */ + CHAN5G(5220, 16), /* Channel 44 */ + CHAN5G(5240, 17), /* Channel 48 */ + /* _We_ call this UNII 2 */ + CHAN5G(5260, 18), /* Channel 52 */ + CHAN5G(5280, 19), /* Channel 56 */ + CHAN5G(5300, 20), /* Channel 60 */ + CHAN5G(5320, 21), /* Channel 64 */ + /* _We_ call this "Middle band" */ + CHAN5G(5500, 22), /* Channel 100 */ + CHAN5G(5520, 23), /* Channel 104 */ + CHAN5G(5540, 24), /* Channel 108 */ + CHAN5G(5560, 25), /* Channel 112 */ + CHAN5G(5580, 26), /* Channel 116 */ + CHAN5G(5600, 27), /* Channel 120 */ + CHAN5G(5620, 28), /* Channel 124 */ + CHAN5G(5640, 29), /* Channel 128 */ + CHAN5G(5660, 30), /* Channel 132 */ + CHAN5G(5680, 31), /* Channel 136 */ + CHAN5G(5700, 32), /* Channel 140 */ + /* _We_ call this UNII 3 */ + CHAN5G(5745, 33), /* Channel 149 */ + CHAN5G(5765, 34), /* Channel 153 */ + CHAN5G(5785, 35), /* Channel 157 */ + CHAN5G(5805, 36), /* Channel 161 */ + CHAN5G(5825, 37), /* Channel 165 */ +}; + +/* Atheros hardware rate code addition for short premble */ +#define SHPCHECK(__hw_rate, __flags) \ + ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ +} + +static struct ieee80211_rate ath9k_legacy_rates[] = { + RATE(10, 0x1b, 0), + RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), + RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ | + IEEE80211_RATE_SUPPORTS_10MHZ)), +}; + +int ath9k_cmn_init_channels_rates(struct ath_common *common) +{ + struct ath_hw *ah = (struct ath_hw *)common->ah; + void *channels; + + BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) + + ARRAY_SIZE(ath9k_5ghz_chantable) != + ATH9K_NUM_CHANNELS); + + if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { + channels = devm_kzalloc(ah->dev, + sizeof(ath9k_2ghz_chantable), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + memcpy(channels, ath9k_2ghz_chantable, + sizeof(ath9k_2ghz_chantable)); + common->sbands[IEEE80211_BAND_2GHZ].channels = channels; + common->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + common->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_chantable); + common->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; + common->sbands[IEEE80211_BAND_2GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates); + } + + if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { + channels = devm_kzalloc(ah->dev, + sizeof(ath9k_5ghz_chantable), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + memcpy(channels, ath9k_5ghz_chantable, + sizeof(ath9k_5ghz_chantable)); + common->sbands[IEEE80211_BAND_5GHZ].channels = channels; + common->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; + common->sbands[IEEE80211_BAND_5GHZ].n_channels = + ARRAY_SIZE(ath9k_5ghz_chantable); + common->sbands[IEEE80211_BAND_5GHZ].bitrates = + ath9k_legacy_rates + 4; + common->sbands[IEEE80211_BAND_5GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates) - 4; + } + return 0; +} +EXPORT_SYMBOL(ath9k_cmn_init_channels_rates); + +void ath9k_cmn_setup_ht_cap(struct ath_hw *ah, + struct ieee80211_sta_ht_cap *ht_info) +{ + struct ath_common *common = ath9k_hw_common(ah); + u8 tx_streams, rx_streams; + int i, max_streams; + + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + + if (ah->caps.hw_caps & ATH9K_HW_CAP_LDPC) + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + + if (ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20) + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + + if (AR_SREV_9271(ah) || AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) + max_streams = 1; + else if (AR_SREV_9462(ah)) + max_streams = 2; + else if (AR_SREV_9300_20_OR_LATER(ah)) + max_streams = 3; + else + max_streams = 2; + + if (AR_SREV_9280_20_OR_LATER(ah)) { + if (max_streams >= 2) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + } + + /* set up supported mcs set */ + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams); + rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams); + + ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", + tx_streams, rx_streams); + + if (tx_streams != rx_streams) { + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } + + for (i = 0; i < rx_streams; i++) + ht_info->mcs.rx_mask[i] = 0xff; + + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; +} +EXPORT_SYMBOL(ath9k_cmn_setup_ht_cap); + +void ath9k_cmn_reload_chainmask(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + + if (!(ah->caps.hw_caps & ATH9K_HW_CAP_HT)) + return; + + if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) + ath9k_cmn_setup_ht_cap(ah, + &common->sbands[IEEE80211_BAND_2GHZ].ht_cap); + if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) + ath9k_cmn_setup_ht_cap(ah, + &common->sbands[IEEE80211_BAND_5GHZ].ht_cap); +} +EXPORT_SYMBOL(ath9k_cmn_reload_chainmask); diff --git a/drivers/net/wireless/ath/ath9k/common-init.h b/drivers/net/wireless/ath/ath9k/common-init.h new file mode 100644 index 00000000000..ac03fca5ffd --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/common-init.h @@ -0,0 +1,20 @@ +/* + * 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. + */ + +int ath9k_cmn_init_channels_rates(struct ath_common *common); +void ath9k_cmn_setup_ht_cap(struct ath_hw *ah, + struct ieee80211_sta_ht_cap *ht_info); +void ath9k_cmn_reload_chainmask(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index d3063c21e16..c6dd7f1fed6 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -27,6 +27,250 @@ MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Shared library for Atheros wireless 802.11n LAN cards."); MODULE_LICENSE("Dual BSD/GPL"); +/* Assumes you've already done the endian to CPU conversion */ +bool ath9k_cmn_rx_accept(struct ath_common *common, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *rxs, + struct ath_rx_status *rx_stats, + bool *decrypt_error, + unsigned int rxfilter) +{ + struct ath_hw *ah = common->ah; + bool is_mc, is_valid_tkip, strip_mic, mic_error; + __le16 fc; + + fc = hdr->frame_control; + + is_mc = !!is_multicast_ether_addr(hdr->addr1); + is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID && + test_bit(rx_stats->rs_keyix, common->tkip_keymap); + strip_mic = is_valid_tkip && ieee80211_is_data(fc) && + ieee80211_has_protected(fc) && + !(rx_stats->rs_status & + (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | + ATH9K_RXERR_KEYMISS)); + + /* + * Key miss events are only relevant for pairwise keys where the + * descriptor does contain a valid key index. This has been observed + * mostly with CCMP encryption. + */ + if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID || + !test_bit(rx_stats->rs_keyix, common->ccmp_keymap)) + rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; + + mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) && + !ieee80211_has_morefrags(fc) && + !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) && + (rx_stats->rs_status & ATH9K_RXERR_MIC); + + /* + * The rx_stats->rs_status will not be set until the end of the + * chained descriptors so it can be ignored if rs_more is set. The + * rs_more will be false at the last element of the chained + * descriptors. + */ + if (rx_stats->rs_status != 0) { + u8 status_mask; + + if (rx_stats->rs_status & ATH9K_RXERR_CRC) { + rxs->flag |= RX_FLAG_FAILED_FCS_CRC; + mic_error = false; + } + + if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) || + (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) { + *decrypt_error = true; + mic_error = false; + } + + + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_KEYMISS; + + if (ah->is_monitoring && (rxfilter & FIF_FCSFAIL)) + status_mask |= ATH9K_RXERR_CRC; + + if (rx_stats->rs_status & ~status_mask) + return false; + } + + /* + * For unicast frames the MIC error bit can have false positives, + * so all MIC error reports need to be validated in software. + * False negatives are not common, so skip software verification + * if the hardware considers the MIC valid. + */ + if (strip_mic) + rxs->flag |= RX_FLAG_MMIC_STRIPPED; + else if (is_mc && mic_error) + rxs->flag |= RX_FLAG_MMIC_ERROR; + + return true; +} +EXPORT_SYMBOL(ath9k_cmn_rx_accept); + +void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs, + bool decrypt_error) +{ + struct ath_hw *ah = common->ah; + struct ieee80211_hdr *hdr; + int hdrlen, padpos, padsize; + u8 keyix; + __le16 fc; + + /* see if any padding is done by the hw and remove it */ + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + fc = hdr->frame_control; + padpos = ieee80211_hdrlen(fc); + + /* The MAC header is padded to have 32-bit boundary if the + * packet payload is non-zero. The general calculation for + * padsize would take into account odd header lengths: + * padsize = (4 - padpos % 4) % 4; However, since only + * even-length headers are used, padding can only be 0 or 2 + * bytes and we can optimize this a bit. In addition, we must + * not try to remove padding from short control frames that do + * not have payload. */ + padsize = padpos & 3; + if (padsize && skb->len>=padpos+padsize+FCS_LEN) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + } + + keyix = rx_stats->rs_keyix; + + if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error && + ieee80211_has_protected(fc)) { + rxs->flag |= RX_FLAG_DECRYPTED; + } else if (ieee80211_has_protected(fc) + && !decrypt_error && skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + + if (test_bit(keyix, common->keymap)) + rxs->flag |= RX_FLAG_DECRYPTED; + } + if (ah->sw_mgmt_crypto && + (rxs->flag & RX_FLAG_DECRYPTED) && + ieee80211_is_mgmt(fc)) + /* Use software decrypt for management frames. */ + rxs->flag &= ~RX_FLAG_DECRYPTED; +} +EXPORT_SYMBOL(ath9k_cmn_rx_skb_postprocess); + +int ath9k_cmn_process_rate(struct ath_common *common, + struct ieee80211_hw *hw, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs) +{ + struct ieee80211_supported_band *sband; + enum ieee80211_band band; + unsigned int i = 0; + struct ath_hw *ah = common->ah; + + band = ah->curchan->chan->band; + sband = hw->wiphy->bands[band]; + + if (IS_CHAN_QUARTER_RATE(ah->curchan)) + rxs->flag |= RX_FLAG_5MHZ; + else if (IS_CHAN_HALF_RATE(ah->curchan)) + rxs->flag |= RX_FLAG_10MHZ; + + if (rx_stats->rs_rate & 0x80) { + /* HT rate */ + rxs->flag |= RX_FLAG_HT; + rxs->flag |= rx_stats->flag; + rxs->rate_idx = rx_stats->rs_rate & 0x7f; + return 0; + } + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].hw_value == rx_stats->rs_rate) { + rxs->rate_idx = i; + return 0; + } + if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) { + rxs->flag |= RX_FLAG_SHORTPRE; + rxs->rate_idx = i; + return 0; + } + } + + return -EINVAL; +} +EXPORT_SYMBOL(ath9k_cmn_process_rate); + +void ath9k_cmn_process_rssi(struct ath_common *common, + struct ieee80211_hw *hw, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs) +{ + struct ath_hw *ah = common->ah; + int last_rssi; + int rssi = rx_stats->rs_rssi; + int i, j; + + /* + * RSSI is not available for subframes in an A-MPDU. + */ + if (rx_stats->rs_moreaggr) { + rxs->flag |= RX_FLAG_NO_SIGNAL_VAL; + return; + } + + /* + * Check if the RSSI for the last subframe in an A-MPDU + * or an unaggregated frame is valid. + */ + if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) { + rxs->flag |= RX_FLAG_NO_SIGNAL_VAL; + return; + } + + for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) { + s8 rssi; + + if (!(ah->rxchainmask & BIT(i))) + continue; + + rssi = rx_stats->rs_rssi_ctl[i]; + if (rssi != ATH9K_RSSI_BAD) { + rxs->chains |= BIT(j); + rxs->chain_signal[j] = ah->noise + rssi; + } + j++; + } + + /* + * Update Beacon RSSI, this is used by ANI. + */ + if (rx_stats->is_mybeacon && + ((ah->opmode == NL80211_IFTYPE_STATION) || + (ah->opmode == NL80211_IFTYPE_ADHOC))) { + ATH_RSSI_LPF(common->last_rssi, rx_stats->rs_rssi); + last_rssi = common->last_rssi; + + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); + if (rssi < 0) + rssi = 0; + + ah->stats.avgbrssi = rssi; + } + + rxs->signal = ah->noise + rx_stats->rs_rssi; +} +EXPORT_SYMBOL(ath9k_cmn_process_rssi); + int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) { struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -49,103 +293,62 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) } EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); -static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef) -{ - u32 chanmode = 0; - - switch (chandef->chan->band) { - case IEEE80211_BAND_2GHZ: - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - chanmode = CHANNEL_G_HT20; - break; - case NL80211_CHAN_WIDTH_40: - if (chandef->center_freq1 > chandef->chan->center_freq) - chanmode = CHANNEL_G_HT40PLUS; - else - chanmode = CHANNEL_G_HT40MINUS; - break; - default: - break; - } - break; - case IEEE80211_BAND_5GHZ: - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - chanmode = CHANNEL_A_HT20; - break; - case NL80211_CHAN_WIDTH_40: - if (chandef->center_freq1 > chandef->chan->center_freq) - chanmode = CHANNEL_A_HT40PLUS; - else - chanmode = CHANNEL_A_HT40MINUS; - break; - default: - break; - } - break; - default: - break; - } - - return chanmode; -} - /* * Update internal channel flags. */ -void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, - struct cfg80211_chan_def *chandef) +static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, + struct cfg80211_chan_def *chandef) { - ichan->channel = chandef->chan->center_freq; - ichan->chan = chandef->chan; - - if (chandef->chan->band == IEEE80211_BAND_2GHZ) { - ichan->chanmode = CHANNEL_G; - ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM; - } else { - ichan->chanmode = CHANNEL_A; - ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; - } + struct ieee80211_channel *chan = chandef->chan; + u16 flags = 0; + + ichan->channel = chan->center_freq; + ichan->chan = chan; + + if (chan->band == IEEE80211_BAND_5GHZ) + flags |= CHANNEL_5GHZ; switch (chandef->width) { case NL80211_CHAN_WIDTH_5: - ichan->channelFlags |= CHANNEL_QUARTER; + flags |= CHANNEL_QUARTER; break; case NL80211_CHAN_WIDTH_10: - ichan->channelFlags |= CHANNEL_HALF; + flags |= CHANNEL_HALF; break; case NL80211_CHAN_WIDTH_20_NOHT: break; case NL80211_CHAN_WIDTH_20: + flags |= CHANNEL_HT; + break; case NL80211_CHAN_WIDTH_40: - ichan->chanmode = ath9k_get_extchanmode(chandef); + if (chandef->center_freq1 > chandef->chan->center_freq) + flags |= CHANNEL_HT40PLUS | CHANNEL_HT; + else + flags |= CHANNEL_HT40MINUS | CHANNEL_HT; break; default: WARN_ON(1); } + + ichan->channelFlags = flags; } -EXPORT_SYMBOL(ath9k_cmn_update_ichannel); /* * Get the internal channel reference. */ -struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, - struct ath_hw *ah) +struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw, + struct ath_hw *ah, + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *curchan = hw->conf.chandef.chan; + struct ieee80211_channel *curchan = chandef->chan; struct ath9k_channel *channel; - u8 chan_idx; - chan_idx = curchan->hw_value; - channel = &ah->channels[chan_idx]; - ath9k_cmn_update_ichannel(channel, &hw->conf.chandef); + channel = &ah->channels[curchan->hw_value]; + ath9k_cmn_update_ichannel(channel, chandef); return channel; } -EXPORT_SYMBOL(ath9k_cmn_get_curchannel); +EXPORT_SYMBOL(ath9k_cmn_get_channel); int ath9k_cmn_count_streams(unsigned int chainmask, int max) { diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index e039bcbfbd7..ffc454b1863 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -21,6 +21,10 @@ #include "hw.h" #include "hw-ops.h" +#include "common-init.h" +#include "common-beacon.h" +#include "common-debug.h" + /* Common header for Atheros 802.11n base driver cores */ #define WME_BA_BMP_SIZE 64 @@ -42,11 +46,42 @@ #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, + struct ath_rx_status *rx_stats, + bool *decrypt_error, + unsigned int rxfilter); +void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, + struct sk_buff *skb, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs, + bool decrypt_error); +int ath9k_cmn_process_rate(struct ath_common *common, + struct ieee80211_hw *hw, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs); +void ath9k_cmn_process_rssi(struct ath_common *common, + struct ieee80211_hw *hw, + struct ath_rx_status *rx_stats, + struct ieee80211_rx_status *rxs); int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); -void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, - struct cfg80211_chan_def *chandef); -struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, - struct ath_hw *ah); +struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw, + struct ath_hw *ah, + struct cfg80211_chan_def *chandef); int ath9k_cmn_count_streams(unsigned int chainmask, int max); void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, enum ath_stomp_type stomp_type); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index c088744a6bf..6cc42be48d4 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -17,7 +17,6 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/export.h> -#include <linux/relay.h> #include <asm/unaligned.h> #include "ath9k.h" @@ -27,6 +26,47 @@ #define REG_READ_D(_ah, _reg) \ ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) +void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause) +{ + if (sync_cause) + sc->debug.stats.istats.sync_cause_all++; + if (sync_cause & AR_INTR_SYNC_RTC_IRQ) + sc->debug.stats.istats.sync_rtc_irq++; + if (sync_cause & AR_INTR_SYNC_MAC_IRQ) + sc->debug.stats.istats.sync_mac_irq++; + if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS) + sc->debug.stats.istats.eeprom_illegal_access++; + if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT) + sc->debug.stats.istats.apb_timeout++; + if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT) + sc->debug.stats.istats.pci_mode_conflict++; + if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) + sc->debug.stats.istats.host1_fatal++; + if (sync_cause & AR_INTR_SYNC_HOST1_PERR) + sc->debug.stats.istats.host1_perr++; + if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR) + sc->debug.stats.istats.trcv_fifo_perr++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP) + sc->debug.stats.istats.radm_cpl_ep++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT) + sc->debug.stats.istats.radm_cpl_dllp_abort++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT) + sc->debug.stats.istats.radm_cpl_tlp_abort++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR) + sc->debug.stats.istats.radm_cpl_ecrc_err++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) + sc->debug.stats.istats.radm_cpl_timeout++; + if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) + sc->debug.stats.istats.local_timeout++; + if (sync_cause & AR_INTR_SYNC_PM_ACCESS) + sc->debug.stats.istats.pm_access++; + if (sync_cause & AR_INTR_SYNC_MAC_AWAKE) + sc->debug.stats.istats.mac_awake++; + if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP) + sc->debug.stats.istats.mac_asleep++; + if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS) + sc->debug.stats.istats.mac_sleep_access++; +} static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -95,46 +135,45 @@ static ssize_t read_file_ani(struct file *file, char __user *user_buf, struct ath_softc *sc = file->private_data; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; - unsigned int len = 0, size = 1024; + unsigned int len = 0; + 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 += snprintf(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 += snprintf(buf + len, size - len, "%15s: %s\n", - "ANI", "ENABLED"); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "ANI RESET", ah->stats.ast_ani_reset); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "SPUR UP", ah->stats.ast_ani_spurup); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "SPUR DOWN", ah->stats.ast_ani_spurup); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "MRC-CCK ON", ah->stats.ast_ani_ccklow); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "MRC-CCK OFF", ah->stats.ast_ani_cckhigh); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "FIR-STEP UP", ah->stats.ast_ani_stepup); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "FIR-STEP DOWN", ah->stats.ast_ani_stepdown); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs); - len += snprintf(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; @@ -169,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); @@ -267,83 +306,83 @@ static ssize_t read_file_antenna_diversity(struct file *file, struct ath_antenna_stats *as_main = &sc->debug.stats.ant_stats[ANT_MAIN]; struct ath_antenna_stats *as_alt = &sc->debug.stats.ant_stats[ANT_ALT]; struct ath_hw_antcomb_conf div_ant_conf; - unsigned int len = 0, size = 1024; + unsigned int len = 0; + const unsigned int size = 1024; ssize_t retval = 0; char *buf; - char *lna_conf_str[4] = {"LNA1_MINUS_LNA2", - "LNA2", - "LNA1", - "LNA1_PLUS_LNA2"}; + static const char *lna_conf_str[4] = { + "LNA1_MINUS_LNA2", "LNA2", "LNA1", "LNA1_PLUS_LNA2" + }; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; if (!(pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) { - len += snprintf(buf + len, size - len, "%s\n", - "Antenna Diversity Combining is disabled"); + len += scnprintf(buf + len, size - len, "%s\n", + "Antenna Diversity Combining is disabled"); goto exit; } ath9k_ps_wakeup(sc); ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); - len += snprintf(buf + len, size - len, "Current MAIN config : %s\n", - lna_conf_str[div_ant_conf.main_lna_conf]); - len += snprintf(buf + len, size - len, "Current ALT config : %s\n", - lna_conf_str[div_ant_conf.alt_lna_conf]); - len += snprintf(buf + len, size - len, "Average MAIN RSSI : %d\n", - as_main->rssi_avg); - len += snprintf(buf + len, size - len, "Average ALT RSSI : %d\n\n", - as_alt->rssi_avg); + len += scnprintf(buf + len, size - len, "Current MAIN config : %s\n", + lna_conf_str[div_ant_conf.main_lna_conf]); + len += scnprintf(buf + len, size - len, "Current ALT config : %s\n", + lna_conf_str[div_ant_conf.alt_lna_conf]); + len += scnprintf(buf + len, size - len, "Average MAIN RSSI : %d\n", + as_main->rssi_avg); + len += scnprintf(buf + len, size - len, "Average ALT RSSI : %d\n\n", + as_alt->rssi_avg); ath9k_ps_restore(sc); - len += snprintf(buf + len, size - len, "Packet Receive Cnt:\n"); - len += snprintf(buf + len, size - len, "-------------------\n"); - - len += snprintf(buf + len, size - len, "%30s%15s\n", - "MAIN", "ALT"); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "TOTAL COUNT", - as_main->recv_cnt, - as_alt->recv_cnt); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA2", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 + LNA2", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 - LNA2", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); - - len += snprintf(buf + len, size - len, "\nLNA Config Attempts:\n"); - len += snprintf(buf + len, size - len, "--------------------\n"); - - len += snprintf(buf + len, size - len, "%30s%15s\n", - "MAIN", "ALT"); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA2", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 + LNA2", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 - LNA2", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); + len += scnprintf(buf + len, size - len, "Packet Receive Cnt:\n"); + len += scnprintf(buf + len, size - len, "-------------------\n"); + + len += scnprintf(buf + len, size - len, "%30s%15s\n", + "MAIN", "ALT"); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "TOTAL COUNT", + as_main->recv_cnt, + as_alt->recv_cnt); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 + LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 - LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); + + len += scnprintf(buf + len, size - len, "\nLNA Config Attempts:\n"); + len += scnprintf(buf + len, size - len, "--------------------\n"); + + len += scnprintf(buf + len, size - len, "%30s%15s\n", + "MAIN", "ALT"); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 + LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 - LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); exit: if (len > size) @@ -385,21 +424,21 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); - len += snprintf(buf + len, DMA_BUF_LEN - len, - "Raw DMA Debug values:\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "Raw DMA Debug values:\n"); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { if (i % 4 == 0) - len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n"); val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32))); - len += snprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ", - i, val[i]); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ", + i, val[i]); } - len += snprintf(buf + len, DMA_BUF_LEN - len, "\n\n"); - len += snprintf(buf + len, DMA_BUF_LEN - len, - "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { if (i == 8) { @@ -412,39 +451,39 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, dcuBase++; } - len += snprintf(buf + len, DMA_BUF_LEN - len, - "%2d %2x %1x %2x %2x\n", - i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, - (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), - val[2] & (0x7 << (i * 3)) >> (i * 3), - (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "%2d %2x %1x %2x %2x\n", + i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, + (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), + val[2] & (0x7 << (i * 3)) >> (i * 3), + (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); } - len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n"); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "qcu_stitch state: %2x qcu_fetch state: %2x\n", (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "qcu_complete state: %2x dcu_complete state: %2x\n", (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "dcu_arb state: %2x dcu_fp state: %2x\n", (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); - len += snprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n", - REG_READ_D(ah, AR_OBS_BUS_1)); - len += snprintf(buf + len, DMA_BUF_LEN - len, - "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR)); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n", + REG_READ_D(ah, AR_OBS_BUS_1)); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR)); ath9k_ps_restore(sc); @@ -530,9 +569,9 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, #define PR_IS(a, s) \ do { \ - len += snprintf(buf + len, mxlen - len, \ - "%21s: %10u\n", a, \ - sc->debug.stats.istats.s); \ + len += scnprintf(buf + len, mxlen - len, \ + "%21s: %10u\n", a, \ + sc->debug.stats.istats.s); \ } while (0) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { @@ -563,8 +602,8 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, PR_IS("GENTIMER", gen_timer); PR_IS("TOTAL", total); - len += snprintf(buf + len, mxlen - len, - "SYNC_CAUSE stats:\n"); + len += scnprintf(buf + len, mxlen - len, + "SYNC_CAUSE stats:\n"); PR_IS("Sync-All", sync_cause_all); PR_IS("RTC-IRQ", sync_rtc_irq); @@ -655,16 +694,16 @@ static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq, ath_txq_lock(sc, txq); - len += snprintf(buf + len, size - len, "%s: %d ", - "qnum", txq->axq_qnum); - len += snprintf(buf + len, size - len, "%s: %2d ", - "qdepth", txq->axq_depth); - len += snprintf(buf + len, size - len, "%s: %2d ", - "ampdu-depth", txq->axq_ampdu_depth); - len += snprintf(buf + len, size - len, "%s: %3d ", - "pending", txq->pending_frames); - len += snprintf(buf + len, size - len, "%s: %d\n", - "stopped", txq->stopped); + len += scnprintf(buf + len, size - len, "%s: %d ", + "qnum", txq->axq_qnum); + len += scnprintf(buf + len, size - len, "%s: %2d ", + "qdepth", txq->axq_depth); + len += scnprintf(buf + len, size - len, "%s: %2d ", + "ampdu-depth", txq->axq_ampdu_depth); + len += scnprintf(buf + len, size - len, "%s: %3d ", + "pending", txq->pending_frames); + len += scnprintf(buf + len, size - len, "%s: %d\n", + "stopped", txq->stopped); ath_txq_unlock(sc, txq); return len; @@ -676,10 +715,13 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf, struct ath_softc *sc = file->private_data; struct ath_txq *txq; char *buf; - unsigned int len = 0, size = 1024; + unsigned int len = 0; + const unsigned int size = 1024; ssize_t retval = 0; int i; - char *qname[4] = {"VO", "VI", "BE", "BK"}; + static const char *qname[4] = { + "VO", "VI", "BE", "BK" + }; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) @@ -687,11 +729,11 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf, for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; - len += snprintf(buf + len, size - len, "(%s): ", qname[i]); + len += scnprintf(buf + len, size - len, "(%s): ", qname[i]); len += print_queue(sc, txq, buf + len, size - len); } - len += snprintf(buf + len, size - len, "(CAB): "); + len += scnprintf(buf + len, size - len, "(CAB): "); len += print_queue(sc, sc->beacon.cabq, buf + len, size - len); if (len > size) @@ -716,80 +758,82 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, unsigned int reg; u32 rxfilter; - len += snprintf(buf + len, sizeof(buf) - len, - "BSSID: %pM\n", common->curbssid); - len += snprintf(buf + len, sizeof(buf) - len, - "BSSID-MASK: %pM\n", common->bssidmask); - len += snprintf(buf + len, sizeof(buf) - len, - "OPMODE: %s\n", ath_opmode_to_string(sc->sc_ah->opmode)); + len += scnprintf(buf + len, sizeof(buf) - len, + "BSSID: %pM\n", common->curbssid); + len += scnprintf(buf + len, sizeof(buf) - len, + "BSSID-MASK: %pM\n", common->bssidmask); + len += scnprintf(buf + len, sizeof(buf) - len, + "OPMODE: %s\n", + ath_opmode_to_string(sc->sc_ah->opmode)); ath9k_ps_wakeup(sc); rxfilter = ath9k_hw_getrxfilter(sc->sc_ah); ath9k_ps_restore(sc); - len += snprintf(buf + len, sizeof(buf) - len, - "RXFILTER: 0x%x", rxfilter); + len += scnprintf(buf + len, sizeof(buf) - len, + "RXFILTER: 0x%x", rxfilter); if (rxfilter & ATH9K_RX_FILTER_UCAST) - len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); + len += scnprintf(buf + len, sizeof(buf) - len, " UCAST"); if (rxfilter & ATH9K_RX_FILTER_MCAST) - len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); + len += scnprintf(buf + len, sizeof(buf) - len, " MCAST"); if (rxfilter & ATH9K_RX_FILTER_BCAST) - len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); + len += scnprintf(buf + len, sizeof(buf) - len, " BCAST"); if (rxfilter & ATH9K_RX_FILTER_CONTROL) - len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); + len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL"); if (rxfilter & ATH9K_RX_FILTER_BEACON) - len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); + len += scnprintf(buf + len, sizeof(buf) - len, " BEACON"); if (rxfilter & ATH9K_RX_FILTER_PROM) - len += snprintf(buf + len, sizeof(buf) - len, " PROM"); + len += scnprintf(buf + len, sizeof(buf) - len, " PROM"); if (rxfilter & ATH9K_RX_FILTER_PROBEREQ) - len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); + len += scnprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); if (rxfilter & ATH9K_RX_FILTER_PHYERR) - len += snprintf(buf + len, sizeof(buf) - len, " PHYERR"); + len += scnprintf(buf + len, sizeof(buf) - len, " PHYERR"); if (rxfilter & ATH9K_RX_FILTER_MYBEACON) - len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON"); + len += scnprintf(buf + len, sizeof(buf) - len, " MYBEACON"); if (rxfilter & ATH9K_RX_FILTER_COMP_BAR) - len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); + len += scnprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); if (rxfilter & ATH9K_RX_FILTER_PSPOLL) - len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL"); + len += scnprintf(buf + len, sizeof(buf) - len, " PSPOLL"); if (rxfilter & ATH9K_RX_FILTER_PHYRADAR) - len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); + len += scnprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); if (rxfilter & ATH9K_RX_FILTER_MCAST_BCAST_ALL) - len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); + len += scnprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); if (rxfilter & ATH9K_RX_FILTER_CONTROL_WRAPPER) - len += snprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER"); + len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER"); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += scnprintf(buf + len, sizeof(buf) - len, "\n"); reg = sc->sc_ah->imask; - len += snprintf(buf + len, sizeof(buf) - len, "INTERRUPT-MASK: 0x%x", reg); + len += scnprintf(buf + len, sizeof(buf) - len, + "INTERRUPT-MASK: 0x%x", reg); if (reg & ATH9K_INT_SWBA) - len += snprintf(buf + len, sizeof(buf) - len, " SWBA"); + len += scnprintf(buf + len, sizeof(buf) - len, " SWBA"); if (reg & ATH9K_INT_BMISS) - len += snprintf(buf + len, sizeof(buf) - len, " BMISS"); + len += scnprintf(buf + len, sizeof(buf) - len, " BMISS"); if (reg & ATH9K_INT_CST) - len += snprintf(buf + len, sizeof(buf) - len, " CST"); + len += scnprintf(buf + len, sizeof(buf) - len, " CST"); if (reg & ATH9K_INT_RX) - len += snprintf(buf + len, sizeof(buf) - len, " RX"); + len += scnprintf(buf + len, sizeof(buf) - len, " RX"); if (reg & ATH9K_INT_RXHP) - len += snprintf(buf + len, sizeof(buf) - len, " RXHP"); + len += scnprintf(buf + len, sizeof(buf) - len, " RXHP"); if (reg & ATH9K_INT_RXLP) - len += snprintf(buf + len, sizeof(buf) - len, " RXLP"); + len += scnprintf(buf + len, sizeof(buf) - len, " RXLP"); if (reg & ATH9K_INT_BB_WATCHDOG) - len += snprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG"); + len += scnprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG"); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += scnprintf(buf + len, sizeof(buf) - len, "\n"); ath9k_calculate_iter_data(hw, NULL, &iter_data); - len += snprintf(buf + len, sizeof(buf) - len, - "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" - " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.naps, iter_data.nstations, iter_data.nmeshes, - iter_data.nwds, iter_data.nadhocs, - sc->nvifs, sc->nbcnvifs); + len += scnprintf(buf + len, sizeof(buf) - len, + "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" + " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.naps, iter_data.nstations, iter_data.nmeshes, + iter_data.nwds, iter_data.nadhocs, + sc->nvifs, sc->nbcnvifs); if (len > sizeof(buf)) len = sizeof(buf); @@ -805,27 +849,33 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf, char buf[512]; unsigned int len = 0; - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Baseband Hang", - sc->debug.stats.reset[RESET_TYPE_BB_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Baseband Watchdog", - sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Fatal HW Error", - sc->debug.stats.reset[RESET_TYPE_FATAL_INT]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "TX HW error", - sc->debug.stats.reset[RESET_TYPE_TX_ERROR]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "TX Path Hang", - sc->debug.stats.reset[RESET_TYPE_TX_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "PLL RX Hang", - sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "MCI Reset", - sc->debug.stats.reset[RESET_TYPE_MCI]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Baseband Hang", + sc->debug.stats.reset[RESET_TYPE_BB_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Baseband Watchdog", + sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Fatal HW Error", + sc->debug.stats.reset[RESET_TYPE_FATAL_INT]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "TX HW error", + sc->debug.stats.reset[RESET_TYPE_TX_ERROR]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "TX Path Hang", + sc->debug.stats.reset[RESET_TYPE_TX_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "PLL RX Hang", + sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "MAC Hang", + sc->debug.stats.reset[RESET_TYPE_MAC_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Stuck Beacon", + sc->debug.stats.reset[RESET_TYPE_BEACON_STUCK]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "MCI Reset", + sc->debug.stats.reset[RESET_TYPE_MCI]); if (len > sizeof(buf)) len = sizeof(buf); @@ -898,406 +948,11 @@ static const struct file_operations fops_reset = { .llseek = default_llseek, }; -static ssize_t read_file_recv(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PHY_ERR(s, p) \ - len += snprintf(buf + len, size - len, "%22s : %10u\n", s, \ - sc->debug.stats.rxstats.phy_err_stats[p]); - -#define RXS_ERR(s, e) \ - do { \ - len += snprintf(buf + len, size - len, \ - "%22s : %10u\n", s, \ - sc->debug.stats.rxstats.e); \ - } while (0) - - struct ath_softc *sc = file->private_data; - char *buf; - unsigned int len = 0, size = 1600; - ssize_t retval = 0; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - RXS_ERR("CRC ERR", crc_err); - RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); - RXS_ERR("PHY ERR", phy_err); - RXS_ERR("MIC ERR", mic_err); - RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); - RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); - RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); - RXS_ERR("RX-LENGTH-ERR", rx_len_err); - RXS_ERR("RX-OOM-ERR", rx_oom_err); - RXS_ERR("RX-RATE-ERR", rx_rate_err); - RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); - - PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); - PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); - PHY_ERR("PARITY ERR", ATH9K_PHYERR_PARITY); - PHY_ERR("RATE ERR", ATH9K_PHYERR_RATE); - PHY_ERR("LENGTH ERR", ATH9K_PHYERR_LENGTH); - PHY_ERR("RADAR ERR", ATH9K_PHYERR_RADAR); - PHY_ERR("SERVICE ERR", ATH9K_PHYERR_SERVICE); - PHY_ERR("TOR ERR", ATH9K_PHYERR_TOR); - PHY_ERR("OFDM-TIMING ERR", ATH9K_PHYERR_OFDM_TIMING); - PHY_ERR("OFDM-SIGNAL-PARITY ERR", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); - PHY_ERR("OFDM-RATE ERR", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); - PHY_ERR("OFDM-LENGTH ERR", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); - PHY_ERR("OFDM-POWER-DROP ERR", ATH9K_PHYERR_OFDM_POWER_DROP); - PHY_ERR("OFDM-SERVICE ERR", ATH9K_PHYERR_OFDM_SERVICE); - PHY_ERR("OFDM-RESTART ERR", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT ERR", ATH9K_PHYERR_FALSE_RADAR_EXT); - PHY_ERR("CCK-TIMING ERR", ATH9K_PHYERR_CCK_TIMING); - PHY_ERR("CCK-HEADER-CRC ERR", ATH9K_PHYERR_CCK_HEADER_CRC); - PHY_ERR("CCK-RATE ERR", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE ERR", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART ERR", ATH9K_PHYERR_CCK_RESTART); - PHY_ERR("CCK-LENGTH ERR", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); - PHY_ERR("CCK-POWER-DROP ERR", ATH9K_PHYERR_CCK_POWER_DROP); - PHY_ERR("HT-CRC ERR", ATH9K_PHYERR_HT_CRC_ERROR); - PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); - PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); - - RXS_ERR("RX-Pkts-All", rx_pkts_all); - RXS_ERR("RX-Bytes-All", rx_bytes_all); - RXS_ERR("RX-Beacons", rx_beacons); - RXS_ERR("RX-Frags", rx_frags); - RXS_ERR("RX-Spectral", rx_spectral); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef RXS_ERR -#undef PHY_ERR -} - void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { -#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ - - RX_STAT_INC(rx_pkts_all); - sc->debug.stats.rxstats.rx_bytes_all += rs->rs_datalen; - - if (rs->rs_status & ATH9K_RXERR_CRC) - RX_STAT_INC(crc_err); - if (rs->rs_status & ATH9K_RXERR_DECRYPT) - RX_STAT_INC(decrypt_crc_err); - if (rs->rs_status & ATH9K_RXERR_MIC) - RX_STAT_INC(mic_err); - if (rs->rs_status & ATH9K_RX_DELIM_CRC_PRE) - RX_STAT_INC(pre_delim_crc_err); - if (rs->rs_status & ATH9K_RX_DELIM_CRC_POST) - RX_STAT_INC(post_delim_crc_err); - if (rs->rs_status & ATH9K_RX_DECRYPT_BUSY) - RX_STAT_INC(decrypt_busy_err); - - if (rs->rs_status & ATH9K_RXERR_PHY) { - RX_STAT_INC(phy_err); - if (rs->rs_phyerr < ATH9K_PHYERR_MAX) - RX_PHY_ERR_INC(rs->rs_phyerr); - } - -#undef RX_PHY_ERR_INC -} - -static const struct file_operations fops_recv = { - .read = read_file_recv, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char *mode = ""; - unsigned int len; - - switch (sc->spectral_mode) { - case SPECTRAL_DISABLED: - mode = "disable"; - break; - case SPECTRAL_BACKGROUND: - mode = "background"; - break; - case SPECTRAL_CHANSCAN: - mode = "chanscan"; - break; - case SPECTRAL_MANUAL: - mode = "manual"; - break; - } - len = strlen(mode); - return simple_read_from_buffer(user_buf, count, ppos, mode, len); -} - -static ssize_t write_file_spec_scan_ctl(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - - if (strncmp("trigger", buf, 7) == 0) { - ath9k_spectral_scan_trigger(sc->hw); - } else if (strncmp("background", buf, 9) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND); - ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); - } else if (strncmp("chanscan", buf, 8) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN); - ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); - } else if (strncmp("manual", buf, 6) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL); - ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); - } else if (strncmp("disable", buf, 7) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED); - ath_dbg(common, CONFIG, "spectral scan: disabled\n"); - } else { - return -EINVAL; - } - - return count; -} - -static const struct file_operations fops_spec_scan_ctl = { - .read = read_file_spec_scan_ctl, - .write = write_file_spec_scan_ctl, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_short_repeat(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.short_repeat); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); + ath9k_cmn_debug_stat_rx(&sc->debug.stats.rxstats, rs); } -static ssize_t write_file_spectral_short_repeat(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 1) - return -EINVAL; - - sc->spec_config.short_repeat = val; - return count; -} - -static const struct file_operations fops_spectral_short_repeat = { - .read = read_file_spectral_short_repeat, - .write = write_file_spectral_short_repeat, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_count(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.count); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_count(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 255) - return -EINVAL; - - sc->spec_config.count = val; - return count; -} - -static const struct file_operations fops_spectral_count = { - .read = read_file_spectral_count, - .write = write_file_spectral_count, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_period(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.period); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_period(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 255) - return -EINVAL; - - sc->spec_config.period = val; - return count; -} - -static const struct file_operations fops_spectral_period = { - .read = read_file_spectral_period, - .write = write_file_spectral_period, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_fft_period(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.fft_period); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_fft_period(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 15) - return -EINVAL; - - sc->spec_config.fft_period = val; - return count; -} - -static const struct file_operations fops_spectral_fft_period = { - .read = read_file_spectral_fft_period, - .write = write_file_spectral_fft_period, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static struct dentry *create_buf_file_handler(const char *filename, - struct dentry *parent, - umode_t mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *buf_file; - - buf_file = debugfs_create_file(filename, mode, parent, buf, - &relay_file_operations); - *is_global = 1; - return buf_file; -} - -static int remove_buf_file_handler(struct dentry *dentry) -{ - debugfs_remove(dentry); - - return 0; -} - -void ath_debug_send_fft_sample(struct ath_softc *sc, - struct fft_sample_tlv *fft_sample_tlv) -{ - int length; - if (!sc->rfs_chan_spec_scan) - return; - - length = __be16_to_cpu(fft_sample_tlv->length) + - sizeof(*fft_sample_tlv); - relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length); -} - -static struct rchan_callbacks rfs_spec_scan_cb = { - .create_buf_file = create_buf_file_handler, - .remove_buf_file = remove_buf_file_handler, -}; - - static ssize_t read_file_regidx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1439,22 +1094,22 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - len += snprintf(buf + len, size - len, - "Channel Noise Floor : %d\n", ah->noise); - len += snprintf(buf + len, size - len, - "Chain | privNF | # Readings | NF Readings\n"); + len += scnprintf(buf + len, size - len, + "Channel Noise Floor : %d\n", ah->noise); + len += scnprintf(buf + len, size - len, + "Chain | privNF | # Readings | NF Readings\n"); for (i = 0; i < NUM_NF_READINGS; i++) { if (!(chainmask & (1 << i)) || ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) continue; nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount; - len += snprintf(buf + len, size - len, " %d\t %d\t %d\t\t", - i, h[i].privNF, nread); + len += scnprintf(buf + len, size - len, " %d\t %d\t %d\t\t", + i, h[i].privNF, nread); for (j = 0; j < nread; j++) - len += snprintf(buf + len, size - len, - " %d", h[i].nfCalBuffer[j]); - len += snprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " %d", h[i].nfCalBuffer[j]); + len += scnprintf(buf + len, size - len, "\n"); } if (len > size) @@ -1473,62 +1128,6 @@ static const struct file_operations fops_dump_nfcal = { .llseek = default_llseek, }; -static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - u32 len = 0, size = 1500; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - len = ah->eep_ops->dump_eeprom(ah, true, buf, len, size); - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_base_eeprom = { - .read = read_file_base_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_hw *ah = sc->sc_ah; - u32 len = 0, size = 6000; - char *buf; - size_t retval; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len = ah->eep_ops->dump_eeprom(ah, false, buf, len, size); - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_modal_eeprom = { - .read = read_file_modal_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1543,8 +1142,8 @@ static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, return -ENOMEM; if (!sc->sc_ah->common.btcoex_enabled) { - len = snprintf(buf, size, "%s\n", - "BTCOEX is disabled"); + len = scnprintf(buf, size, "%s\n", + "BTCOEX is disabled"); goto exit; } @@ -1564,86 +1163,6 @@ static const struct file_operations fops_btcoex = { }; #endif -static ssize_t read_file_node_stat(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_node *an = file->private_data; - struct ath_softc *sc = an->sc; - struct ath_atx_tid *tid; - struct ath_atx_ac *ac; - struct ath_txq *txq; - u32 len = 0, size = 4096; - char *buf; - size_t retval; - int tidno, acno; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - if (!an->sta->ht_cap.ht_supported) { - len = snprintf(buf, size, "%s\n", - "HT not supported"); - goto exit; - } - - len = snprintf(buf, size, "Max-AMPDU: %d\n", - an->maxampdu); - len += snprintf(buf + len, size - len, "MPDU Density: %d\n\n", - an->mpdudensity); - - len += snprintf(buf + len, size - len, - "%2s%7s\n", "AC", "SCHED"); - - for (acno = 0, ac = &an->ac[acno]; - acno < IEEE80211_NUM_ACS; acno++, ac++) { - txq = ac->txq; - ath_txq_lock(sc, txq); - len += snprintf(buf + len, size - len, - "%2d%7d\n", - acno, ac->sched); - ath_txq_unlock(sc, txq); - } - - len += snprintf(buf + len, size - len, - "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", - "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", - "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); - - for (tidno = 0, tid = &an->tid[tidno]; - tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - txq = tid->ac->txq; - ath_txq_lock(sc, txq); - len += snprintf(buf + len, size - len, - "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", - tid->tidno, tid->seq_start, tid->seq_next, - tid->baw_size, tid->baw_head, tid->baw_tail, - tid->bar_index, tid->sched, tid->paused); - ath_txq_unlock(sc, txq); - } -exit: - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_node_stat = { - .read = read_file_node_stat, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct dentry *dir) -{ - struct ath_node *an = (struct ath_node *)sta->drv_priv; - debugfs_create_file("node_stat", S_IRUGO, dir, an, &fops_node_stat); -} - /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" @@ -1767,10 +1286,7 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw, void ath9k_deinit_debug(struct ath_softc *sc) { - if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) { - relay_close(sc->rfs_chan_spec_scan); - sc->rfs_chan_spec_scan = NULL; - } + ath9k_spectral_deinit_debug(sc); } int ath9k_init_debug(struct ath_hw *ah) @@ -1789,6 +1305,8 @@ int ath9k_init_debug(struct ath_hw *ah) #endif ath9k_dfs_init_debug(sc); + ath9k_tx99_init_debug(sc); + ath9k_spectral_init_debug(sc); debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dma); @@ -1810,8 +1328,10 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_misc); debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_reset); - debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_recv); + + ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); + ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); + debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy, &ah->rxchainmask); debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy, @@ -1831,27 +1351,10 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_regdump); debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dump_nfcal); - debugfs_create_file("base_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_base_eeprom); - debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_modal_eeprom); - sc->rfs_chan_spec_scan = relay_open("spectral_scan", - sc->debug.debugfs_phy, - 1024, 256, &rfs_spec_scan_cb, - NULL); - debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, - &fops_spec_scan_ctl); - debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, - &fops_spectral_short_repeat); - debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_spectral_count); - debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_spectral_period); - debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, - &fops_spectral_fft_period); + + ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah); + ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah); + debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, @@ -1864,5 +1367,6 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_btcoex); #endif + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 6e1556fa2f3..53ae15bd0c9 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -18,7 +18,6 @@ #define DEBUG_H #include "hw.h" -#include "rc.h" #include "dfs_debug.h" struct ath_txq; @@ -27,11 +26,13 @@ struct fft_sample_tlv; #ifdef CONFIG_ATH9K_DEBUGFS #define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].c++ +#define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) #define RESET_STAT_INC(sc, type) sc->debug.stats.reset[type]++ #define ANT_STAT_INC(i, c) sc->debug.stats.ant_stats[i].c++ #define ANT_LNA_INC(i, c) sc->debug.stats.ant_stats[i].lna_recv_cnt[c]++; #else #define TX_STAT_INC(q, c) do { } while (0) +#define RX_STAT_INC(c) #define RESET_STAT_INC(sc, type) do { } while (0) #define ANT_STAT_INC(i, c) do { } while (0) #define ANT_LNA_INC(i, c) do { } while (0) @@ -42,6 +43,7 @@ enum ath_reset_type { RESET_TYPE_BB_WATCHDOG, RESET_TYPE_FATAL_INT, RESET_TYPE_TX_ERROR, + RESET_TYPE_TX_GTT, RESET_TYPE_TX_HANG, RESET_TYPE_PLL_HANG, RESET_TYPE_MAC_HANG, @@ -193,58 +195,30 @@ struct ath_tx_stats { #define TXSTATS sc->debug.stats.txstats #define PR(str, elem) \ do { \ - len += snprintf(buf + len, size - len, \ - "%s%13u%11u%10u%10u\n", str, \ - TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem, \ - TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem, \ - TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem, \ - TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ + len += scnprintf(buf + len, size - len, \ + "%s%13u%11u%10u%10u\n", str, \ + TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem,\ + TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem,\ + TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem,\ + TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ } while(0) -#define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) +struct ath_rx_rate_stats { + struct { + u32 ht20_cnt; + u32 ht40_cnt; + u32 sgi_cnt; + u32 lgi_cnt; + } ht_stats[24]; -/** - * struct ath_rx_stats - RX Statistics - * @rx_pkts_all: No. of total frames received, including ones that - may have had errors. - * @rx_bytes_all: No. of total bytes received, including ones that - may have had errors. - * @crc_err: No. of frames with incorrect CRC value - * @decrypt_crc_err: No. of frames whose CRC check failed after - decryption process completed - * @phy_err: No. of frames whose reception failed because the PHY - encountered an error - * @mic_err: No. of frames with incorrect TKIP MIC verification failure - * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections - * @post_delim_crc_err: Post-Frame delimiter CRC error detections - * @decrypt_busy_err: Decryption interruptions counter - * @phy_err_stats: Individual PHY error statistics - * @rx_len_err: No. of frames discarded due to bad length. - * @rx_oom_err: No. of frames dropped due to OOM issues. - * @rx_rate_err: No. of frames dropped due to rate errors. - * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. - * @rx_beacons: No. of beacons received. - * @rx_frags: No. of rx-fragements received. - * @rx_spectral: No of spectral packets received. - */ -struct ath_rx_stats { - u32 rx_pkts_all; - u32 rx_bytes_all; - u32 crc_err; - u32 decrypt_crc_err; - u32 phy_err; - u32 mic_err; - u32 pre_delim_crc_err; - u32 post_delim_crc_err; - u32 decrypt_busy_err; - u32 phy_err_stats[ATH9K_PHYERR_MAX]; - u32 rx_len_err; - u32 rx_oom_err; - u32 rx_rate_err; - u32 rx_too_many_frags_err; - u32 rx_beacons; - u32 rx_frags; - u32 rx_spectral; + struct { + u32 ofdm_cnt; + } ofdm_stats[8]; + + struct { + u32 cck_lp_cnt; + u32 cck_sp_cnt; + } cck_stats[4]; }; #define ANT_MAIN 0 @@ -292,14 +266,12 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); -void ath_debug_send_fft_sample(struct ath_softc *sc, - struct fft_sample_tlv *fft_sample); void ath9k_debug_stat_ant(struct ath_softc *sc, struct ath_hw_antcomb_conf *div_ant_conf, int main_rssi_avg, int alt_rssi_avg); -#else +void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause); -#define RX_STAT_INC(c) /* NOP */ +#else static inline int ath9k_init_debug(struct ath_hw *ah) { @@ -331,6 +303,23 @@ static inline void ath9k_debug_stat_ant(struct ath_softc *sc, } +static inline void +ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause) +{ +} + #endif /* CONFIG_ATH9K_DEBUGFS */ +#ifdef CONFIG_ATH9K_STATION_STATISTICS +void ath_debug_rate_stats(struct ath_softc *sc, + struct ath_rx_status *rs, + struct sk_buff *skb); +#else +static inline void ath_debug_rate_stats(struct ath_softc *sc, + struct ath_rx_status *rs, + struct sk_buff *skb) +{ +} +#endif /* CONFIG_ATH9K_STATION_STATISTICS */ + #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wireless/ath/ath9k/debug_sta.c new file mode 100644 index 00000000000..ffca918ff16 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/debug_sta.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +/*************/ +/* node_aggr */ +/*************/ + +static ssize_t read_file_node_aggr(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_node *an = file->private_data; + struct ath_softc *sc = an->sc; + struct ath_atx_tid *tid; + struct ath_atx_ac *ac; + struct ath_txq *txq; + u32 len = 0, size = 4096; + char *buf; + size_t retval; + int tidno, acno; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + if (!an->sta->ht_cap.ht_supported) { + len = scnprintf(buf, size, "%s\n", + "HT not supported"); + goto exit; + } + + len = scnprintf(buf, size, "Max-AMPDU: %d\n", + an->maxampdu); + len += scnprintf(buf + len, size - len, "MPDU Density: %d\n\n", + an->mpdudensity); + + len += scnprintf(buf + len, size - len, + "%2s%7s\n", "AC", "SCHED"); + + for (acno = 0, ac = &an->ac[acno]; + acno < IEEE80211_NUM_ACS; acno++, ac++) { + txq = ac->txq; + ath_txq_lock(sc, txq); + len += scnprintf(buf + len, size - len, + "%2d%7d\n", + acno, ac->sched); + ath_txq_unlock(sc, txq); + } + + len += scnprintf(buf + len, size - len, + "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", + "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", + "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); + + for (tidno = 0, tid = &an->tid[tidno]; + tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { + txq = tid->ac->txq; + ath_txq_lock(sc, txq); + if (tid->active) { + len += scnprintf(buf + len, size - len, + "%3d%11d%10d%10d%10d%10d%9d%6d\n", + tid->tidno, + tid->seq_start, + tid->seq_next, + tid->baw_size, + tid->baw_head, + tid->baw_tail, + tid->bar_index, + tid->sched); + } + ath_txq_unlock(sc, txq); + } +exit: + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static const struct file_operations fops_node_aggr = { + .read = read_file_node_aggr, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*************/ +/* node_recv */ +/*************/ + +void ath_debug_rate_stats(struct ath_softc *sc, + struct ath_rx_status *rs, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_rx_status *rxs; + struct ath_rx_rate_stats *rstats; + struct ieee80211_sta *sta; + struct ath_node *an; + + if (!ieee80211_is_data(hdr->frame_control)) + return; + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(sc->hw, hdr->addr2, NULL); + if (!sta) + goto exit; + + an = (struct ath_node *) sta->drv_priv; + rstats = &an->rx_rate_stats; + rxs = IEEE80211_SKB_RXCB(skb); + + if (IS_HT_RATE(rs->rs_rate)) { + if (rxs->rate_idx >= ARRAY_SIZE(rstats->ht_stats)) + goto exit; + + if (rxs->flag & RX_FLAG_40MHZ) + rstats->ht_stats[rxs->rate_idx].ht40_cnt++; + else + rstats->ht_stats[rxs->rate_idx].ht20_cnt++; + + if (rxs->flag & RX_FLAG_SHORT_GI) + rstats->ht_stats[rxs->rate_idx].sgi_cnt++; + else + rstats->ht_stats[rxs->rate_idx].lgi_cnt++; + + goto exit; + } + + if (IS_CCK_RATE(rs->rs_rate)) { + if (rxs->flag & RX_FLAG_SHORTPRE) + rstats->cck_stats[rxs->rate_idx].cck_sp_cnt++; + else + rstats->cck_stats[rxs->rate_idx].cck_lp_cnt++; + + goto exit; + } + + if (IS_OFDM_RATE(rs->rs_rate)) { + if (ah->curchan->chan->band == IEEE80211_BAND_2GHZ) + rstats->ofdm_stats[rxs->rate_idx - 4].ofdm_cnt++; + else + rstats->ofdm_stats[rxs->rate_idx].ofdm_cnt++; + } +exit: + rcu_read_unlock(); +} + +#define PRINT_CCK_RATE(str, i, sp) \ + do { \ + len += scnprintf(buf + len, size - len, \ + "%11s : %10u\n", \ + str, \ + (sp) ? rstats->cck_stats[i].cck_sp_cnt : \ + rstats->cck_stats[i].cck_lp_cnt); \ + } while (0) + +#define PRINT_OFDM_RATE(str, i) \ + do { \ + len += scnprintf(buf + len, size - len, \ + "%11s : %10u\n", \ + str, \ + rstats->ofdm_stats[i].ofdm_cnt); \ + } while (0) + +static ssize_t read_file_node_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_node *an = file->private_data; + struct ath_softc *sc = an->sc; + struct ath_hw *ah = sc->sc_ah; + struct ath_rx_rate_stats *rstats; + struct ieee80211_sta *sta = an->sta; + enum ieee80211_band band; + u32 len = 0, size = 4096; + char *buf; + size_t retval; + int i; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + band = ah->curchan->chan->band; + rstats = &an->rx_rate_stats; + + if (!sta->ht_cap.ht_supported) + goto legacy; + + len += scnprintf(buf + len, size - len, + "%24s%10s%10s%10s\n", + "HT20", "HT40", "SGI", "LGI"); + + for (i = 0; i < 24; i++) { + len += scnprintf(buf + len, size - len, + "%8s%3u : %10u%10u%10u%10u\n", + "MCS", i, + rstats->ht_stats[i].ht20_cnt, + rstats->ht_stats[i].ht40_cnt, + rstats->ht_stats[i].sgi_cnt, + rstats->ht_stats[i].lgi_cnt); + } + + len += scnprintf(buf + len, size - len, "\n"); + +legacy: + if (band == IEEE80211_BAND_2GHZ) { + PRINT_CCK_RATE("CCK-1M/LP", 0, false); + PRINT_CCK_RATE("CCK-2M/LP", 1, false); + PRINT_CCK_RATE("CCK-5.5M/LP", 2, false); + PRINT_CCK_RATE("CCK-11M/LP", 3, false); + + PRINT_CCK_RATE("CCK-2M/SP", 1, true); + PRINT_CCK_RATE("CCK-5.5M/SP", 2, true); + PRINT_CCK_RATE("CCK-11M/SP", 3, true); + } + + PRINT_OFDM_RATE("OFDM-6M", 0); + PRINT_OFDM_RATE("OFDM-9M", 1); + PRINT_OFDM_RATE("OFDM-12M", 2); + PRINT_OFDM_RATE("OFDM-18M", 3); + PRINT_OFDM_RATE("OFDM-24M", 4); + PRINT_OFDM_RATE("OFDM-36M", 5); + PRINT_OFDM_RATE("OFDM-48M", 6); + PRINT_OFDM_RATE("OFDM-54M", 7); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +#undef PRINT_OFDM_RATE +#undef PRINT_CCK_RATE + +static const struct file_operations fops_node_recv = { + .read = read_file_node_recv, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir) +{ + struct ath_node *an = (struct ath_node *)sta->drv_priv; + + debugfs_create_file("node_aggr", S_IRUGO, dir, an, &fops_node_aggr); + debugfs_create_file("node_recv", S_IRUGO, dir, an, &fops_node_recv); +} diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c index 7187d367151..726271c7c33 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.c +++ b/drivers/net/wireless/ath/ath9k/dfs.c @@ -158,8 +158,8 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, return; } - ard.rssi = rs->rs_rssi_ctl0; - ard.ext_rssi = rs->rs_rssi_ext0; + ard.rssi = rs->rs_rssi_ctl[0]; + ard.ext_rssi = rs->rs_rssi_ext[0]; /* * hardware stores this as 8 bit signed value. @@ -178,12 +178,12 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, pe.ts = mactime; if (ath9k_postprocess_radar_event(sc, &ard, &pe)) { struct dfs_pattern_detector *pd = sc->dfs_detector; - static u64 last_ts; ath_dbg(common, DFS, "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " "width=%d, rssi=%d, delta_ts=%llu\n", - pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts); - last_ts = pe.ts; + pe.freq, pe.ts, pe.width, pe.rssi, + pe.ts - sc->dfs_prev_pulse_ts); + sc->dfs_prev_pulse_ts = pe.ts; DFS_STAT_INC(sc, pulses_processed); if (pd != NULL && pd->add_pulse(pd, &pe)) { DFS_STAT_INC(sc, radar_detected); diff --git a/drivers/net/wireless/ath/ath9k/dfs.h b/drivers/net/wireless/ath/ath9k/dfs.h index 3c839f06a06..c6fa3d5b5d7 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.h +++ b/drivers/net/wireless/ath/ath9k/dfs.h @@ -17,7 +17,7 @@ #ifndef ATH9K_DFS_H #define ATH9K_DFS_H -#include "dfs_pattern_detector.h" +#include "../dfs_pattern_detector.h" #if defined(CONFIG_ATH9K_DFS_CERTIFIED) /** diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 3c6e4138a95..8824610c21f 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -20,16 +20,16 @@ #include "ath9k.h" #include "dfs_debug.h" +#include "../dfs_pattern_detector.h" - -struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 }; +static struct ath_dfs_pool_stats dfs_pool_stats = { 0 }; #define ATH9K_DFS_STAT(s, p) \ - len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ - sc->debug.stats.dfs_stats.p); + len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ + sc->debug.stats.dfs_stats.p); #define ATH9K_DFS_POOL_STAT(s, p) \ - len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ - global_dfs_pool_stats.p); + len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ + dfs_pool_stats.p); static ssize_t read_file_dfs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -44,12 +44,21 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, "DFS support for " - "macVersion = 0x%x, macRev = 0x%x: %s\n", - hw_ver->macVersion, hw_ver->macRev, - (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ? + len += scnprintf(buf + len, size - len, "DFS support for " + "macVersion = 0x%x, macRev = 0x%x: %s\n", + hw_ver->macVersion, hw_ver->macRev, + (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ? "enabled" : "disabled"); - len += snprintf(buf + len, size - len, "Pulse detector statistics:\n"); + + if (!sc->dfs_detector) { + len += scnprintf(buf + len, size - len, + "DFS detector not enabled\n"); + goto exit; + } + + dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector); + + len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n"); ATH9K_DFS_STAT("pulse events reported ", pulses_total); ATH9K_DFS_STAT("invalid pulse events ", pulses_no_dfs); ATH9K_DFS_STAT("DFS pulses detected ", pulses_detected); @@ -59,11 +68,12 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, ATH9K_DFS_STAT("Primary channel pulses ", pri_phy_errors); ATH9K_DFS_STAT("Secondary channel pulses", ext_phy_errors); ATH9K_DFS_STAT("Dual channel pulses ", dc_phy_errors); - len += snprintf(buf + len, size - len, "Radar detector statistics " - "(current DFS region: %d)\n", sc->dfs_detector->region); + len += scnprintf(buf + len, size - len, "Radar detector statistics " + "(current DFS region: %d)\n", + sc->dfs_detector->region); ATH9K_DFS_STAT("Pulse events processed ", pulses_processed); ATH9K_DFS_STAT("Radars detected ", radar_detected); - len += snprintf(buf + len, size - len, "Global Pool statistics:\n"); + len += scnprintf(buf + len, size - len, "Global Pool statistics:\n"); ATH9K_DFS_POOL_STAT("Pool references ", pool_reference); ATH9K_DFS_POOL_STAT("Pulses allocated ", pulse_allocated); ATH9K_DFS_POOL_STAT("Pulses alloc error ", pulse_alloc_error); @@ -72,6 +82,7 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, ATH9K_DFS_POOL_STAT("Seqs. alloc error ", pseq_alloc_error); ATH9K_DFS_POOL_STAT("Seqs. in use ", pseq_used); +exit: if (len > size) len = size; diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.h b/drivers/net/wireless/ath/ath9k/dfs_debug.h index e36810a4b58..7936c9126a2 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.h +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.h @@ -21,6 +21,8 @@ #include "hw.h" +struct ath_softc; + /** * struct ath_dfs_stats - DFS Statistics per wiphy * @pulses_total: pulses reported by HW @@ -51,25 +53,11 @@ struct ath_dfs_stats { u32 radar_detected; }; -/** - * struct ath_dfs_pool_stats - DFS Statistics for global pools - */ -struct ath_dfs_pool_stats { - u32 pool_reference; - u32 pulse_allocated; - u32 pulse_alloc_error; - u32 pulse_used; - u32 pseq_allocated; - u32 pseq_alloc_error; - u32 pseq_used; -}; #if defined(CONFIG_ATH9K_DFS_DEBUGFS) #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++) void ath9k_dfs_init_debug(struct ath_softc *sc); -#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) -#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) extern struct ath_dfs_pool_stats global_dfs_pool_stats; #else @@ -77,8 +65,6 @@ extern struct ath_dfs_pool_stats global_dfs_pool_stats; #define DFS_STAT_INC(sc, c) do { } while (0) static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { } -#define DFS_POOL_STAT_INC(c) do { } while (0) -#define DFS_POOL_STAT_DEC(c) do { } while (0) #endif /* CONFIG_ATH9K_DFS_DEBUGFS */ #endif /* ATH9K_DFS_DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 9ea8e4b779c..07b806c56c5 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -129,10 +129,10 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct base_eep_header_4k *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ath9k_dump_4k_modal_eeprom(buf, len, size, - &eep->modalHeader); + &eep->modalHeader); goto out; } @@ -160,8 +160,8 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("TX Gain type", pBase->txGainType); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); out: if (len > size) @@ -1085,31 +1085,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { -#define EEP_MAP4K_SPURCHAN \ - (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) - struct ath_common *common = ath9k_hw_common(ah); - - u16 spur_val = AR_NO_SPUR; - - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); - break; - case SPUR_ENABLE_EEPROM: - spur_val = EEP_MAP4K_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_MAP4K_SPURCHAN + return ah->eeprom.map4k.modalHeader.spurChans[i].spurChan; } const struct eeprom_ops eep_4k_ops = { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index 3ae1f3df063..5ba1385c983 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -125,8 +125,8 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct base_eep_ar9287_header *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ar9287_dump_modal_eeprom(buf, len, size, &eep->modalHeader); goto out; @@ -157,8 +157,8 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Power Table Offset", pBase->pwrTableOffset); PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); out: if (len > size) @@ -1004,31 +1004,7 @@ static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah, static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { -#define EEP_MAP9287_SPURCHAN \ - (ah->eeprom.map9287.modalHeader.spurChans[i].spurChan) - - struct ath_common *common = ath9k_hw_common(ah); - u16 spur_val = AR_NO_SPUR; - - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); - break; - case SPUR_ENABLE_EEPROM: - spur_val = EEP_MAP9287_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_MAP9287_SPURCHAN + return ah->eeprom.map9287.modalHeader.spurChans[i].spurChan; } const struct eeprom_ops eep_ar9287_ops = { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 1c25368b383..3218ca99474 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -205,12 +205,12 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct base_eep_header *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[0]); - len += snprintf(buf + len, size - len, - "%20s :\n", "5GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "5GHz modal Header"); len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[1]); goto out; @@ -240,8 +240,8 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); out: if (len > size) @@ -1348,31 +1348,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { -#define EEP_DEF_SPURCHAN \ - (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) - struct ath_common *common = ath9k_hw_common(ah); - - u16 spur_val = AR_NO_SPUR; - - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); - break; - case SPUR_ENABLE_EEPROM: - spur_val = EEP_DEF_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_DEF_SPURCHAN + return ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan; } const struct eeprom_ops eep_def_ops = { diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 4b412aaf4f3..b1956bf6e01 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -157,36 +157,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc) } } -static void ath9k_gen_timer_start(struct ath_hw *ah, - struct ath_gen_timer *timer, - u32 trig_timeout, - u32 timer_period) -{ - ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period); - - if ((ah->imask & ATH9K_INT_GENTIMER) == 0) { - ath9k_hw_disable_interrupts(ah); - ah->imask |= ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); - } -} - -static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) -{ - struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - - ath9k_hw_gen_timer_stop(ah, timer); - - /* if no timer is enabled, turn off interrupt mask */ - if (timer_table->timer_mask.val == 0) { - ath9k_hw_disable_interrupts(ah); - ah->imask &= ~ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); - } -} - static void ath_mci_ftp_adjust(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; @@ -257,19 +227,9 @@ static void ath_btcoex_period_timer(unsigned long data) spin_unlock_bh(&btcoex->btcoex_lock); - /* - * btcoex_period is in msec while (btocex/btscan_)no_stomp are in usec, - * ensure that we properly convert btcoex_period to usec - * for any comparision with (btcoex/btscan_)no_stomp. - */ - if (btcoex->btcoex_period * 1000 != btcoex->btcoex_no_stomp) { - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period, - timer_period * 10); - btcoex->hw_timer_enabled = true; - } + if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) + mod_timer(&btcoex->no_stomp_timer, + jiffies + msecs_to_jiffies(timer_period)); ath9k_ps_restore(sc); @@ -282,7 +242,7 @@ skip_hw_wakeup: * Generic tsf based hw timer which configures weight * registers to time slice between wlan and bt traffic */ -static void ath_btcoex_no_stomp_timer(void *arg) +static void ath_btcoex_no_stomp_timer(unsigned long arg) { struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; @@ -311,24 +271,18 @@ static int ath_init_btcoex_timer(struct ath_softc *sc) struct ath_btcoex *btcoex = &sc->btcoex; btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; - btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 1000 * + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * btcoex->btcoex_period / 100; - btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * 1000 * + btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * btcoex->btcoex_period / 100; setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, (unsigned long) sc); + setup_timer(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer, + (unsigned long) sc); spin_lock_init(&btcoex->btcoex_lock); - btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, - ath_btcoex_no_stomp_timer, - ath_btcoex_no_stomp_timer, - (void *) sc, AR_FIRST_NDP_TIMER); - - if (!btcoex->no_stomp_timer) - return -ENOMEM; - return 0; } @@ -343,10 +297,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ - if (btcoex->hw_timer_enabled) { - ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); - btcoex->hw_timer_enabled = false; - } + del_timer_sync(&btcoex->no_stomp_timer); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; @@ -363,24 +314,16 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) void ath9k_btcoex_timer_pause(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; del_timer_sync(&btcoex->period_timer); - - if (btcoex->hw_timer_enabled) { - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - btcoex->hw_timer_enabled = false; - } + del_timer_sync(&btcoex->no_stomp_timer); } void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - if (btcoex->hw_timer_enabled) { - ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); - btcoex->hw_timer_enabled = false; - } + del_timer_sync(&btcoex->no_stomp_timer); } u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) @@ -400,12 +343,6 @@ u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status) { - struct ath_hw *ah = sc->sc_ah; - - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) - if (status & ATH9K_INT_GENTIMER) - ath_gen_timer_isr(sc->sc_ah); - if (status & ATH9K_INT_MCI) ath_mci_intr(sc); } @@ -447,10 +384,6 @@ void ath9k_deinit_btcoex(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - if ((sc->btcoex.no_stomp_timer) && - ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE) - ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); - if (ath9k_hw_mci_is_enabled(ah)) ath_mci_cleanup(sc); } @@ -522,22 +455,22 @@ static int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size) ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx); ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count); - len += snprintf(buf + len, size - len, "BT Weights: "); + len += scnprintf(buf + len, size - len, "BT Weights: "); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) - len += snprintf(buf + len, size - len, "%08x ", - btcoex_hw->bt_weight[i]); - len += snprintf(buf + len, size - len, "\n"); - len += snprintf(buf + len, size - len, "WLAN Weights: "); + len += scnprintf(buf + len, size - len, "%08x ", + btcoex_hw->bt_weight[i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, "WLAN Weights: "); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) - len += snprintf(buf + len, size - len, "%08x ", - btcoex_hw->wlan_weight[i]); - len += snprintf(buf + len, size - len, "\n"); - len += snprintf(buf + len, size - len, "Tx Priorities: "); + len += scnprintf(buf + len, size - len, "%08x ", + btcoex_hw->wlan_weight[i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, "Tx Priorities: "); for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) - len += snprintf(buf + len, size - len, "%08x ", + len += scnprintf(buf + len, size - len, "%08x ", btcoex_hw->tx_prio[i]); - len += snprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, "\n"); return len; } diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 6d5d716adc1..8e7153b186e 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -54,6 +54,8 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { .driver_info = AR9280_USB }, /* SMC Networks */ { USB_DEVICE(0x0411, 0x017f), .driver_info = AR9280_USB }, /* Sony UWA-BR100 */ + { USB_DEVICE(0x0411, 0x0197), + .driver_info = AR9280_USB }, /* Buffalo WLI-UV-AG300P */ { USB_DEVICE(0x04da, 0x3904), .driver_info = AR9280_USB }, diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 055d7c25e09..09a5d72f3ff 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)) @@ -262,6 +261,8 @@ enum tid_aggr_state { struct ath9k_htc_sta { u8 index; enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID]; + struct work_struct rc_update_work; + struct ath9k_htc_priv *htc_priv; }; #define ATH9K_HTC_RXBUF 256 @@ -275,7 +276,6 @@ struct ath9k_htc_rxbuf { }; struct ath9k_htc_rx { - int last_rssi; /* FIXME: per-STA */ struct list_head rxbuf; spinlock_t rxbuflock; }; @@ -325,14 +325,14 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb) #define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) #define TX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c += a) -#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++) -#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c += a) +#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c++) +#define RX_STAT_ADD(c, a) (hif_dev->htc_handle->drv_priv->debug.skbrx_stats.c += a) #define CAB_STAT_INC priv->debug.tx_stats.cab_queued++ #define TX_QSTAT_INC(q) (priv->debug.tx_stats.queue_stats[q]++) void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, - struct ath_htc_rx_status *rxs); + struct ath_rx_status *rs); struct ath_tx_stats { u32 buf_queued; @@ -345,25 +345,18 @@ struct ath_tx_stats { u32 queue_stats[IEEE80211_NUM_ACS]; }; -struct ath_rx_stats { +struct ath_skbrx_stats { u32 skb_allocated; u32 skb_completed; u32 skb_completed_bytes; u32 skb_dropped; - u32 err_crc; - u32 err_decrypt_crc; - u32 err_mic; - u32 err_pre_delim; - u32 err_post_delim; - u32 err_decrypt_busy; - u32 err_phy; - u32 err_phy_stats[ATH9K_PHYERR_MAX]; }; struct ath9k_debug { struct dentry *debugfs_phy; struct ath_tx_stats tx_stats; struct ath_rx_stats rx_stats; + struct ath_skbrx_stats skbrx_stats; }; void ath9k_htc_get_et_strings(struct ieee80211_hw *hw, @@ -385,7 +378,7 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw, #define TX_QSTAT_INC(c) do { } while (0) static inline void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, - struct ath_htc_rx_status *rxs) + struct ath_rx_status *rs) { } @@ -405,12 +398,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 { @@ -438,12 +437,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 { @@ -486,10 +481,10 @@ struct ath9k_htc_priv { unsigned long op_flags; struct ath9k_hw_cal_data caldata; - struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; 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 +509,6 @@ struct ath9k_htc_priv { struct work_struct led_work; #endif - int beaconq; int cabq; int hwq_map[IEEE80211_NUM_ACS]; @@ -600,10 +594,15 @@ void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw); struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv); #ifdef CONFIG_MAC80211_LEDS +void ath9k_configure_leds(struct ath9k_htc_priv *priv); void ath9k_init_leds(struct ath9k_htc_priv *priv); void ath9k_deinit_leds(struct ath9k_htc_priv *priv); void ath9k_led_work(struct work_struct *work); #else +static inline void ath9k_configure_leds(struct ath9k_htc_priv *priv) +{ +} + static inline void ath9k_init_leds(struct ath9k_htc_priv *priv) { } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index e0c03bd6418..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,235 +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, sleepduration; - int cfpperiod, cfpcount, bmiss_timeout; - u32 nexttbtt = 0, intval, tsftu; - __be32 htc_imask = 0; - u64 tsf; - int num_beacons, offset, dtim_dec_count, cfp_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 and cfp 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; - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; - - sleepduration = intval; - if (sleepduration <= 0) - sleepduration = intval; - - /* - * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim+cfp 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; - /* CFP every cfpperiod DTIM Beacon */ - cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; - if (dtim_dec_count) - cfp_dec_count++; - - dtimcount -= dtim_dec_count; - if (dtimcount < 0) - dtimcount += dtimperiod; - - cfpcount -= cfp_dec_count; - if (cfpcount < 0) - cfpcount += cfpperiod; - - bs.bs_intval = intval; - bs.bs_nexttbtt = nexttbtt; - bs.bs_dtimperiod = dtimperiod*intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; - bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; - bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; - bs.bs_cfpmaxduration = 0; - - /* - * 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. - */ - if (sleepduration > intval) { - bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2; - } else { - 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 = roundup(IEEE80211_MS_TO_TU(100), sleepduration); - 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 cfp-period: %u maxdur: %u next: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration, - bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); - - /* 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, @@ -302,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); @@ -363,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; } @@ -446,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); @@ -455,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; } @@ -483,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", @@ -503,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", @@ -519,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) @@ -551,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; @@ -606,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; @@ -642,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_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index c1b45e2f848..8b529e4b8ac 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -37,29 +37,29 @@ static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, ath9k_htc_ps_restore(priv); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "RX", - be32_to_cpu(cmd_rsp.rx)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "RX", + be32_to_cpu(cmd_rsp.rx)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "RXORN", - be32_to_cpu(cmd_rsp.rxorn)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "RXORN", + be32_to_cpu(cmd_rsp.rxorn)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "RXEOL", - be32_to_cpu(cmd_rsp.rxeol)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "RXEOL", + be32_to_cpu(cmd_rsp.rxeol)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "TXURN", - be32_to_cpu(cmd_rsp.txurn)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "TXURN", + be32_to_cpu(cmd_rsp.txurn)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "TXTO", - be32_to_cpu(cmd_rsp.txto)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "TXTO", + be32_to_cpu(cmd_rsp.txto)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "CST", - be32_to_cpu(cmd_rsp.cst)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "CST", + be32_to_cpu(cmd_rsp.cst)); if (len > sizeof(buf)) len = sizeof(buf); @@ -95,41 +95,41 @@ static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf, ath9k_htc_ps_restore(priv); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Xretries", - be32_to_cpu(cmd_rsp.xretries)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Xretries", + be32_to_cpu(cmd_rsp.xretries)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "FifoErr", - be32_to_cpu(cmd_rsp.fifoerr)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "FifoErr", + be32_to_cpu(cmd_rsp.fifoerr)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Filtered", - be32_to_cpu(cmd_rsp.filtered)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Filtered", + be32_to_cpu(cmd_rsp.filtered)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "TimerExp", - be32_to_cpu(cmd_rsp.timer_exp)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "TimerExp", + be32_to_cpu(cmd_rsp.timer_exp)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "ShortRetries", - be32_to_cpu(cmd_rsp.shortretries)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "ShortRetries", + be32_to_cpu(cmd_rsp.shortretries)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "LongRetries", - be32_to_cpu(cmd_rsp.longretries)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "LongRetries", + be32_to_cpu(cmd_rsp.longretries)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "QueueNull", - be32_to_cpu(cmd_rsp.qnull)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "QueueNull", + be32_to_cpu(cmd_rsp.qnull)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "EncapFail", - be32_to_cpu(cmd_rsp.encap_fail)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "EncapFail", + be32_to_cpu(cmd_rsp.encap_fail)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "NoBuf", - be32_to_cpu(cmd_rsp.nobuf)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "NoBuf", + be32_to_cpu(cmd_rsp.nobuf)); if (len > sizeof(buf)) len = sizeof(buf); @@ -165,17 +165,17 @@ static ssize_t read_file_tgt_rx_stats(struct file *file, char __user *user_buf, ath9k_htc_ps_restore(priv); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "NoBuf", - be32_to_cpu(cmd_rsp.nobuf)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "NoBuf", + be32_to_cpu(cmd_rsp.nobuf)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "HostSend", - be32_to_cpu(cmd_rsp.host_send)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "HostSend", + be32_to_cpu(cmd_rsp.host_send)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "HostDone", - be32_to_cpu(cmd_rsp.host_done)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "HostDone", + be32_to_cpu(cmd_rsp.host_done)); if (len > sizeof(buf)) len = sizeof(buf); @@ -197,37 +197,37 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, char buf[512]; unsigned int len = 0; - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Buffers queued", - priv->debug.tx_stats.buf_queued); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Buffers completed", - priv->debug.tx_stats.buf_completed); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "SKBs queued", - priv->debug.tx_stats.skb_queued); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "SKBs success", - priv->debug.tx_stats.skb_success); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "SKBs failed", - priv->debug.tx_stats.skb_failed); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "CAB queued", - priv->debug.tx_stats.cab_queued); - - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "BE queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "BK queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "VI queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "VO queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers queued", + priv->debug.tx_stats.buf_queued); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers completed", + priv->debug.tx_stats.buf_completed); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs queued", + priv->debug.tx_stats.skb_queued); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs success", + priv->debug.tx_stats.skb_success); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs failed", + priv->debug.tx_stats.skb_failed); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "CAB queued", + priv->debug.tx_stats.cab_queued); + + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "BE queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "BK queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "VI queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "VO queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]); if (len > sizeof(buf)) len = sizeof(buf); @@ -243,39 +243,14 @@ static const struct file_operations fops_xmit = { }; void ath9k_htc_err_stat_rx(struct ath9k_htc_priv *priv, - struct ath_htc_rx_status *rxs) + struct ath_rx_status *rs) { -#define RX_PHY_ERR_INC(c) priv->debug.rx_stats.err_phy_stats[c]++ - - if (rxs->rs_status & ATH9K_RXERR_CRC) - priv->debug.rx_stats.err_crc++; - if (rxs->rs_status & ATH9K_RXERR_DECRYPT) - priv->debug.rx_stats.err_decrypt_crc++; - if (rxs->rs_status & ATH9K_RXERR_MIC) - priv->debug.rx_stats.err_mic++; - if (rxs->rs_status & ATH9K_RX_DELIM_CRC_PRE) - priv->debug.rx_stats.err_pre_delim++; - if (rxs->rs_status & ATH9K_RX_DELIM_CRC_POST) - priv->debug.rx_stats.err_post_delim++; - if (rxs->rs_status & ATH9K_RX_DECRYPT_BUSY) - priv->debug.rx_stats.err_decrypt_busy++; - - if (rxs->rs_status & ATH9K_RXERR_PHY) { - priv->debug.rx_stats.err_phy++; - if (rxs->rs_phyerr < ATH9K_PHYERR_MAX) - RX_PHY_ERR_INC(rxs->rs_phyerr); - } - -#undef RX_PHY_ERR_INC + ath9k_cmn_debug_stat_rx(&priv->debug.rx_stats, rs); } -static ssize_t read_file_recv(struct file *file, char __user *user_buf, +static ssize_t read_file_skb_rx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { -#define PHY_ERR(s, p) \ - len += snprintf(buf + len, size - len, "%20s : %10u\n", s, \ - priv->debug.rx_stats.err_phy_stats[p]); - struct ath9k_htc_priv *priv = file->private_data; char *buf; unsigned int len = 0, size = 1500; @@ -285,65 +260,15 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "SKBs allocated", - priv->debug.rx_stats.skb_allocated); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "SKBs completed", - priv->debug.rx_stats.skb_completed); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "SKBs Dropped", - priv->debug.rx_stats.skb_dropped); - - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "CRC ERR", - priv->debug.rx_stats.err_crc); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "DECRYPT CRC ERR", - priv->debug.rx_stats.err_decrypt_crc); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "MIC ERR", - priv->debug.rx_stats.err_mic); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "PRE-DELIM CRC ERR", - priv->debug.rx_stats.err_pre_delim); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "POST-DELIM CRC ERR", - priv->debug.rx_stats.err_post_delim); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "DECRYPT BUSY ERR", - priv->debug.rx_stats.err_decrypt_busy); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "TOTAL PHY ERR", - priv->debug.rx_stats.err_phy); - - - PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); - PHY_ERR("TIMING", ATH9K_PHYERR_TIMING); - PHY_ERR("PARITY", ATH9K_PHYERR_PARITY); - PHY_ERR("RATE", ATH9K_PHYERR_RATE); - PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH); - PHY_ERR("RADAR", ATH9K_PHYERR_RADAR); - PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE); - PHY_ERR("TOR", ATH9K_PHYERR_TOR); - PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING); - PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); - PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); - PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); - PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP); - PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE); - PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART); - PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT); - PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING); - PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC); - PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL); - PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE); - PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART); - PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); - PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP); - PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR); - PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); - PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "SKBs allocated", + priv->debug.skbrx_stats.skb_allocated); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "SKBs completed", + priv->debug.skbrx_stats.skb_completed); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "SKBs Dropped", + priv->debug.skbrx_stats.skb_dropped); if (len > size) len = size; @@ -352,12 +277,10 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, kfree(buf); return retval; - -#undef PHY_ERR } -static const struct file_operations fops_recv = { - .read = read_file_recv, +static const struct file_operations fops_skb_rx = { + .read = read_file_skb_rx, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -372,16 +295,16 @@ static ssize_t read_file_slot(struct file *file, char __user *user_buf, spin_lock_bh(&priv->tx.tx_lock); - len += snprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : "); + len += scnprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : "); len += bitmap_scnprintf(buf + len, sizeof(buf) - len, priv->tx.tx_slot, MAX_TX_BUF_NUM); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += scnprintf(buf + len, sizeof(buf) - len, "\n"); - len += snprintf(buf + len, sizeof(buf) - len, - "Used slots : %d\n", - bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM)); + len += scnprintf(buf + len, sizeof(buf) - len, + "Used slots : %d\n", + bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM)); spin_unlock_bh(&priv->tx.tx_lock); @@ -405,30 +328,30 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, char buf[512]; unsigned int len = 0; - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Failed queue", skb_queue_len(&priv->tx.tx_failed)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Failed queue", skb_queue_len(&priv->tx.tx_failed)); spin_lock_bh(&priv->tx.tx_lock); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Queued count", priv->tx.queued_cnt); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Queued count", priv->tx.queued_cnt); spin_unlock_bh(&priv->tx.tx_lock); if (len > sizeof(buf)) @@ -486,423 +409,6 @@ static const struct file_operations fops_debug = { .llseek = default_llseek, }; -static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath9k_htc_priv *priv = file->private_data; - struct ath_common *common = ath9k_hw_common(priv->ah); - struct base_eep_header *pBase = NULL; - unsigned int len = 0, size = 1500; - ssize_t retval = 0; - char *buf; - - pBase = ath9k_htc_get_eeprom_base(priv); - - if (pBase == NULL) { - ath_err(common, "Unknown EEPROM type\n"); - return 0; - } - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Major Version", - pBase->version >> 12); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Minor Version", - pBase->version & 0xFFF); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Checksum", - pBase->checksum); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Length", - pBase->length); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "RegDomain1", - pBase->regDmn[0]); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "RegDomain2", - pBase->regDmn[1]); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "TX Mask", pBase->txMask); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "RX Mask", pBase->rxMask); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Allow 5GHz", - !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Allow 2GHz", - !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 2GHz HT20", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 2GHz HT40", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 5Ghz HT20", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 5Ghz HT40", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Big Endian", - !!(pBase->eepMisc & 0x01)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Major Ver", - (pBase->binBuildNumber >> 24) & 0xFF); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Minor Ver", - (pBase->binBuildNumber >> 16) & 0xFF); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Build", - (pBase->binBuildNumber >> 8) & 0xFF); - - /* - * UB91 specific data. - */ - if (AR_SREV_9271(priv->ah)) { - struct base_eep_header_4k *pBase4k = - &priv->ah->eeprom.map4k.baseEepHeader; - - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "TX Gain type", - pBase4k->txGainType); - } - - /* - * UB95 specific data. - */ - if (priv->ah->hw_version.usbdev == AR9287_USB) { - struct base_eep_ar9287_header *pBase9287 = - &priv->ah->eeprom.map9287.baseEepHeader; - - len += snprintf(buf + len, size - len, - "%20s : %10ddB\n", - "Power Table Offset", - pBase9287->pwrTableOffset); - - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "OpenLoop Power Ctrl", - pBase9287->openLoopPwrCntl); - } - - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; -} - -static const struct file_operations fops_base_eeprom = { - .read = read_file_base_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_4k_modal_eeprom(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_EEP(_s, _val) \ - do { \ - len += snprintf(buf + len, size - len, "%20s : %10d\n", \ - _s, (_val)); \ - } while (0) - - struct ath9k_htc_priv *priv = file->private_data; - struct modal_eep_4k_header *pModal = &priv->ah->eeprom.map4k.modalHeader; - unsigned int len = 0, size = 2048; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); - PR_EEP("Ant. Common Control", pModal->antCtrlCommon); - PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); - PR_EEP("Switch Settle", pModal->switchSettling); - PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); - PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); - PR_EEP("ADC Desired size", pModal->adcDesiredSize); - PR_EEP("PGA Desired size", pModal->pgaDesiredSize); - PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]); - PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); - PR_EEP("txEndToRxOn", pModal->txEndToRxOn); - PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); - PR_EEP("CCA Threshold)", pModal->thresh62); - PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); - PR_EEP("xpdGain", pModal->xpdGain); - PR_EEP("External PD", pModal->xpd); - PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); - PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); - PR_EEP("pdGainOverlap", pModal->pdGainOverlap); - PR_EEP("O/D Bias Version", pModal->version); - PR_EEP("CCK OutputBias", pModal->ob_0); - PR_EEP("BPSK OutputBias", pModal->ob_1); - PR_EEP("QPSK OutputBias", pModal->ob_2); - PR_EEP("16QAM OutputBias", pModal->ob_3); - PR_EEP("64QAM OutputBias", pModal->ob_4); - PR_EEP("CCK Driver1_Bias", pModal->db1_0); - PR_EEP("BPSK Driver1_Bias", pModal->db1_1); - PR_EEP("QPSK Driver1_Bias", pModal->db1_2); - PR_EEP("16QAM Driver1_Bias", pModal->db1_3); - PR_EEP("64QAM Driver1_Bias", pModal->db1_4); - PR_EEP("CCK Driver2_Bias", pModal->db2_0); - PR_EEP("BPSK Driver2_Bias", pModal->db2_1); - PR_EEP("QPSK Driver2_Bias", pModal->db2_2); - PR_EEP("16QAM Driver2_Bias", pModal->db2_3); - PR_EEP("64QAM Driver2_Bias", pModal->db2_4); - PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); - PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); - PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); - PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); - PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); - PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); - PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); - PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]); - PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]); - PR_EEP("Ant. Diversity ctl1", pModal->antdiv_ctl1); - PR_EEP("Ant. Diversity ctl2", pModal->antdiv_ctl2); - PR_EEP("TX Diversity", pModal->tx_diversity); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PR_EEP -} - -static ssize_t read_def_modal_eeprom(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_EEP(_s, _val) \ - do { \ - if (pBase->opCapFlags & AR5416_OPFLAGS_11G) { \ - pModal = &priv->ah->eeprom.def.modalHeader[1]; \ - len += snprintf(buf + len, size - len, "%20s : %8d%7s", \ - _s, (_val), "|"); \ - } \ - if (pBase->opCapFlags & AR5416_OPFLAGS_11A) { \ - pModal = &priv->ah->eeprom.def.modalHeader[0]; \ - len += snprintf(buf + len, size - len, "%9d\n", \ - (_val)); \ - } \ - } while (0) - - struct ath9k_htc_priv *priv = file->private_data; - struct base_eep_header *pBase = &priv->ah->eeprom.def.baseEepHeader; - struct modal_eep_header *pModal = NULL; - unsigned int len = 0, size = 3500; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += snprintf(buf + len, size - len, - "%31s %15s\n", "2G", "5G"); - len += snprintf(buf + len, size - len, - "%32s %16s\n", "====", "====\n"); - - PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); - PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); - PR_EEP("Chain2 Ant. Control", pModal->antCtrlChain[2]); - PR_EEP("Ant. Common Control", pModal->antCtrlCommon); - PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); - PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]); - PR_EEP("Chain2 Ant. Gain", pModal->antennaGainCh[2]); - PR_EEP("Switch Settle", pModal->switchSettling); - PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); - PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]); - PR_EEP("Chain2 TxRxAtten", pModal->txRxAttenCh[2]); - PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); - PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]); - PR_EEP("Chain2 RxTxMargin", pModal->rxTxMarginCh[2]); - PR_EEP("ADC Desired size", pModal->adcDesiredSize); - PR_EEP("PGA Desired size", pModal->pgaDesiredSize); - PR_EEP("Chain0 xlna Gain", pModal->xlnaGainCh[0]); - PR_EEP("Chain1 xlna Gain", pModal->xlnaGainCh[1]); - PR_EEP("Chain2 xlna Gain", pModal->xlnaGainCh[2]); - PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); - PR_EEP("txEndToRxOn", pModal->txEndToRxOn); - PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); - PR_EEP("CCA Threshold)", pModal->thresh62); - PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); - PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]); - PR_EEP("Chain2 NF Threshold", pModal->noiseFloorThreshCh[2]); - PR_EEP("xpdGain", pModal->xpdGain); - PR_EEP("External PD", pModal->xpd); - PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); - PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]); - PR_EEP("Chain2 I Coefficient", pModal->iqCalICh[2]); - PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); - PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]); - PR_EEP("Chain2 Q Coefficient", pModal->iqCalQCh[2]); - PR_EEP("pdGainOverlap", pModal->pdGainOverlap); - PR_EEP("Chain0 OutputBias", pModal->ob); - PR_EEP("Chain0 DriverBias", pModal->db); - PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); - PR_EEP("2chain pwr decrease", pModal->pwrDecreaseFor2Chain); - PR_EEP("3chain pwr decrease", pModal->pwrDecreaseFor3Chain); - PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); - PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); - PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); - PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); - PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]); - PR_EEP("Chain2 bswAtten", pModal->bswAtten[2]); - PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); - PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]); - PR_EEP("Chain2 bswMargin", pModal->bswMargin[2]); - PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); - PR_EEP("Chain0 xatten2Db", pModal->xatten2Db[0]); - PR_EEP("Chain1 xatten2Db", pModal->xatten2Db[1]); - PR_EEP("Chain2 xatten2Db", pModal->xatten2Db[2]); - PR_EEP("Chain0 xatten2Margin", pModal->xatten2Margin[0]); - PR_EEP("Chain1 xatten2Margin", pModal->xatten2Margin[1]); - PR_EEP("Chain2 xatten2Margin", pModal->xatten2Margin[2]); - PR_EEP("Chain1 OutputBias", pModal->ob_ch1); - PR_EEP("Chain1 DriverBias", pModal->db_ch1); - PR_EEP("LNA Control", pModal->lna_ctl); - PR_EEP("XPA Bias Freq0", pModal->xpaBiasLvlFreq[0]); - PR_EEP("XPA Bias Freq1", pModal->xpaBiasLvlFreq[1]); - PR_EEP("XPA Bias Freq2", pModal->xpaBiasLvlFreq[2]); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PR_EEP -} - -static ssize_t read_9287_modal_eeprom(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ -#define PR_EEP(_s, _val) \ - do { \ - len += snprintf(buf + len, size - len, "%20s : %10d\n", \ - _s, (_val)); \ - } while (0) - - struct ath9k_htc_priv *priv = file->private_data; - struct modal_eep_ar9287_header *pModal = &priv->ah->eeprom.map9287.modalHeader; - unsigned int len = 0, size = 3000; - ssize_t retval = 0; - char *buf; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); - PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); - PR_EEP("Ant. Common Control", pModal->antCtrlCommon); - PR_EEP("Chain0 Ant. Gain", pModal->antennaGainCh[0]); - PR_EEP("Chain1 Ant. Gain", pModal->antennaGainCh[1]); - PR_EEP("Switch Settle", pModal->switchSettling); - PR_EEP("Chain0 TxRxAtten", pModal->txRxAttenCh[0]); - PR_EEP("Chain1 TxRxAtten", pModal->txRxAttenCh[1]); - PR_EEP("Chain0 RxTxMargin", pModal->rxTxMarginCh[0]); - PR_EEP("Chain1 RxTxMargin", pModal->rxTxMarginCh[1]); - PR_EEP("ADC Desired size", pModal->adcDesiredSize); - PR_EEP("txEndToXpaOff", pModal->txEndToXpaOff); - PR_EEP("txEndToRxOn", pModal->txEndToRxOn); - PR_EEP("txFrameToXpaOn", pModal->txFrameToXpaOn); - PR_EEP("CCA Threshold)", pModal->thresh62); - PR_EEP("Chain0 NF Threshold", pModal->noiseFloorThreshCh[0]); - PR_EEP("Chain1 NF Threshold", pModal->noiseFloorThreshCh[1]); - PR_EEP("xpdGain", pModal->xpdGain); - PR_EEP("External PD", pModal->xpd); - PR_EEP("Chain0 I Coefficient", pModal->iqCalICh[0]); - PR_EEP("Chain1 I Coefficient", pModal->iqCalICh[1]); - PR_EEP("Chain0 Q Coefficient", pModal->iqCalQCh[0]); - PR_EEP("Chain1 Q Coefficient", pModal->iqCalQCh[1]); - PR_EEP("pdGainOverlap", pModal->pdGainOverlap); - PR_EEP("xPA Bias Level", pModal->xpaBiasLvl); - PR_EEP("txFrameToDataStart", pModal->txFrameToDataStart); - PR_EEP("txFrameToPaOn", pModal->txFrameToPaOn); - PR_EEP("HT40 Power Inc.", pModal->ht40PowerIncForPdadc); - PR_EEP("Chain0 bswAtten", pModal->bswAtten[0]); - PR_EEP("Chain1 bswAtten", pModal->bswAtten[1]); - PR_EEP("Chain0 bswMargin", pModal->bswMargin[0]); - PR_EEP("Chain1 bswMargin", pModal->bswMargin[1]); - PR_EEP("HT40 Switch Settle", pModal->swSettleHt40); - PR_EEP("AR92x7 Version", pModal->version); - PR_EEP("DriverBias1", pModal->db1); - PR_EEP("DriverBias2", pModal->db1); - PR_EEP("CCK OutputBias", pModal->ob_cck); - PR_EEP("PSK OutputBias", pModal->ob_psk); - PR_EEP("QAM OutputBias", pModal->ob_qam); - PR_EEP("PAL_OFF OutputBias", pModal->ob_pal_off); - - if (len > size) - len = size; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - - return retval; - -#undef PR_EEP -} - -static ssize_t read_file_modal_eeprom(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath9k_htc_priv *priv = file->private_data; - - if (AR_SREV_9271(priv->ah)) - return read_4k_modal_eeprom(file, user_buf, count, ppos); - else if (priv->ah->hw_version.usbdev == AR9280_USB) - return read_def_modal_eeprom(file, user_buf, count, ppos); - else if (priv->ah->hw_version.usbdev == AR9287_USB) - return read_9287_modal_eeprom(file, user_buf, count, ppos); - - return 0; -} - -static const struct file_operations fops_modal_eeprom = { - .read = read_file_modal_eeprom, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - - /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" static const char ath9k_htc_gstrings_stats[][ETH_GSTRING_LEN] = { @@ -947,6 +453,8 @@ int ath9k_htc_get_et_sset_count(struct ieee80211_hw *hw, #define STXBASE priv->debug.tx_stats #define SRXBASE priv->debug.rx_stats +#define SKBTXBASE priv->debug.tx_stats +#define SKBRXBASE priv->debug.skbrx_stats #define ASTXQ(a) \ data[i++] = STXBASE.a[IEEE80211_AC_BE]; \ data[i++] = STXBASE.a[IEEE80211_AC_BK]; \ @@ -960,24 +468,24 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw, struct ath9k_htc_priv *priv = hw->priv; int i = 0; - data[i++] = STXBASE.skb_success; - data[i++] = STXBASE.skb_success_bytes; - data[i++] = SRXBASE.skb_completed; - data[i++] = SRXBASE.skb_completed_bytes; + data[i++] = SKBTXBASE.skb_success; + data[i++] = SKBTXBASE.skb_success_bytes; + data[i++] = SKBRXBASE.skb_completed; + data[i++] = SKBRXBASE.skb_completed_bytes; ASTXQ(queue_stats); - data[i++] = SRXBASE.err_crc; - data[i++] = SRXBASE.err_decrypt_crc; - data[i++] = SRXBASE.err_phy; - data[i++] = SRXBASE.err_mic; - data[i++] = SRXBASE.err_pre_delim; - data[i++] = SRXBASE.err_post_delim; - data[i++] = SRXBASE.err_decrypt_busy; + data[i++] = SRXBASE.crc_err; + data[i++] = SRXBASE.decrypt_crc_err; + data[i++] = SRXBASE.phy_err; + data[i++] = SRXBASE.mic_err; + data[i++] = SRXBASE.pre_delim_crc_err; + data[i++] = SRXBASE.post_delim_crc_err; + data[i++] = SRXBASE.decrypt_busy_err; - data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_RADAR]; - data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_OFDM_TIMING]; - data[i++] = SRXBASE.err_phy_stats[ATH9K_PHYERR_CCK_TIMING]; + data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_RADAR]; + data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_OFDM_TIMING]; + data[i++] = SRXBASE.phy_err_stats[ATH9K_PHYERR_CCK_TIMING]; WARN_ON(i != ATH9K_HTC_SSTATS_LEN); } @@ -1001,18 +509,21 @@ int ath9k_htc_init_debug(struct ath_hw *ah) priv, &fops_tgt_rx_stats); debugfs_create_file("xmit", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_xmit); - debugfs_create_file("recv", S_IRUSR, priv->debug.debugfs_phy, - priv, &fops_recv); + debugfs_create_file("skb_rx", S_IRUSR, priv->debug.debugfs_phy, + priv, &fops_skb_rx); + + ath9k_cmn_debug_recv(priv->debug.debugfs_phy, &priv->debug.rx_stats); + ath9k_cmn_debug_phy_err(priv->debug.debugfs_phy, &priv->debug.rx_stats); + debugfs_create_file("slot", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_slot); debugfs_create_file("queue", S_IRUSR, priv->debug.debugfs_phy, priv, &fops_queue); debugfs_create_file("debug", S_IRUSR | S_IWUSR, priv->debug.debugfs_phy, priv, &fops_debug); - debugfs_create_file("base_eeprom", S_IRUSR, priv->debug.debugfs_phy, - priv, &fops_base_eeprom); - debugfs_create_file("modal_eeprom", S_IRUSR, priv->debug.debugfs_phy, - priv, &fops_modal_eeprom); + + ath9k_cmn_debug_base_eeprom(priv->debug.debugfs_phy, priv->ah); + ath9k_cmn_debug_modal_eeprom(priv->debug.debugfs_phy, priv->ah); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 105582d6b71..50f74a2a4cf 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -255,6 +255,17 @@ void ath9k_deinit_leds(struct ath9k_htc_priv *priv) cancel_work_sync(&priv->led_work); } + +void ath9k_configure_leds(struct ath9k_htc_priv *priv) +{ + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); + +} + void ath9k_init_leds(struct ath9k_htc_priv *priv) { int ret; @@ -268,11 +279,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv) else priv->ah->led_pin = ATH_LED_PIN_DEF; - /* Configure gpio 1 for output */ - ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - /* LED off, active low */ - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); + ath9k_configure_leds(priv); snprintf(priv->led_name, sizeof(priv->led_name), "ath9k_htc-%s", wiphy_name(priv->hw->wiphy)); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index c3676bf1d6c..8a3bd5fe3a5 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -34,92 +34,9 @@ static int ath9k_htc_btcoex_enable; module_param_named(btcoex_enable, ath9k_htc_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); -#define CHAN2G(_freq, _idx) { \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -#define CHAN5G(_freq, _idx) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -static struct ieee80211_channel ath9k_2ghz_channels[] = { - CHAN2G(2412, 0), /* Channel 1 */ - CHAN2G(2417, 1), /* Channel 2 */ - CHAN2G(2422, 2), /* Channel 3 */ - CHAN2G(2427, 3), /* Channel 4 */ - CHAN2G(2432, 4), /* Channel 5 */ - CHAN2G(2437, 5), /* Channel 6 */ - CHAN2G(2442, 6), /* Channel 7 */ - CHAN2G(2447, 7), /* Channel 8 */ - CHAN2G(2452, 8), /* Channel 9 */ - CHAN2G(2457, 9), /* Channel 10 */ - CHAN2G(2462, 10), /* Channel 11 */ - CHAN2G(2467, 11), /* Channel 12 */ - CHAN2G(2472, 12), /* Channel 13 */ - CHAN2G(2484, 13), /* Channel 14 */ -}; - -static struct ieee80211_channel ath9k_5ghz_channels[] = { - /* _We_ call this UNII 1 */ - CHAN5G(5180, 14), /* Channel 36 */ - CHAN5G(5200, 15), /* Channel 40 */ - CHAN5G(5220, 16), /* Channel 44 */ - CHAN5G(5240, 17), /* Channel 48 */ - /* _We_ call this UNII 2 */ - CHAN5G(5260, 18), /* Channel 52 */ - CHAN5G(5280, 19), /* Channel 56 */ - CHAN5G(5300, 20), /* Channel 60 */ - CHAN5G(5320, 21), /* Channel 64 */ - /* _We_ call this "Middle band" */ - CHAN5G(5500, 22), /* Channel 100 */ - CHAN5G(5520, 23), /* Channel 104 */ - CHAN5G(5540, 24), /* Channel 108 */ - CHAN5G(5560, 25), /* Channel 112 */ - CHAN5G(5580, 26), /* Channel 116 */ - CHAN5G(5600, 27), /* Channel 120 */ - CHAN5G(5620, 28), /* Channel 124 */ - CHAN5G(5640, 29), /* Channel 128 */ - CHAN5G(5660, 30), /* Channel 132 */ - CHAN5G(5680, 31), /* Channel 136 */ - CHAN5G(5700, 32), /* Channel 140 */ - /* _We_ call this UNII 3 */ - CHAN5G(5745, 33), /* Channel 149 */ - CHAN5G(5765, 34), /* Channel 153 */ - CHAN5G(5785, 35), /* Channel 157 */ - CHAN5G(5805, 36), /* Channel 161 */ - CHAN5G(5825, 37), /* Channel 165 */ -}; - -/* Atheros hardware rate code addition for short premble */ -#define SHPCHECK(__hw_rate, __flags) \ - ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0) - -#define RATE(_bitrate, _hw_rate, _flags) { \ - .bitrate = (_bitrate), \ - .flags = (_flags), \ - .hw_value = (_hw_rate), \ - .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ -} - -static struct ieee80211_rate ath9k_legacy_rates[] = { - RATE(10, 0x1b, 0), - RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */ - RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */ - RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */ - RATE(60, 0x0b, 0), - RATE(90, 0x0f, 0), - RATE(120, 0x0a, 0), - RATE(180, 0x0e, 0), - RATE(240, 0x09, 0), - RATE(360, 0x0d, 0), - RATE(480, 0x08, 0), - RATE(540, 0x0c, 0), -}; +static int ath9k_ps_enable; +module_param_named(ps_enable, ath9k_ps_enable, int, 0444); +MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); #ifdef CONFIG_MAC80211_LEDS static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = { @@ -339,6 +256,25 @@ static void ath9k_multi_regread(void *hw_priv, u32 *addr, } } +static void ath9k_regwrite_multi(struct ath_common *common) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + u32 rsp_status; + int r; + + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, + (u8 *) &priv->wmi->multi_write, + sizeof(struct register_write) * priv->wmi->multi_write_idx, + (u8 *) &rsp_status, sizeof(rsp_status), + 100); + if (unlikely(r)) { + ath_dbg(common, WMI, + "REGISTER WRITE FAILED, multi len: %d\n", + priv->wmi->multi_write_idx); + } + priv->wmi->multi_write_idx = 0; +} + static void ath9k_regwrite_single(void *hw_priv, u32 val, u32 reg_offset) { struct ath_hw *ah = (struct ath_hw *) hw_priv; @@ -365,8 +301,6 @@ static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset) struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; - u32 rsp_status; - int r; mutex_lock(&priv->wmi->multi_write_mutex); @@ -379,19 +313,8 @@ static void ath9k_regwrite_buffer(void *hw_priv, u32 val, u32 reg_offset) priv->wmi->multi_write_idx++; /* If the buffer is full, send it out. */ - if (priv->wmi->multi_write_idx == MAX_CMD_NUMBER) { - r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, - (u8 *) &priv->wmi->multi_write, - sizeof(struct register_write) * priv->wmi->multi_write_idx, - (u8 *) &rsp_status, sizeof(rsp_status), - 100); - if (unlikely(r)) { - ath_dbg(common, WMI, - "REGISTER WRITE FAILED, multi len: %d\n", - priv->wmi->multi_write_idx); - } - priv->wmi->multi_write_idx = 0; - } + if (priv->wmi->multi_write_idx == MAX_CMD_NUMBER) + ath9k_regwrite_multi(common); mutex_unlock(&priv->wmi->multi_write_mutex); } @@ -422,26 +345,13 @@ static void ath9k_regwrite_flush(void *hw_priv) struct ath_hw *ah = (struct ath_hw *) hw_priv; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; - u32 rsp_status; - int r; atomic_dec(&priv->wmi->mwrite_cnt); mutex_lock(&priv->wmi->multi_write_mutex); - if (priv->wmi->multi_write_idx) { - r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, - (u8 *) &priv->wmi->multi_write, - sizeof(struct register_write) * priv->wmi->multi_write_idx, - (u8 *) &rsp_status, sizeof(rsp_status), - 100); - if (unlikely(r)) { - ath_dbg(common, WMI, - "REGISTER WRITE FAILED, multi len: %d\n", - priv->wmi->multi_write_idx); - } - priv->wmi->multi_write_idx = 0; - } + if (priv->wmi->multi_write_idx) + ath9k_regwrite_multi(common); mutex_unlock(&priv->wmi->multi_write_mutex); } @@ -487,51 +397,6 @@ static const struct ath_bus_ops ath9k_usb_bus_ops = { .eeprom_read = ath_usb_eeprom_read, }; -static void setup_ht_cap(struct ath9k_htc_priv *priv, - struct ieee80211_sta_ht_cap *ht_info) -{ - struct ath_common *common = ath9k_hw_common(priv->ah); - u8 tx_streams, rx_streams; - int i; - - ht_info->ht_supported = true; - ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_SM_PS | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20) - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - - ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); - - ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; - - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - - /* ath9k_htc supports only 1 or 2 stream devices */ - tx_streams = ath9k_cmn_count_streams(priv->ah->txchainmask, 2); - rx_streams = ath9k_cmn_count_streams(priv->ah->rxchainmask, 2); - - ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", - tx_streams, rx_streams); - - if (tx_streams >= 2) - ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; - - if (tx_streams != rx_streams) { - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_streams - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } - - for (i = 0; i < rx_streams; i++) - ht_info->mcs.rx_mask[i] = 0xff; - - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; -} - static int ath9k_init_queues(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); @@ -540,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; } @@ -576,37 +441,13 @@ err: return -EINVAL; } -static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) -{ - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { - priv->sbands[IEEE80211_BAND_2GHZ].channels = - ath9k_2ghz_channels; - priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; - priv->sbands[IEEE80211_BAND_2GHZ].n_channels = - ARRAY_SIZE(ath9k_2ghz_channels); - priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; - priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates); - } - - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { - priv->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_channels; - priv->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; - priv->sbands[IEEE80211_BAND_5GHZ].n_channels = - ARRAY_SIZE(ath9k_5ghz_channels); - priv->sbands[IEEE80211_BAND_5GHZ].bitrates = - ath9k_legacy_rates + 4; - priv->sbands[IEEE80211_BAND_5GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates) - 4; - } -} - static void ath9k_init_misc(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + common->last_rssi = ATH_RSSI_DUMMY_MARKER; priv->ah->opmode = NL80211_IFTYPE_STATION; } @@ -618,12 +459,11 @@ 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; + ah->dev = priv->dev; ah->hw_version.devid = devid; ah->hw_version.usbdev = drv_info; ah->ah_flags |= AH_USE_EEPROM; @@ -643,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); @@ -678,10 +519,11 @@ 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); - ath9k_init_channels_rates(priv); ath9k_init_misc(priv); ath9k_htc_init_btcoex(priv, product); @@ -717,6 +559,7 @@ static const struct ieee80211_iface_combination if_comb = { static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw) { + struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(priv->ah); struct base_eep_header *pBase; @@ -725,12 +568,14 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING; + if (ath9k_ps_enable) + hw->flags |= IEEE80211_HW_SUPPORTS_PS; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC) | @@ -748,7 +593,6 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->queues = 4; - hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->vif_data_size = sizeof(struct ath9k_htc_vif); @@ -760,19 +604,12 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &priv->sbands[IEEE80211_BAND_2GHZ]; + &common->sbands[IEEE80211_BAND_2GHZ]; if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &priv->sbands[IEEE80211_BAND_5GHZ]; - - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) - setup_ht_cap(priv, - &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) - setup_ht_cap(priv, - &priv->sbands[IEEE80211_BAND_5GHZ].ht_cap); - } + &common->sbands[IEEE80211_BAND_5GHZ]; + + ath9k_cmn_reload_chainmask(ah); pBase = ath9k_htc_get_eeprom_base(priv); if (pBase) { @@ -1000,6 +837,8 @@ int ath9k_htc_resume(struct htc_target *htc_handle) ret = ath9k_init_htc_services(priv, priv->ah->hw_version.devid, priv->ah->hw_version.usbdev); + ath9k_configure_leds(priv); + return ret; } #endif diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index d44258172c0..5627917c5ff 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -24,30 +24,10 @@ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, struct ath9k_channel *ichan) { - enum htc_phymode mode; - - mode = -EINVAL; - - switch (ichan->chanmode) { - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - mode = HTC_MODE_11NG; - break; - case CHANNEL_A: - case CHANNEL_A_HT20: - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - mode = HTC_MODE_11NA; - break; - default: - break; - } - - WARN_ON(mode < 0); + if (IS_CHAN_5GHZ(ichan)) + return HTC_MODE_11NA; - return mode; + return HTC_MODE_11NG; } bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, @@ -115,8 +95,10 @@ static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) if ((vif->type == NL80211_IFTYPE_AP || vif->type == NL80211_IFTYPE_MESH_POINT) && - bss_conf->enable_beacon) + bss_conf->enable_beacon) { priv->reconfig_beacon = true; + priv->rearm_ani = true; + } if (bss_conf->assoc) { priv->rearm_ani = true; @@ -147,21 +129,26 @@ static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif) struct ath9k_vif_iter_data *iter_data = data; int i; - for (i = 0; i < ETH_ALEN; i++) - iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); + if (iter_data->hw_macaddr != NULL) { + for (i = 0; i < ETH_ALEN; i++) + iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); + } else { + iter_data->hw_macaddr = mac; + } } -static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, +static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_vif_iter_data iter_data; /* - * Use the hardware MAC address as reference, the hardware uses it - * together with the BSSID mask when matching addresses. + * Pick the MAC address of the first interface as the new hardware + * MAC address. The hardware will use it together with the BSSID mask + * when matching addresses. */ - iter_data.hw_macaddr = common->macaddr; + iter_data.hw_macaddr = NULL; memset(&iter_data.mask, 0xff, ETH_ALEN); if (vif) @@ -173,6 +160,10 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, ath9k_htc_bssid_iter, &iter_data); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); + + if (iter_data.hw_macaddr) + memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN); + ath_hw_setbssidmask(common); } @@ -261,13 +252,14 @@ 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); ath9k_htc_ps_wakeup(priv); + ath9k_htc_stop_ani(priv); del_timer_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); @@ -315,7 +307,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); @@ -759,7 +751,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)); @@ -767,8 +759,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) @@ -926,7 +919,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) WMI_CMD(WMI_FLUSH_RECV_CMDID); /* setup initial channel */ - init_channel = ath9k_cmn_get_curchannel(hw, ah); + init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { @@ -953,7 +946,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); @@ -982,7 +975,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; @@ -1024,7 +1017,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); @@ -1083,7 +1076,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, goto out; } - ath9k_htc_set_bssid_mask(priv, vif); + ath9k_htc_set_mac_bssid_mask(priv, vif); priv->vif_slot |= (1 << avp->index); priv->nvifs++; @@ -1098,7 +1091,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); } @@ -1148,7 +1141,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, ath9k_htc_set_opmode(priv); - ath9k_htc_set_bssid_mask(priv, vif); + ath9k_htc_set_mac_bssid_mask(priv, vif); /* * Stop ANI only if there are no associated station interfaces. @@ -1208,9 +1201,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); - ath9k_cmn_update_ichannel(&priv->ah->channels[pos], - &hw->conf.chandef); - + ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); ret = -EINVAL; @@ -1258,13 +1249,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); @@ -1283,18 +1275,50 @@ static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, mutex_unlock(&priv->mutex); } +static void ath9k_htc_sta_rc_update_work(struct work_struct *work) +{ + struct ath9k_htc_sta *ista = + container_of(work, struct ath9k_htc_sta, rc_update_work); + struct ieee80211_sta *sta = + container_of((void *)ista, struct ieee80211_sta, drv_priv); + struct ath9k_htc_priv *priv = ista->htc_priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_rate trate; + + mutex_lock(&priv->mutex); + ath9k_htc_ps_wakeup(priv); + + memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); + ath9k_htc_setup_rate(priv, sta, &trate); + if (!ath9k_htc_send_rate_cmd(priv, &trate)) + ath_dbg(common, CONFIG, + "Supported rates for sta: %pM updated, rate caps: 0x%X\n", + sta->addr, be32_to_cpu(trate.capflags)); + else + ath_dbg(common, CONFIG, + "Unable to update supported rates for sta: %pM\n", + sta->addr); + + ath9k_htc_ps_restore(priv); + mutex_unlock(&priv->mutex); +} + static int ath9k_htc_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { struct ath9k_htc_priv *priv = hw->priv; + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; int ret; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); ret = ath9k_htc_add_station(priv, vif, sta); - if (!ret) + if (!ret) { + INIT_WORK(&ista->rc_update_work, ath9k_htc_sta_rc_update_work); + ista->htc_priv = priv; ath9k_htc_init_rate(priv, sta); + } ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); @@ -1306,12 +1330,13 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct ath9k_htc_priv *priv = hw->priv; - struct ath9k_htc_sta *ista; + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; int ret; + cancel_work_sync(&ista->rc_update_work); + mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); - ista = (struct ath9k_htc_sta *) sta->drv_priv; htc_sta_drain(priv->htc, ista->index); ret = ath9k_htc_remove_station(priv, vif, sta); ath9k_htc_ps_restore(priv); @@ -1324,28 +1349,12 @@ static void ath9k_htc_sta_rc_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u32 changed) { - struct ath9k_htc_priv *priv = hw->priv; - struct ath_common *common = ath9k_hw_common(priv->ah); - struct ath9k_htc_target_rate trate; - - mutex_lock(&priv->mutex); - ath9k_htc_ps_wakeup(priv); + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; - if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { - memset(&trate, 0, sizeof(struct ath9k_htc_target_rate)); - ath9k_htc_setup_rate(priv, sta, &trate); - if (!ath9k_htc_send_rate_cmd(priv, &trate)) - ath_dbg(common, CONFIG, - "Supported rates for sta: %pM updated, rate caps: 0x%X\n", - sta->addr, be32_to_cpu(trate.capflags)); - else - ath_dbg(common, CONFIG, - "Unable to update supported rates for sta: %pM\n", - sta->addr); - } + if (!(changed & IEEE80211_RC_SUPP_RATES_CHANGED)) + return; - ath9k_htc_ps_restore(priv); - mutex_unlock(&priv->mutex); + schedule_work(&ista->rc_update_work); } static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, @@ -1470,7 +1479,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) if ((vif->type == NL80211_IFTYPE_STATION) && bss_conf->assoc) { 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); } } @@ -1492,6 +1503,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); @@ -1503,6 +1515,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)) @@ -1524,7 +1539,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); } @@ -1538,7 +1553,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); } } @@ -1564,11 +1579,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) @@ -1665,10 +1690,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); @@ -1678,10 +1704,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 c028df76b56..bb86eb2ffc9 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -471,8 +471,11 @@ static void ath9k_htc_tx_process(struct ath9k_htc_priv *priv, if (!txok || !vif || !txs) goto send_mac80211; - if (txs->ts_flags & ATH9K_HTC_TXSTAT_ACK) + if (txs->ts_flags & ATH9K_HTC_TXSTAT_ACK) { tx_info->flags |= IEEE80211_TX_STAT_ACK; + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) + tx_info->flags |= IEEE80211_TX_STAT_AMPDU; + } if (txs->ts_flags & ATH9K_HTC_TXSTAT_FILT) tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; @@ -924,46 +927,43 @@ 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)); - priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER; + ath9k_hw_startpcureceive(priv->ah, test_bit(ATH_OP_SCANNING, &common->op_flags)); } -static void ath9k_process_rate(struct ieee80211_hw *hw, - struct ieee80211_rx_status *rxs, - u8 rx_rate, u8 rs_flags) +static inline void convert_htc_flag(struct ath_rx_status *rx_stats, + struct ath_htc_rx_status *rxstatus) { - struct ieee80211_supported_band *sband; - enum ieee80211_band band; - unsigned int i = 0; - - if (rx_rate & 0x80) { - /* HT rate */ - rxs->flag |= RX_FLAG_HT; - if (rs_flags & ATH9K_RX_2040) - rxs->flag |= RX_FLAG_40MHZ; - if (rs_flags & ATH9K_RX_GI) - rxs->flag |= RX_FLAG_SHORT_GI; - rxs->rate_idx = rx_rate & 0x7f; - return; - } - - band = hw->conf.chandef.chan->band; - sband = hw->wiphy->bands[band]; - - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].hw_value == rx_rate) { - rxs->rate_idx = i; - return; - } - if (sband->bitrates[i].hw_value_short == rx_rate) { - rxs->rate_idx = i; - rxs->flag |= RX_FLAG_SHORTPRE; - return; - } - } + rx_stats->flag = 0; + if (rxstatus->rs_flags & ATH9K_RX_2040) + rx_stats->flag |= RX_FLAG_40MHZ; + if (rxstatus->rs_flags & ATH9K_RX_GI) + rx_stats->flag |= RX_FLAG_SHORT_GI; +} +static void rx_status_htc_to_ath(struct ath_rx_status *rx_stats, + struct ath_htc_rx_status *rxstatus) +{ + rx_stats->rs_datalen = rxstatus->rs_datalen; + rx_stats->rs_status = rxstatus->rs_status; + rx_stats->rs_phyerr = rxstatus->rs_phyerr; + rx_stats->rs_rssi = rxstatus->rs_rssi; + rx_stats->rs_keyix = rxstatus->rs_keyix; + rx_stats->rs_rate = rxstatus->rs_rate; + rx_stats->rs_antenna = rxstatus->rs_antenna; + rx_stats->rs_more = rxstatus->rs_more; + + memcpy(rx_stats->rs_rssi_ctl, rxstatus->rs_rssi_ctl, + sizeof(rx_stats->rs_rssi_ctl)); + memcpy(rx_stats->rs_rssi_ext, rxstatus->rs_rssi_ext, + sizeof(rx_stats->rs_rssi_ext)); + + rx_stats->rs_isaggr = rxstatus->rs_isaggr; + rx_stats->rs_moreaggr = rxstatus->rs_moreaggr; + rx_stats->rs_num_delims = rxstatus->rs_num_delims; + convert_htc_flag(rx_stats, rxstatus); } static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, @@ -975,10 +975,10 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, struct ieee80211_hw *hw = priv->hw; struct sk_buff *skb = rxbuf->skb; struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath_hw *ah = common->ah; struct ath_htc_rx_status *rxstatus; - int hdrlen, padsize; - int last_rssi = ATH_RSSI_DUMMY_MARKER; - __le16 fc; + struct ath_rx_status rx_stats; + bool decrypt_error; if (skb->len < HTC_RX_FRAME_HEADER_SIZE) { ath_err(common, "Corrupted RX frame, dropping (len: %d)\n", @@ -996,108 +996,41 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, goto rx_next; } - ath9k_htc_err_stat_rx(priv, rxstatus); - /* Get the RX status information */ - memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE); - skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE); - - hdr = (struct ieee80211_hdr *)skb->data; - fc = hdr->frame_control; - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - - padsize = hdrlen & 3; - if (padsize && skb->len >= hdrlen+padsize+FCS_LEN) { - memmove(skb->data + padsize, skb->data, hdrlen); - skb_pull(skb, padsize); - } memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); - if (rxbuf->rxstatus.rs_status != 0) { - if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC) - rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY) - goto rx_next; - - if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) { - /* FIXME */ - } else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) { - if (ieee80211_is_ctl(fc)) - /* - * Sometimes, we get invalid - * MIC failures on valid control frames. - * Remove these mic errors. - */ - rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC; - else - rx_status->flag |= RX_FLAG_MMIC_ERROR; - } - - /* - * Reject error frames with the exception of - * decryption and MIC failures. For monitor mode, - * we also ignore the CRC error. - */ - if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) { - if (rxbuf->rxstatus.rs_status & - ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | - ATH9K_RXERR_CRC)) - goto rx_next; - } else { - if (rxbuf->rxstatus.rs_status & - ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { - goto rx_next; - } - } - } - - if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) { - u8 keyix; - keyix = rxbuf->rxstatus.rs_keyix; - if (keyix != ATH9K_RXKEYIX_INVALID) { - rx_status->flag |= RX_FLAG_DECRYPTED; - } else if (ieee80211_has_protected(fc) && - skb->len >= hdrlen + 4) { - keyix = skb->data[hdrlen + 3] >> 6; - if (test_bit(keyix, common->keymap)) - rx_status->flag |= RX_FLAG_DECRYPTED; - } - } - - ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate, - rxbuf->rxstatus.rs_flags); - - if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD && - !rxbuf->rxstatus.rs_moreaggr) - ATH_RSSI_LPF(priv->rx.last_rssi, - rxbuf->rxstatus.rs_rssi); - - last_rssi = priv->rx.last_rssi; + /* Copy everything from ath_htc_rx_status (HTC_RX_FRAME_HEADER). + * After this, we can drop this part of skb. */ + rx_status_htc_to_ath(&rx_stats, rxstatus); + ath9k_htc_err_stat_rx(priv, &rx_stats); + rx_status->mactime = be64_to_cpu(rxstatus->rs_tstamp); + skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE); - if (ieee80211_is_beacon(hdr->frame_control) && - !is_zero_ether_addr(common->curbssid) && - ether_addr_equal(hdr->addr3, common->curbssid)) { - s8 rssi = rxbuf->rxstatus.rs_rssi; + /* + * everything but the rate is checked here, the rate check is done + * separately to avoid doing two lookups for a rate for each frame. + */ + hdr = (struct ieee80211_hdr *)skb->data; + if (!ath9k_cmn_rx_accept(common, hdr, rx_status, &rx_stats, + &decrypt_error, priv->rxfilter)) + goto rx_next; - if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) - rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); + ath9k_cmn_rx_skb_postprocess(common, skb, &rx_stats, + rx_status, decrypt_error); - if (rssi < 0) - rssi = 0; + if (ath9k_cmn_process_rate(common, hw, &rx_stats, rx_status)) + goto rx_next; - priv->ah->stats.avgbrssi = rssi; - } + rx_stats.is_mybeacon = ath_is_mybeacon(common, hdr); + ath9k_cmn_process_rssi(common, hw, &rx_stats, rx_status); - rx_status->mactime = be64_to_cpu(rxbuf->rxstatus.rs_tstamp); - rx_status->band = hw->conf.chandef.chan->band; - rx_status->freq = hw->conf.chandef.chan->center_freq; - rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; - rx_status->antenna = rxbuf->rxstatus.rs_antenna; + rx_status->band = ah->curchan->chan->band; + rx_status->freq = ah->curchan->chan->center_freq; + rx_status->antenna = rx_stats.rs_antenna; rx_status->flag |= RX_FLAG_MACTIME_END; return true; - rx_next: return false; } diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index aac4a406a51..a0ff5b63705 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -358,6 +358,36 @@ ret: kfree_skb(skb); } +static void ath9k_htc_fw_panic_report(struct htc_target *htc_handle, + struct sk_buff *skb) +{ + uint32_t *pattern = (uint32_t *)skb->data; + + switch (*pattern) { + case 0x33221199: + { + struct htc_panic_bad_vaddr *htc_panic; + htc_panic = (struct htc_panic_bad_vaddr *) skb->data; + dev_err(htc_handle->dev, "ath: firmware panic! " + "exccause: 0x%08x; pc: 0x%08x; badvaddr: 0x%08x.\n", + htc_panic->exccause, htc_panic->pc, + htc_panic->badvaddr); + break; + } + case 0x33221299: + { + struct htc_panic_bad_epid *htc_panic; + htc_panic = (struct htc_panic_bad_epid *) skb->data; + dev_err(htc_handle->dev, "ath: firmware panic! " + "bad epid: 0x%08x\n", htc_panic->epid); + break; + } + default: + dev_err(htc_handle->dev, "ath: uknown panic pattern!\n"); + break; + } +} + /* * HTC Messages are handled directly here and the obtained SKB * is freed. @@ -379,6 +409,12 @@ void ath9k_htc_rx_msg(struct htc_target *htc_handle, htc_hdr = (struct htc_frame_hdr *) skb->data; epid = htc_hdr->endpoint_id; + if (epid == 0x99) { + ath9k_htc_fw_panic_report(htc_handle, skb); + kfree_skb(skb); + return; + } + if (epid >= ENDPOINT_MAX) { if (pipe_id != USB_REG_IN_PIPE) dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h index e1ffbb6bd63..06474ccc769 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.h +++ b/drivers/net/wireless/ath/ath9k/htc_hst.h @@ -77,6 +77,18 @@ struct htc_config_pipe_msg { u8 credits; } __packed; +struct htc_panic_bad_vaddr { + __be32 pattern; + __be32 exccause; + __be32 pc; + __be32 badvaddr; +} __packed; + +struct htc_panic_bad_epid { + __be32 pattern; + __be32 epid; +} __packed; + struct htc_ep_callbacks { void *priv; void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok); diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 83f4927aeac..a47ea8423f1 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -49,9 +49,10 @@ static inline bool ath9k_hw_calibrate(struct ath_hw *ah, return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal); } -static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) +static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p) { - return ath9k_hw_ops(ah)->get_isr(ah, masked); + return ath9k_hw_ops(ah)->get_isr(ah, masked, sync_cause_p); } static inline void ath9k_hw_set_txdesc(struct ath_hw *ah, void *ds, @@ -78,6 +79,22 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } +static inline void ath9k_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + ath9k_hw_ops(ah)->tx99_start(ah, qnum); +} + +static inline void ath9k_hw_tx99_stop(struct ath_hw *ah) +{ + ath9k_hw_ops(ah)->tx99_stop(ah); +} + +static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power) +{ + if (ath9k_hw_ops(ah)->tx99_set_txpower) + ath9k_hw_ops(ah)->tx99_set_txpower(ah, power); +} + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) @@ -90,6 +107,21 @@ static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) /* Private hardware call ops */ +static inline void ath9k_hw_init_hang_checks(struct ath_hw *ah) +{ + ath9k_hw_private_ops(ah)->init_hang_checks(ah); +} + +static inline bool ath9k_hw_detect_mac_hang(struct ath_hw *ah) +{ + return ath9k_hw_private_ops(ah)->detect_mac_hang(ah); +} + +static inline bool ath9k_hw_detect_bb_hang(struct ath_hw *ah) +{ + return ath9k_hw_private_ops(ah)->detect_bb_hang(ah); +} + /* PHY ops */ static inline int ath9k_hw_rf_set_freq(struct ath_hw *ah, @@ -215,4 +247,31 @@ static inline void ath9k_hw_set_radar_params(struct ath_hw *ah) ath9k_hw_private_ops(ah)->set_radar_params(ah, &ah->radar_conf); } +static inline void ath9k_hw_init_cal_settings(struct ath_hw *ah) +{ + ath9k_hw_private_ops(ah)->init_cal_settings(ah); +} + +static inline u32 ath9k_hw_compute_pll_control(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + return ath9k_hw_private_ops(ah)->compute_pll_control(ah, chan); +} + +static inline void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) +{ + if (!ath9k_hw_private_ops(ah)->init_mode_gain_regs) + return; + + ath9k_hw_private_ops(ah)->init_mode_gain_regs(ah); +} + +static inline void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah) +{ + if (!ath9k_hw_private_ops(ah)->ani_cache_ini_regs) + return; + + ath9k_hw_private_ops(ah)->ani_cache_ini_regs(ah); +} + #endif /* ATH9K_HW_OPS_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ecc6ec4a1ed..2a8ed8375ec 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -17,15 +17,15 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/time.h> +#include <linux/bitops.h> #include <asm/unaligned.h> #include "hw.h" #include "hw-ops.h" -#include "rc.h" #include "ar9003_mac.h" #include "ar9003_mci.h" #include "ar9003_phy.h" -#include "debug.h" #include "ath9k.h" static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); @@ -35,124 +35,30 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); MODULE_LICENSE("Dual BSD/GPL"); -static int __init ath9k_init(void) -{ - return 0; -} -module_init(ath9k_init); - -static void __exit ath9k_exit(void) -{ - return; -} -module_exit(ath9k_exit); - -/* Private hardware callbacks */ - -static void ath9k_hw_init_cal_settings(struct ath_hw *ah) -{ - ath9k_hw_private_ops(ah)->init_cal_settings(ah); -} - -static u32 ath9k_hw_compute_pll_control(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - return ath9k_hw_private_ops(ah)->compute_pll_control(ah, chan); -} - -static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah) -{ - if (!ath9k_hw_private_ops(ah)->init_mode_gain_regs) - return; - - ath9k_hw_private_ops(ah)->init_mode_gain_regs(ah); -} - -static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah) -{ - /* You will not have this callback if using the old ANI */ - if (!ath9k_hw_private_ops(ah)->ani_cache_ini_regs) - return; - - ath9k_hw_private_ops(ah)->ani_cache_ini_regs(ah); -} - -/********************/ -/* Helper Functions */ -/********************/ - -#ifdef CONFIG_ATH9K_DEBUGFS - -void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause) -{ - struct ath_softc *sc = common->priv; - if (sync_cause) - sc->debug.stats.istats.sync_cause_all++; - if (sync_cause & AR_INTR_SYNC_RTC_IRQ) - sc->debug.stats.istats.sync_rtc_irq++; - if (sync_cause & AR_INTR_SYNC_MAC_IRQ) - sc->debug.stats.istats.sync_mac_irq++; - if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS) - sc->debug.stats.istats.eeprom_illegal_access++; - if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT) - sc->debug.stats.istats.apb_timeout++; - if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT) - sc->debug.stats.istats.pci_mode_conflict++; - if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) - sc->debug.stats.istats.host1_fatal++; - if (sync_cause & AR_INTR_SYNC_HOST1_PERR) - sc->debug.stats.istats.host1_perr++; - if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR) - sc->debug.stats.istats.trcv_fifo_perr++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP) - sc->debug.stats.istats.radm_cpl_ep++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT) - sc->debug.stats.istats.radm_cpl_dllp_abort++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT) - sc->debug.stats.istats.radm_cpl_tlp_abort++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR) - sc->debug.stats.istats.radm_cpl_ecrc_err++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) - sc->debug.stats.istats.radm_cpl_timeout++; - if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) - sc->debug.stats.istats.local_timeout++; - if (sync_cause & AR_INTR_SYNC_PM_ACCESS) - sc->debug.stats.istats.pm_access++; - if (sync_cause & AR_INTR_SYNC_MAC_AWAKE) - sc->debug.stats.istats.mac_awake++; - if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP) - sc->debug.stats.istats.mac_asleep++; - if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS) - sc->debug.stats.istats.mac_sleep_access++; -} -#endif - - static void ath9k_hw_set_clockrate(struct ath_hw *ah) { - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_channel *chan = ah->curchan; unsigned int clockrate; /* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) clockrate = 117; - else if (!ah->curchan) /* should really check for CCK instead */ + else if (!chan) /* should really check for CCK instead */ clockrate = ATH9K_CLOCK_RATE_CCK; - else if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ) + else if (IS_CHAN_2GHZ(chan)) clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; else clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; - if (conf_is_ht40(conf)) - clockrate *= 2; - - if (ah->curchan) { - if (IS_CHAN_HALF_RATE(ah->curchan)) + if (chan) { + if (IS_CHAN_HT40(chan)) + clockrate *= 2; + if (IS_CHAN_HALF_RATE(chan)) clockrate /= 2; - if (IS_CHAN_QUARTER_RATE(ah->curchan)) + if (IS_CHAN_QUARTER_RATE(chan)) clockrate /= 4; } @@ -190,10 +96,7 @@ EXPORT_SYMBOL(ath9k_hw_wait); void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, int hw_delay) { - if (IS_CHAN_B(chan)) - hw_delay = (4 * hw_delay) / 22; - else - hw_delay /= 10; + hw_delay /= 10; if (IS_CHAN_HALF_RATE(chan)) hw_delay *= 2; @@ -294,8 +197,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah, return; } - if ((chan->chanmode == CHANNEL_A_HT40PLUS) || - (chan->chanmode == CHANNEL_G_HT40PLUS)) { + if (IS_CHAN_HT40PLUS(chan)) { centers->synth_center = chan->channel + HT40_CHANNEL_CENTER_SHIFT; extoff = 1; @@ -341,6 +243,11 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) case AR9300_DEVID_QCA955X: ah->hw_version.macVersion = AR_SREV_VERSION_9550; return; + case AR9300_DEVID_AR953X: + ah->hw_version.macVersion = AR_SREV_VERSION_9531; + if (ah->get_mac_revision) + ah->hw_version.macRev = ah->get_mac_revision(); + return; } val = REG_READ(ah, AR_SREV) & AR_SREV_ID; @@ -442,23 +349,22 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah) static void ath9k_hw_init_config(struct ath_hw *ah) { - int i; + struct ath_common *common = ath9k_hw_common(ah); ah->config.dma_beacon_response_time = 1; ah->config.sw_beacon_response_time = 6; - ah->config.additional_swba_backoff = 0; - ah->config.ack_6mb = 0x0; ah->config.cwm_ignore_extcca = 0; - ah->config.pcie_clock_req = 0; ah->config.analog_shiftreg = 1; - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { - ah->config.spurchans[i][0] = AR_NO_SPUR; - ah->config.spurchans[i][1] = AR_NO_SPUR; - } - ah->config.rx_intr_mitigation = true; - ah->config.pcieSerDesWrite = true; + + if (AR_SREV_9300_20_OR_LATER(ah)) { + ah->config.rimt_last = 500; + ah->config.rimt_first = 2000; + } else { + ah->config.rimt_last = 250; + ah->config.rimt_first = 700; + } /* * We need this for PCI devices only (Cardbus, PCI, miniPCI) @@ -478,6 +384,24 @@ static void ath9k_hw_init_config(struct ath_hw *ah) */ if (num_possible_cpus() > 1) ah->config.serialize_regmode = SER_REG_MODE_AUTO; + + if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) { + if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || + ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) && + !ah->is_pciexpress)) { + ah->config.serialize_regmode = SER_REG_MODE_ON; + } else { + ah->config.serialize_regmode = SER_REG_MODE_OFF; + } + } + + ath_dbg(common, RESET, "serialize_regmode is %d\n", + ah->config.serialize_regmode); + + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) + ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1; + else + ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD; } static void ath9k_hw_init_defaults(struct ath_hw *ah) @@ -490,16 +414,24 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->hw_version.magic = AR5416_MAGIC; ah->hw_version.subvendorid = 0; - ah->atim_window = 0; - ah->sta_id1_defaults = - AR_STA_ID1_CRPT_MIC_ENABLE | - AR_STA_ID1_MCAST_KSRCH; + ah->sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE | + AR_STA_ID1_MCAST_KSRCH; if (AR_SREV_9100(ah)) ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX; + ah->slottime = ATH9K_SLOT_TIME_9; ah->globaltxtimeout = (u32) -1; ah->power_mode = ATH9K_PM_UNDEFINED; ah->htc_reset_init = true; + + ah->ani_function = ATH9K_ANI_ALL; + if (!AR_SREV_9300_20_OR_LATER(ah)) + ah->ani_function &= ~ATH9K_ANI_MRC_CCK; + + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) + ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S); + else + ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); } static int ath9k_hw_init_macaddr(struct ath_hw *ah) @@ -549,6 +481,18 @@ static int ath9k_hw_post_init(struct ath_hw *ah) ath9k_hw_ani_init(ah); + /* + * EEPROM needs to be initialized before we do this. + * This is required for regulatory compliance. + */ + if (AR_SREV_9300_20_OR_LATER(ah)) { + u16 regdmn = ah->eep_ops->get_eeprom(ah, EEP_REG_0); + if ((regdmn & 0xF0) == CTL_FCC) { + ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_FCC_2GHZ; + ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_FCC_5GHZ; + } + } + return 0; } @@ -569,6 +513,31 @@ static int __ath9k_hw_init(struct ath_hw *ah) ath9k_hw_read_revisions(ah); + switch (ah->hw_version.macVersion) { + case AR_SREV_VERSION_5416_PCI: + case AR_SREV_VERSION_5416_PCIE: + case AR_SREV_VERSION_9160: + case AR_SREV_VERSION_9100: + case AR_SREV_VERSION_9280: + case AR_SREV_VERSION_9285: + case AR_SREV_VERSION_9287: + case AR_SREV_VERSION_9271: + case AR_SREV_VERSION_9300: + case AR_SREV_VERSION_9330: + case AR_SREV_VERSION_9485: + case AR_SREV_VERSION_9340: + case AR_SREV_VERSION_9462: + case AR_SREV_VERSION_9550: + case AR_SREV_VERSION_9565: + case AR_SREV_VERSION_9531: + break; + default: + ath_err(common, + "Mac Chip Rev 0x%02x.%x is not supported by this driver\n", + ah->hw_version.macVersion, ah->hw_version.macRev); + return -EOPNOTSUPP; + } + /* * Read back AR_WA into a permanent copy and set bits 14 and 17. * We need to do this to avoid RMW of this register. We cannot @@ -602,50 +571,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) return -EIO; } - if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) { - if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || - ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) && - !ah->is_pciexpress)) { - ah->config.serialize_regmode = - SER_REG_MODE_ON; - } else { - ah->config.serialize_regmode = - SER_REG_MODE_OFF; - } - } - - ath_dbg(common, RESET, "serialize_regmode is %d\n", - ah->config.serialize_regmode); - - if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) - ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1; - else - ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD; - - switch (ah->hw_version.macVersion) { - case AR_SREV_VERSION_5416_PCI: - case AR_SREV_VERSION_5416_PCIE: - case AR_SREV_VERSION_9160: - case AR_SREV_VERSION_9100: - case AR_SREV_VERSION_9280: - case AR_SREV_VERSION_9285: - case AR_SREV_VERSION_9287: - case AR_SREV_VERSION_9271: - case AR_SREV_VERSION_9300: - case AR_SREV_VERSION_9330: - case AR_SREV_VERSION_9485: - case AR_SREV_VERSION_9340: - case AR_SREV_VERSION_9462: - case AR_SREV_VERSION_9550: - case AR_SREV_VERSION_9565: - break; - default: - ath_err(common, - "Mac Chip Rev 0x%02x.%x is not supported by this driver\n", - ah->hw_version.macVersion, ah->hw_version.macRev); - return -EOPNOTSUPP; - } - if (AR_SREV_9271(ah) || AR_SREV_9100(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || AR_SREV_9550(ah)) ah->is_pciexpress = false; @@ -653,10 +578,6 @@ static int __ath9k_hw_init(struct ath_hw *ah) ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID); ath9k_hw_init_cal_settings(ah); - ah->ani_function = ATH9K_ANI_ALL; - if (!AR_SREV_9300_20_OR_LATER(ah)) - ah->ani_function &= ~ATH9K_ANI_MRC_CCK; - if (!ah->is_pciexpress) ath9k_hw_disablepcie(ah); @@ -675,15 +596,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) return r; } - if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) - ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S); - else - ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); - - if (AR_SREV_9330(ah)) - ah->bb_watchdog_timeout_ms = 85; - else - ah->bb_watchdog_timeout_ms = 25; + ath9k_hw_init_hang_checks(ah); common->state = ATH_HW_INITIALIZED; @@ -716,6 +629,7 @@ int ath9k_hw_init(struct ath_hw *ah) case AR9300_DEVID_AR9462: case AR9485_DEVID_AR1111: case AR9300_DEVID_AR9565: + case AR9300_DEVID_AR953X: break; default: if (common->bus_ops->ath_bus_type == ATH_USB) @@ -851,7 +765,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, /* program BB PLL phase_shift */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3, AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1); - } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) { + } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { u32 regval, pll2_divint, pll2_divfrac, refdiv; REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c); @@ -861,9 +775,15 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, udelay(100); if (ah->is_clk_25mhz) { - pll2_divint = 0x54; - pll2_divfrac = 0x1eb85; - refdiv = 3; + if (AR_SREV_9531(ah)) { + pll2_divint = 0x1c; + pll2_divfrac = 0xa3d2; + refdiv = 1; + } else { + pll2_divint = 0x54; + pll2_divfrac = 0x1eb85; + refdiv = 3; + } } else { if (AR_SREV_9340(ah)) { pll2_divint = 88; @@ -877,7 +797,10 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, } regval = REG_READ(ah, AR_PHY_PLL_MODE); - regval |= (0x1 << 16); + if (AR_SREV_9531(ah)) + regval |= (0x1 << 22); + else + regval |= (0x1 << 16); REG_WRITE(ah, AR_PHY_PLL_MODE, regval); udelay(100); @@ -887,14 +810,33 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, regval = REG_READ(ah, AR_PHY_PLL_MODE); if (AR_SREV_9340(ah)) - regval = (regval & 0x80071fff) | (0x1 << 30) | - (0x1 << 13) | (0x4 << 26) | (0x18 << 19); + regval = (regval & 0x80071fff) | + (0x1 << 30) | + (0x1 << 13) | + (0x4 << 26) | + (0x18 << 19); + else if (AR_SREV_9531(ah)) + regval = (regval & 0x01c00fff) | + (0x1 << 31) | + (0x2 << 29) | + (0xa << 25) | + (0x1 << 19) | + (0x6 << 12); else - regval = (regval & 0x80071fff) | (0x3 << 30) | - (0x1 << 13) | (0x4 << 26) | (0x60 << 19); + regval = (regval & 0x80071fff) | + (0x3 << 30) | + (0x1 << 13) | + (0x4 << 26) | + (0x60 << 19); REG_WRITE(ah, AR_PHY_PLL_MODE, regval); - REG_WRITE(ah, AR_PHY_PLL_MODE, - REG_READ(ah, AR_PHY_PLL_MODE) & 0xfffeffff); + + if (AR_SREV_9531(ah)) + REG_WRITE(ah, AR_PHY_PLL_MODE, + REG_READ(ah, AR_PHY_PLL_MODE) & 0xffbfffff); + else + REG_WRITE(ah, AR_PHY_PLL_MODE, + REG_READ(ah, AR_PHY_PLL_MODE) & 0xfffeffff); + udelay(1000); } @@ -941,7 +883,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)) { @@ -1030,7 +972,6 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) void ath9k_hw_init_global_settings(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; const struct ath9k_channel *chan = ah->curchan; int acktimeout, ctstimeout, ack_offset = 0; int slottime; @@ -1105,8 +1046,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ - if (conf->chandef.chan && - conf->chandef.chan->band == IEEE80211_BAND_2GHZ && + if (IS_CHAN_2GHZ(chan) && !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) { acktimeout += 64 - sifstime - ah->slottime; ctstimeout += 48 - sifstime - ah->slottime; @@ -1148,9 +1088,7 @@ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan) { u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band); - if (IS_CHAN_B(chan)) - ctl |= CTL_11B; - else if (IS_CHAN_G(chan)) + if (IS_CHAN_2GHZ(chan)) ctl |= CTL_11G; else ctl |= CTL_11A; @@ -1278,6 +1216,42 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, *coef_exponent = coef_exp - 16; } +/* AR9330 WAR: + * call external reset function to reset WMAC if: + * - doing a cold reset + * - we have pending frames in the TX queues. + */ +static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type) +{ + int i, npend = 0; + + for (i = 0; i < AR_NUM_QCU; i++) { + npend = ath9k_hw_numtxpending(ah, i); + if (npend) + break; + } + + if (ah->external_reset && + (npend || type == ATH9K_RESET_COLD)) { + int reset_err = 0; + + ath_dbg(ath9k_hw_common(ah), RESET, + "reset MAC via external reset\n"); + + reset_err = ah->external_reset(); + if (reset_err) { + ath_err(ath9k_hw_common(ah), + "External reset failed, err=%d\n", + reset_err); + return false; + } + + REG_WRITE(ah, AR_RTC_RESET, 1); + } + + return true; +} + static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) { u32 rst_flags; @@ -1328,38 +1302,8 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) } if (AR_SREV_9330(ah)) { - int npend = 0; - int i; - - /* AR9330 WAR: - * call external reset function to reset WMAC if: - * - doing a cold reset - * - we have pending frames in the TX queues - */ - - for (i = 0; i < AR_NUM_QCU; i++) { - npend = ath9k_hw_numtxpending(ah, i); - if (npend) - break; - } - - if (ah->external_reset && - (npend || type == ATH9K_RESET_COLD)) { - int reset_err = 0; - - ath_dbg(ath9k_hw_common(ah), RESET, - "reset MAC via external reset\n"); - - reset_err = ah->external_reset(); - if (reset_err) { - ath_err(ath9k_hw_common(ah), - "External reset failed, err=%d\n", - reset_err); - return false; - } - - REG_WRITE(ah, AR_RTC_RESET, 1); - } + if (!ath9k_hw_ar9330_reset_war(ah, type)) + return false; } if (ath9k_hw_mci_is_enabled(ah)) @@ -1369,7 +1313,12 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) REGWRITE_BUFFER_FLUSH(ah); - udelay(50); + if (AR_SREV_9300_20_OR_LATER(ah)) + udelay(50); + else if (AR_SREV_9100(ah)) + mdelay(10); + else + udelay(100); REG_WRITE(ah, AR_RTC_RC, 0); if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { @@ -1405,8 +1354,7 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) REGWRITE_BUFFER_FLUSH(ah); - if (!AR_SREV_9300_20_OR_LATER(ah)) - udelay(2); + udelay(2); if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RC, 0); @@ -1482,7 +1430,6 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah, if (AR_SREV_9330(ah)) ar9003_hw_internal_regulator_apply(ah); ath9k_hw_init_pll(ah, chan); - ath9k_hw_set_rfmode(ah, chan); return true; } @@ -1498,10 +1445,9 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, int r; if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) { - u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); - u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); - band_switch = (cur != new); - mode_diff = (chan->chanmode != ah->curchan->chanmode); + u32 flags_diff = chan->channelFlags ^ ah->curchan->channelFlags; + band_switch = !!(flags_diff & CHANNEL_5GHZ); + mode_diff = !!(flags_diff & ~CHANNEL_HT); } for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { @@ -1540,9 +1486,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, ath9k_hw_set_clockrate(ah); ath9k_hw_apply_txpower(ah, chan, false); - if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) - ath9k_hw_set_delta_slope(ah, chan); - + ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); if (band_switch || ini_reloaded) @@ -1574,80 +1518,23 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah) } } -static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states, - int *hang_state, int *hang_pos) +void ath9k_hw_check_nav(struct ath_hw *ah) { - static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */ - u32 chain_state, dcs_pos, i; - - for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) { - chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f; - for (i = 0; i < 3; i++) { - if (chain_state == dcu_chain_state[i]) { - *hang_state = chain_state; - *hang_pos = dcs_pos; - return true; - } - } - } - return false; -} - -#define DCU_COMPLETE_STATE 1 -#define DCU_COMPLETE_STATE_MASK 0x3 -#define NUM_STATUS_READS 50 -static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah) -{ - u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4; - u32 i, hang_pos, hang_state, num_state = 6; - - comp_state = REG_READ(ah, AR_DMADBG_6); - - if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) { - ath_dbg(ath9k_hw_common(ah), RESET, - "MAC Hang signature not found at DCU complete\n"); - return false; - } - - chain_state = REG_READ(ah, dcs_reg); - if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos)) - goto hang_check_iter; - - dcs_reg = AR_DMADBG_5; - num_state = 4; - chain_state = REG_READ(ah, dcs_reg); - if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos)) - goto hang_check_iter; - - ath_dbg(ath9k_hw_common(ah), RESET, - "MAC Hang signature 1 not found\n"); - return false; - -hang_check_iter: - ath_dbg(ath9k_hw_common(ah), RESET, - "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n", - chain_state, comp_state, hang_state, hang_pos); - - for (i = 0; i < NUM_STATUS_READS; i++) { - chain_state = REG_READ(ah, dcs_reg); - chain_state = (chain_state >> (5 * hang_pos)) & 0x1f; - comp_state = REG_READ(ah, AR_DMADBG_6); + struct ath_common *common = ath9k_hw_common(ah); + u32 val; - if (((comp_state & DCU_COMPLETE_STATE_MASK) != - DCU_COMPLETE_STATE) || - (chain_state != hang_state)) - return false; + val = REG_READ(ah, AR_NAV); + if (val != 0xdeadbeef && val > 0x7fff) { + ath_dbg(common, BSTUCK, "Abnormal NAV: 0x%x\n", val); + REG_WRITE(ah, AR_NAV, 0); } - - ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n"); - - return true; } +EXPORT_SYMBOL(ath9k_hw_check_nav); bool ath9k_hw_check_alive(struct ath_hw *ah) { int count = 50; - u32 reg; + u32 reg, last_val; if (AR_SREV_9300(ah)) return !ath9k_hw_detect_mac_hang(ah); @@ -1655,9 +1542,14 @@ bool ath9k_hw_check_alive(struct ath_hw *ah) if (AR_SREV_9285_12_OR_LATER(ah)) return true; + last_val = REG_READ(ah, AR_OBS_BUS_1); do { reg = REG_READ(ah, AR_OBS_BUS_1); + if (reg != last_val) + return true; + udelay(1); + last_val = reg; if ((reg & 0x7E7FFFEF) == 0x00702400) continue; @@ -1705,7 +1597,6 @@ static void ath9k_hw_reset_opmode(struct ath_hw *ah, REG_RMW(ah, AR_STA_ID1, macStaId1 | AR_STA_ID1_RTS_USE_DEF - | (ah->config.ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0) | ah->sta_id1_defaults, ~AR_STA_ID1_SADH_MASK); ath_hw_setbssidmask(common); @@ -1764,7 +1655,7 @@ static void ath9k_hw_init_desc(struct ath_hw *ah) } #ifdef __BIG_ENDIAN else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) || - AR_SREV_9550(ah)) + AR_SREV_9550(ah) || AR_SREV_9531(ah)) REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0); else REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); @@ -1799,20 +1690,11 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) goto fail; /* - * If cross-band fcc is not supoprted, bail out if - * either channelFlags or chanmode differ. - * - * chanmode will be different if the HT operating mode - * changes because of CSA. + * If cross-band fcc is not supoprted, bail out if channelFlags differ. */ - if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) { - if ((chan->channelFlags & CHANNEL_ALL) != - (ah->curchan->channelFlags & CHANNEL_ALL)) - goto fail; - - if (chan->chanmode != ah->curchan->chanmode) - goto fail; - } + if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) && + ((chan->channelFlags ^ ah->curchan->channelFlags) & ~CHANNEL_HT)) + goto fail; if (!ath9k_hw_check_alive(ah)) goto fail; @@ -1822,9 +1704,9 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) * re-using are present. */ if (AR_SREV_9462(ah) && (ah->caldata && - (!ah->caldata->done_txiqcal_once || - !ah->caldata->done_txclcal_once || - !ah->caldata->rtt_done))) + (!test_bit(TXIQCAL_DONE, &ah->caldata->cal_flags) || + !test_bit(TXCLCAL_DONE, &ah->caldata->cal_flags) || + !test_bit(RTT_DONE, &ah->caldata->cal_flags)))) goto fail; ath_dbg(common, RESET, "FastChannelChange for %d -> %d\n", @@ -1852,10 +1734,12 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata, bool fastcc) { struct ath_common *common = ath9k_hw_common(ah); + struct timespec ts; u32 saveLedState; u32 saveDefAntenna; u32 macStaId1; u64 tsf = 0; + s64 usec = 0; int r; bool start_mci_reset = false; bool save_fullsleep = ah->chip_fullsleep; @@ -1874,15 +1758,14 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ah->caldata = caldata; if (caldata && (chan->channel != caldata->channel || - chan->channelFlags != caldata->channelFlags || - chan->chanmode != caldata->chanmode)) { + chan->channelFlags != caldata->channelFlags)) { /* Operating channel changed, reset channel calibration data */ memset(caldata, 0, sizeof(*caldata)); ath9k_init_nfcal_hist_buffer(ah, chan); } else if (caldata) { - caldata->paprd_packet_sent = false; + clear_bit(PAPRD_PACKET_SENT, &caldata->cal_flags); } - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); if (fastcc) { r = ath9k_hw_do_fastcc(ah, chan); @@ -1899,10 +1782,10 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, macStaId1 = REG_READ(ah, AR_STA_ID1) & AR_STA_ID1_BASE_RATE_11B; - /* For chips on which RTC reset is done, save TSF before it gets cleared */ - if (AR_SREV_9100(ah) || - (AR_SREV_9280(ah) && ah->eep_ops->get_eeprom(ah, EEP_OL_PWRCTRL))) - tsf = ath9k_hw_gettsf64(ah); + /* Save TSF before chip reset, a cold reset clears it */ + tsf = ath9k_hw_gettsf64(ah); + getrawmonotonic(&ts); + usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000; saveLedState = REG_READ(ah, AR_CFG_LED) & (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL | @@ -1935,8 +1818,9 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } /* Restore TSF */ - if (tsf) - ath9k_hw_settsf64(ah, tsf); + getrawmonotonic(&ts); + usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec; + ath9k_hw_settsf64(ah, tsf + usec); if (AR_SREV_9280_20_OR_LATER(ah)) REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE); @@ -1948,6 +1832,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (r) return r; + ath9k_hw_set_rfmode(ah, chan); + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep); @@ -1964,9 +1850,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_mfp(ah); - if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) - ath9k_hw_set_delta_slope(ah, chan); - + ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); ah->eep_ops->set_board_values(ah, chan); @@ -2005,8 +1889,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, REG_WRITE(ah, AR_OBS, 8); if (ah->config.rx_intr_mitigation) { - REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); - REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, 2000); + REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, ah->config.rimt_last); + REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_FIRST, ah->config.rimt_first); } if (ah->config.tx_intr_mitigation) { @@ -2017,8 +1901,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_bb(ah, chan); if (caldata) { - caldata->done_txiqcal_once = false; - caldata->done_txclcal_once = false; + clear_bit(TXIQCAL_DONE, &caldata->cal_flags); + clear_bit(TXCLCAL_DONE, &caldata->cal_flags); } if (!ath9k_hw_init_cal(ah, chan)) return -EIO; @@ -2044,10 +1928,11 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); - if (AR_SREV_9300_20_OR_LATER(ah)) { + if (AR_SREV_9300_20_OR_LATER(ah)) ar9003_hw_bb_watchdog_config(ah); + + if (ah->config.hw_hang_checks & HW_PHYRESTART_CLC_WAR) ar9003_hw_disable_phy_restart(ah); - } ath9k_hw_apply_gpio_override(ah); @@ -2171,7 +2056,10 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah) REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN); - udelay(50); + if (AR_SREV_9100(ah)) + mdelay(10); + else + udelay(50); for (i = POWER_UP_TIME / 50; i > 0; i--) { val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M; @@ -2260,9 +2148,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) case NL80211_IFTYPE_ADHOC: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); - REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + - TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); - flags |= AR_NDP_TIMER_EN; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); @@ -2283,7 +2168,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period); REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period); REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period); - REG_WRITE(ah, AR_NDP_PERIOD, beacon_period); REGWRITE_BUFFER_FLUSH(ah); @@ -2300,12 +2184,9 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); - - REG_WRITE(ah, AR_BEACON_PERIOD, - TU_TO_USEC(bs->bs_intval)); - REG_WRITE(ah, AR_DMA_BEACON_PERIOD, - TU_TO_USEC(bs->bs_intval)); + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, bs->bs_nexttbtt); + REG_WRITE(ah, AR_BEACON_PERIOD, bs->bs_intval); + REG_WRITE(ah, AR_DMA_BEACON_PERIOD, bs->bs_intval); REGWRITE_BUFFER_FLUSH(ah); @@ -2333,9 +2214,8 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_NEXT_DTIM, - TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); - REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); + REG_WRITE(ah, AR_NEXT_DTIM, bs->bs_nextdtim - SLEEP_SLOP); + REG_WRITE(ah, AR_NEXT_TIM, nextTbtt - SLEEP_SLOP); REG_WRITE(ah, AR_SLEEP1, SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) @@ -2349,8 +2229,8 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, REG_WRITE(ah, AR_SLEEP2, SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT)); - REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval)); - REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); + REG_WRITE(ah, AR_TIM_PERIOD, beaconintval); + REG_WRITE(ah, AR_DTIM_PERIOD, dtimperiod); REGWRITE_BUFFER_FLUSH(ah); @@ -2608,13 +2488,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) pCap->hw_caps |= ATH9K_HW_CAP_PAPRD; - /* - * Fast channel change across bands is available - * only for AR9462 and AR9565. - */ - if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) - pCap->hw_caps |= ATH9K_HW_CAP_FCC_BAND_SWITCH; - return 0; } @@ -2943,12 +2816,11 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set) } EXPORT_SYMBOL(ath9k_hw_set_tsfadjust); -void ath9k_hw_set11nmac2040(struct ath_hw *ah) +void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan) { - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; u32 macmode; - if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca) + if (IS_CHAN_HT40(chan) && !ah->config.cwm_ignore_extcca) macmode = AR_2040_JOINED_RX_CLEAR; else macmode = 0; @@ -2987,20 +2859,6 @@ static const struct ath_gen_timer_configuration gen_tmr_configuration[] = /* HW generic timer primitives */ -/* compute and clear index of rightmost 1 */ -static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask) -{ - u32 b; - - b = *mask; - b &= (0-b); - *mask &= ~b; - b *= debruijn32; - b >>= 27; - - return timer_table->gen_timer_index[b]; -} - u32 ath9k_hw_gettsf32(struct ath_hw *ah) { return REG_READ(ah, AR_TSF_L32); @@ -3016,6 +2874,10 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; + if ((timer_index < AR_FIRST_NDP_TIMER) || + (timer_index >= ATH_MAX_GEN_TIMER)) + return NULL; + timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); if (timer == NULL) return NULL; @@ -3033,23 +2895,13 @@ EXPORT_SYMBOL(ath_gen_timer_alloc); void ath9k_hw_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, - u32 trig_timeout, + u32 timer_next, u32 timer_period) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - u32 tsf, timer_next; - - BUG_ON(!timer_period); + u32 mask = 0; - set_bit(timer->index, &timer_table->timer_mask.timer_bits); - - tsf = ath9k_hw_gettsf32(ah); - - timer_next = tsf + trig_timeout; - - ath_dbg(ath9k_hw_common(ah), BTCOEX, - "current tsf %x period %x timer_next %x\n", - tsf, timer_period, timer_next); + timer_table->timer_mask |= BIT(timer->index); /* * Program generic timer registers @@ -3075,10 +2927,19 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah, (1 << timer->index)); } - /* Enable both trigger and thresh interrupt masks */ - REG_SET_BIT(ah, AR_IMR_S5, - (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | - SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); + if (timer->trigger) + mask |= SM(AR_GENTMR_BIT(timer->index), + AR_IMR_S5_GENTIMER_TRIG); + if (timer->overflow) + mask |= SM(AR_GENTMR_BIT(timer->index), + AR_IMR_S5_GENTIMER_THRESH); + + REG_SET_BIT(ah, AR_IMR_S5, mask); + + if ((ah->imask & ATH9K_INT_GENTIMER) == 0) { + ah->imask |= ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah); + } } EXPORT_SYMBOL(ath9k_hw_gen_timer_start); @@ -3086,11 +2947,6 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - if ((timer->index < AR_FIRST_NDP_TIMER) || - (timer->index >= ATH_MAX_GEN_TIMER)) { - return; - } - /* Clear generic timer enable bits. */ REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, gen_tmr_configuration[timer->index].mode_mask); @@ -3110,7 +2966,12 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); - clear_bit(timer->index, &timer_table->timer_mask.timer_bits); + timer_table->timer_mask &= ~BIT(timer->index); + + if (timer_table->timer_mask == 0) { + ah->imask &= ~ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah); + } } EXPORT_SYMBOL(ath9k_hw_gen_timer_stop); @@ -3131,32 +2992,32 @@ void ath_gen_timer_isr(struct ath_hw *ah) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; - struct ath_common *common = ath9k_hw_common(ah); - u32 trigger_mask, thresh_mask, index; + unsigned long trigger_mask, thresh_mask; + unsigned int index; /* get hardware generic timer interrupt status */ trigger_mask = ah->intr_gen_timer_trigger; thresh_mask = ah->intr_gen_timer_thresh; - trigger_mask &= timer_table->timer_mask.val; - thresh_mask &= timer_table->timer_mask.val; + trigger_mask &= timer_table->timer_mask; + thresh_mask &= timer_table->timer_mask; - trigger_mask &= ~thresh_mask; - - while (thresh_mask) { - index = rightmost_index(timer_table, &thresh_mask); + for_each_set_bit(index, &thresh_mask, ARRAY_SIZE(timer_table->timers)) { timer = timer_table->timers[index]; - BUG_ON(!timer); - ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n", - index); + if (!timer) + continue; + if (!timer->overflow) + continue; + + trigger_mask &= ~BIT(index); timer->overflow(timer->arg); } - while (trigger_mask) { - index = rightmost_index(timer_table, &trigger_mask); + for_each_set_bit(index, &trigger_mask, ARRAY_SIZE(timer_table->timers)) { timer = timer_table->timers[index]; - BUG_ON(!timer); - ath_dbg(common, BTCOEX, - "Gen timer[%d] trigger\n", index); + if (!timer) + continue; + if (!timer->trigger) + continue; timer->trigger(timer->arg); } } @@ -3187,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 */ @@ -3240,19 +3102,19 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len) /* chipsets >= AR9280 are single-chip */ if (AR_SREV_9280_20_OR_LATER(ah)) { - used = snprintf(hw_name, len, - "Atheros AR%s Rev:%x", - ath9k_hw_mac_bb_name(ah->hw_version.macVersion), - ah->hw_version.macRev); + used = scnprintf(hw_name, len, + "Atheros AR%s Rev:%x", + ath9k_hw_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev); } else { - used = snprintf(hw_name, len, - "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x", - ath9k_hw_mac_bb_name(ah->hw_version.macVersion), - ah->hw_version.macRev, - ath9k_hw_rf_name((ah->hw_version.analog5GhzRev & - AR_RADIO_SREV_MAJOR)), - ah->hw_version.phyRev); + used = scnprintf(hw_name, len, + "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x", + ath9k_hw_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev, + ath9k_hw_rf_name((ah->hw_version.analog5GhzRev + & AR_RADIO_SREV_MAJOR)), + ah->hw_version.phyRev); } hw_name[used] = '\0'; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 69a907b55a7..0acd4b5a489 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -52,6 +52,7 @@ #define AR9300_DEVID_QCA955X 0x0038 #define AR9485_DEVID_AR1111 0x0037 #define AR9300_DEVID_AR9565 0x0036 +#define AR9300_DEVID_AR953X 0x003d #define AR5416_AR9100_DEVID 0x000b @@ -98,8 +99,8 @@ #define PR_EEP(_s, _val) \ do { \ - len += snprintf(buf + len, size - len, "%20s : %10d\n", \ - _s, (_val)); \ + len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ + _s, (_val)); \ } while (0) #define SM(_v, _f) (((_v) << _f##_S) & _f) @@ -168,7 +169,7 @@ #define CAB_TIMEOUT_VAL 10 #define BEACON_TIMEOUT_VAL 10 #define MIN_BEACON_TIMEOUT_VAL 1 -#define SLEEP_SLOP 3 +#define SLEEP_SLOP TU_TO_USEC(3) #define INIT_CONFIG_STATUS 0x00000000 #define INIT_RSSI_THR 0x00000700 @@ -277,14 +278,25 @@ struct ath9k_hw_capabilities { u8 txs_len; }; +#define AR_NO_SPUR 0x8000 +#define AR_BASE_FREQ_2GHZ 2300 +#define AR_BASE_FREQ_5GHZ 4900 +#define AR_SPUR_FEEQ_BOUND_HT40 19 +#define AR_SPUR_FEEQ_BOUND_HT20 10 + +enum ath9k_hw_hang_checks { + HW_BB_WATCHDOG = BIT(0), + HW_PHYRESTART_CLC_WAR = BIT(1), + HW_BB_RIFS_HANG = BIT(2), + HW_BB_DFS_HANG = BIT(3), + HW_BB_RX_CLEAR_STUCK_HANG = BIT(4), + HW_MAC_HANG = BIT(5), +}; + struct ath9k_ops_config { int dma_beacon_response_time; int sw_beacon_response_time; - int additional_swba_backoff; - int ack_6mb; u32 cwm_ignore_extcca; - bool pcieSerDesWrite; - u8 pcie_clock_req; u32 pcie_waen; u8 analog_shiftreg; u32 ofdm_trig_low; @@ -295,20 +307,11 @@ struct ath9k_ops_config { int serialize_regmode; bool rx_intr_mitigation; bool tx_intr_mitigation; -#define SPUR_DISABLE 0 -#define SPUR_ENABLE_IOCTL 1 -#define SPUR_ENABLE_EEPROM 2 -#define AR_SPUR_5413_1 1640 -#define AR_SPUR_5413_2 1200 -#define AR_NO_SPUR 0x8000 -#define AR_BASE_FREQ_2GHZ 2300 -#define AR_BASE_FREQ_5GHZ 4900 -#define AR_SPUR_FEEQ_BOUND_HT40 19 -#define AR_SPUR_FEEQ_BOUND_HT20 10 - int spurmode; - u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; u8 max_txtrig_level; u16 ani_poll_interval; /* ANI poll interval in ms */ + u16 hw_hang_checks; + u16 rimt_first; + u16 rimt_last; /* Platform specific config */ u32 aspm_l1_fix; @@ -316,6 +319,8 @@ struct ath9k_ops_config { u32 ant_ctrl_comm2g_switch_enable; bool xatten_margin_cfg; bool alt_mingainidx; + bool no_pll_pwrsave; + bool tx_gain_buffalo; }; enum ath9k_int { @@ -369,55 +374,30 @@ enum ath9k_int { ATH9K_INT_NOCARD = 0xffffffff }; -#define CHANNEL_CCK 0x00020 -#define CHANNEL_OFDM 0x00040 -#define CHANNEL_2GHZ 0x00080 -#define CHANNEL_5GHZ 0x00100 -#define CHANNEL_PASSIVE 0x00200 -#define CHANNEL_DYN 0x00400 -#define CHANNEL_HALF 0x04000 -#define CHANNEL_QUARTER 0x08000 -#define CHANNEL_HT20 0x10000 -#define CHANNEL_HT40PLUS 0x20000 -#define CHANNEL_HT40MINUS 0x40000 - -#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) -#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) -#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) -#define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20) -#define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20) -#define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS) -#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS) -#define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS) -#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS) -#define CHANNEL_ALL \ - (CHANNEL_OFDM| \ - CHANNEL_CCK| \ - CHANNEL_2GHZ | \ - CHANNEL_5GHZ | \ - CHANNEL_HT20 | \ - CHANNEL_HT40PLUS | \ - CHANNEL_HT40MINUS) - #define MAX_RTT_TABLE_ENTRY 6 #define MAX_IQCAL_MEASUREMENT 8 #define MAX_CL_TAB_ENTRY 16 #define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j)) +enum ath9k_cal_flags { + RTT_DONE, + PAPRD_PACKET_SENT, + PAPRD_DONE, + NFCAL_PENDING, + NFCAL_INTF, + TXIQCAL_DONE, + TXCLCAL_DONE, + SW_PKDET_DONE, +}; + struct ath9k_hw_cal_data { u16 channel; - u32 channelFlags; - u32 chanmode; + u16 channelFlags; + unsigned long cal_flags; int32_t CalValid; int8_t iCoff; int8_t qCoff; - bool rtt_done; - bool paprd_packet_sent; - bool paprd_done; - bool nfcal_pending; - bool nfcal_interference; - bool done_txiqcal_once; - bool done_txclcal_once; + u8 caldac[2]; u16 small_signal_gain[AR9300_MAX_CHAINS]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; u32 num_measures[AR9300_MAX_CHAINS]; @@ -430,33 +410,34 @@ struct ath9k_hw_cal_data { struct ath9k_channel { struct ieee80211_channel *chan; u16 channel; - u32 channelFlags; - u32 chanmode; + u16 channelFlags; s16 noisefloor; }; -#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ - (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \ - (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \ - (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS)) -#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0) -#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0) -#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0) -#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0) -#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0) +#define CHANNEL_5GHZ BIT(0) +#define CHANNEL_HALF BIT(1) +#define CHANNEL_QUARTER BIT(2) +#define CHANNEL_HT BIT(3) +#define CHANNEL_HT40PLUS BIT(4) +#define CHANNEL_HT40MINUS BIT(5) + +#define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ)) +#define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c)) + +#define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF)) +#define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER)) #define IS_CHAN_A_FAST_CLOCK(_ah, _c) \ - ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \ - ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)) - -/* These macros check chanmode and not channelFlags */ -#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B) -#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \ - ((_c)->chanmode == CHANNEL_G_HT20)) -#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \ - ((_c)->chanmode == CHANNEL_A_HT40MINUS) || \ - ((_c)->chanmode == CHANNEL_G_HT40PLUS) || \ - ((_c)->chanmode == CHANNEL_G_HT40MINUS)) -#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c))) + (IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)) + +#define IS_CHAN_HT(_c) ((_c)->channelFlags & CHANNEL_HT) + +#define IS_CHAN_HT20(_c) (IS_CHAN_HT(_c) && !IS_CHAN_HT40(_c)) + +#define IS_CHAN_HT40(_c) \ + (!!((_c)->channelFlags & (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS))) + +#define IS_CHAN_HT40PLUS(_c) ((_c)->channelFlags & CHANNEL_HT40PLUS) +#define IS_CHAN_HT40MINUS(_c) ((_c)->channelFlags & CHANNEL_HT40MINUS) enum ath9k_power_mode { ATH9K_PM_AWAKE = 0, @@ -483,10 +464,6 @@ struct ath9k_beacon_state { u32 bs_intval; #define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */ u32 bs_dtimperiod; - u16 bs_cfpperiod; - u16 bs_cfpmaxduration; - u32 bs_cfpnext; - u16 bs_timoffset; u16 bs_bmissthreshold; u32 bs_sleepduration; u32 bs_tsfoor_threshold; @@ -522,12 +499,6 @@ struct ath9k_hw_version { #define AR_GENTMR_BIT(_index) (1 << (_index)) -/* - * Using de Bruijin sequence to look up 1's index in a 32 bit number - * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001 - */ -#define debruijn32 0x077CB531U - struct ath_gen_timer_configuration { u32 next_addr; u32 period_addr; @@ -543,12 +514,8 @@ struct ath_gen_timer { }; struct ath_gen_timer_table { - u32 gen_timer_index[32]; struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER]; - union { - unsigned long timer_bits; - u16 val; - } timer_mask; + u16 timer_mask; }; struct ath_hw_antcomb_conf { @@ -558,6 +525,7 @@ struct ath_hw_antcomb_conf { u8 main_gaintb; u8 alt_gaintb; int lna1_lna2_delta; + int lna1_lna2_switch_delta; u8 div_group; }; @@ -618,6 +586,10 @@ struct ath_hw_radar_conf { * register settings through the register initialization. */ struct ath_hw_private_ops { + void (*init_hang_checks)(struct ath_hw *ah); + bool (*detect_mac_hang)(struct ath_hw *ah); + bool (*detect_bb_hang)(struct ath_hw *ah); + /* Calibration ops */ void (*init_cal_settings)(struct ath_hw *ah); bool (*init_cal)(struct ath_hw *ah, struct ath9k_channel *chan); @@ -712,7 +684,8 @@ struct ath_hw_ops { struct ath9k_channel *chan, u8 rxchainmask, bool longcal); - bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked); + bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p); void (*set_txdesc)(struct ath_hw *ah, void *ds, struct ath_tx_info *i); int (*proc_txdesc)(struct ath_hw *ah, void *ds, @@ -726,6 +699,10 @@ struct ath_hw_ops { void (*spectral_scan_trigger)(struct ath_hw *ah); void (*spectral_scan_wait)(struct ath_hw *ah); + void (*tx99_start)(struct ath_hw *ah, u32 qnum); + void (*tx99_stop)(struct ath_hw *ah); + void (*tx99_set_txpower)(struct ath_hw *ah, u8 power); + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable); #endif @@ -804,7 +781,6 @@ struct ath_hw { u32 txurn_interrupt_mask; atomic_t intr_ref_cnt; bool chip_fullsleep; - u32 atim_window; u32 modes_index; /* Calibration */ @@ -883,6 +859,7 @@ struct ath_hw { u32 gpio_mask; u32 gpio_val; + struct ar5416IniArray ini_dfs; struct ar5416IniArray iniModes; struct ar5416IniArray iniCommon; struct ar5416IniArray iniBB_RfGain; @@ -939,7 +916,7 @@ struct ath_hw { /* Enterprise mode cap */ u32 ent_mode; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_ATH9K_WOW u32 wow_event_mask; #endif bool is_clk_25mhz; @@ -1026,21 +1003,15 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set); void ath9k_hw_init_global_settings(struct ath_hw *ah); u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah); -void ath9k_hw_set11nmac2040(struct ath_hw *ah); +void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs); +void ath9k_hw_check_nav(struct ath_hw *ah); bool ath9k_hw_check_alive(struct ath_hw *ah); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); -#ifdef CONFIG_ATH9K_DEBUGFS -void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause); -#else -static inline void ath9k_debug_sync_cause(struct ath_common *common, - u32 sync_cause) {} -#endif - /* Generic hw timer primitives */ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*trigger)(void *), @@ -1075,6 +1046,7 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah); * Code specific to AR9003, we stuff these here to avoid callbacks * for older families */ +bool ar9003_hw_bb_watchdog_check(struct ath_hw *ah); void ar9003_hw_bb_watchdog_config(struct ath_hw *ah); void ar9003_hw_bb_watchdog_read(struct ath_hw *ah); void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah); @@ -1144,7 +1116,7 @@ ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_ATH9K_WOW const char *ath9k_hw_wow_event_to_string(u32 wow_event); void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, u8 *user_mask, int pattern_count, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 9a1f349f926..0246b990fe8 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -57,112 +57,15 @@ static int ath9k_bt_ant_diversity; module_param_named(bt_ant_diversity, ath9k_bt_ant_diversity, int, 0444); MODULE_PARM_DESC(bt_ant_diversity, "Enable WLAN/BT RX antenna diversity"); -bool is_ath9k_unloaded; -/* We use the hw_value as an index into our private channel structure */ - -#define CHAN2G(_freq, _idx) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -#define CHAN5G(_freq, _idx) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -/* Some 2 GHz radios are actually tunable on 2312-2732 - * on 5 MHz steps, we support the channels which we know - * we have calibration data for all cards though to make - * this static */ -static const struct ieee80211_channel ath9k_2ghz_chantable[] = { - CHAN2G(2412, 0), /* Channel 1 */ - CHAN2G(2417, 1), /* Channel 2 */ - CHAN2G(2422, 2), /* Channel 3 */ - CHAN2G(2427, 3), /* Channel 4 */ - CHAN2G(2432, 4), /* Channel 5 */ - CHAN2G(2437, 5), /* Channel 6 */ - CHAN2G(2442, 6), /* Channel 7 */ - CHAN2G(2447, 7), /* Channel 8 */ - CHAN2G(2452, 8), /* Channel 9 */ - CHAN2G(2457, 9), /* Channel 10 */ - CHAN2G(2462, 10), /* Channel 11 */ - CHAN2G(2467, 11), /* Channel 12 */ - CHAN2G(2472, 12), /* Channel 13 */ - CHAN2G(2484, 13), /* Channel 14 */ -}; +static int ath9k_ps_enable; +module_param_named(ps_enable, ath9k_ps_enable, int, 0444); +MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); -/* Some 5 GHz radios are actually tunable on XXXX-YYYY - * on 5 MHz steps, we support the channels which we know - * we have calibration data for all cards though to make - * this static */ -static const struct ieee80211_channel ath9k_5ghz_chantable[] = { - /* _We_ call this UNII 1 */ - CHAN5G(5180, 14), /* Channel 36 */ - CHAN5G(5200, 15), /* Channel 40 */ - CHAN5G(5220, 16), /* Channel 44 */ - CHAN5G(5240, 17), /* Channel 48 */ - /* _We_ call this UNII 2 */ - CHAN5G(5260, 18), /* Channel 52 */ - CHAN5G(5280, 19), /* Channel 56 */ - CHAN5G(5300, 20), /* Channel 60 */ - CHAN5G(5320, 21), /* Channel 64 */ - /* _We_ call this "Middle band" */ - CHAN5G(5500, 22), /* Channel 100 */ - CHAN5G(5520, 23), /* Channel 104 */ - CHAN5G(5540, 24), /* Channel 108 */ - CHAN5G(5560, 25), /* Channel 112 */ - CHAN5G(5580, 26), /* Channel 116 */ - CHAN5G(5600, 27), /* Channel 120 */ - CHAN5G(5620, 28), /* Channel 124 */ - CHAN5G(5640, 29), /* Channel 128 */ - CHAN5G(5660, 30), /* Channel 132 */ - CHAN5G(5680, 31), /* Channel 136 */ - CHAN5G(5700, 32), /* Channel 140 */ - /* _We_ call this UNII 3 */ - CHAN5G(5745, 33), /* Channel 149 */ - CHAN5G(5765, 34), /* Channel 153 */ - CHAN5G(5785, 35), /* Channel 157 */ - CHAN5G(5805, 36), /* Channel 161 */ - CHAN5G(5825, 37), /* Channel 165 */ -}; +static int ath9k_use_chanctx; +module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444); +MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency"); -/* Atheros hardware rate code addition for short premble */ -#define SHPCHECK(__hw_rate, __flags) \ - ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) - -#define RATE(_bitrate, _hw_rate, _flags) { \ - .bitrate = (_bitrate), \ - .flags = (_flags), \ - .hw_value = (_hw_rate), \ - .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ -} - -static struct ieee80211_rate ath9k_legacy_rates[] = { - RATE(10, 0x1b, 0), - RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(60, 0x0b, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(90, 0x0f, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(120, 0x0a, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(180, 0x0e, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(240, 0x09, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(360, 0x0d, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(480, 0x08, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), - RATE(540, 0x0c, (IEEE80211_RATE_SUPPORTS_5MHZ | - IEEE80211_RATE_SUPPORTS_10MHZ)), -}; +bool is_ath9k_unloaded; #ifdef CONFIG_MAC80211_LEDS static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = { @@ -254,64 +157,6 @@ static unsigned int ath9k_reg_rmw(void *hw_priv, u32 reg_offset, u32 set, u32 cl /* Initialization */ /**************************/ -static void setup_ht_cap(struct ath_softc *sc, - struct ieee80211_sta_ht_cap *ht_info) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - u8 tx_streams, rx_streams; - int i, max_streams; - - ht_info->ht_supported = true; - ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_SM_PS | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_LDPC) - ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_SGI_20) - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - - ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; - - if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) - max_streams = 1; - else if (AR_SREV_9462(ah)) - max_streams = 2; - else if (AR_SREV_9300_20_OR_LATER(ah)) - max_streams = 3; - else - max_streams = 2; - - if (AR_SREV_9280_20_OR_LATER(ah)) { - if (max_streams >= 2) - ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; - ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); - } - - /* set up supported mcs set */ - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - tx_streams = ath9k_cmn_count_streams(ah->txchainmask, max_streams); - rx_streams = ath9k_cmn_count_streams(ah->rxchainmask, max_streams); - - ath_dbg(common, CONFIG, "TX streams %d, RX streams: %d\n", - tx_streams, rx_streams); - - if (tx_streams != rx_streams) { - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_streams - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } - - for (i = 0; i < rx_streams; i++) - ht_info->mcs.rx_mask[i] = 0xff; - - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; -} - static void ath9k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { @@ -347,7 +192,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, { struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ds; - struct ath_buf *bf; int i, bsize, desc_len; ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", @@ -399,33 +243,68 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); /* allocate buffers */ - bsize = sizeof(struct ath_buf) * nbuf; - bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); - if (!bf) - return -ENOMEM; + if (is_tx) { + struct ath_buf *bf; + + bsize = sizeof(struct ath_buf) * nbuf; + bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + if (!bf) + return -ENOMEM; + + for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + BUG_ON((caddr_t) bf->bf_desc >= + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += (desc_len * ndesc); + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + } else { + struct ath_rxbuf *bf; + + bsize = sizeof(struct ath_rxbuf) * nbuf; + bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + if (!bf) + return -ENOMEM; - for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); - - if (!(sc->sc_ah->caps.hw_caps & - ATH9K_HW_CAP_4KB_SPLITTRANS)) { - /* - * Skip descriptor addresses which can cause 4KB - * boundary crossing (addr + length) with a 32 dword - * descriptor fetch. - */ - while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { - BUG_ON((caddr_t) bf->bf_desc >= - ((caddr_t) dd->dd_desc + - dd->dd_desc_len)); - - ds += (desc_len * ndesc); - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); + for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + BUG_ON((caddr_t) bf->bf_desc >= + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += (desc_len * ndesc); + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } } + list_add_tail(&bf->list, head); } - list_add_tail(&bf->list, head); } return 0; } @@ -436,8 +315,6 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); - - sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0); @@ -450,51 +327,6 @@ static int ath9k_init_queues(struct ath_softc *sc) return 0; } -static int ath9k_init_channels_rates(struct ath_softc *sc) -{ - void *channels; - - BUILD_BUG_ON(ARRAY_SIZE(ath9k_2ghz_chantable) + - ARRAY_SIZE(ath9k_5ghz_chantable) != - ATH9K_NUM_CHANNELS); - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) { - channels = devm_kzalloc(sc->dev, - sizeof(ath9k_2ghz_chantable), GFP_KERNEL); - if (!channels) - return -ENOMEM; - - memcpy(channels, ath9k_2ghz_chantable, - sizeof(ath9k_2ghz_chantable)); - sc->sbands[IEEE80211_BAND_2GHZ].channels = channels; - sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; - sc->sbands[IEEE80211_BAND_2GHZ].n_channels = - ARRAY_SIZE(ath9k_2ghz_chantable); - sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; - sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates); - } - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) { - channels = devm_kzalloc(sc->dev, - sizeof(ath9k_5ghz_chantable), GFP_KERNEL); - if (!channels) - return -ENOMEM; - - memcpy(channels, ath9k_5ghz_chantable, - sizeof(ath9k_5ghz_chantable)); - sc->sbands[IEEE80211_BAND_5GHZ].channels = channels; - sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; - sc->sbands[IEEE80211_BAND_5GHZ].n_channels = - ARRAY_SIZE(ath9k_5ghz_chantable); - sc->sbands[IEEE80211_BAND_5GHZ].bitrates = - ath9k_legacy_rates + 4; - sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates) - 4; - } - return 0; -} - static void ath9k_init_misc(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -502,7 +334,7 @@ static void ath9k_init_misc(struct ath_softc *sc) setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); - sc->last_rssi = ATH_RSSI_DUMMY_MARKER; + common->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->config.txpowlimit = ATH_TXPOWER_MAX; memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); sc->beacon.slottime = ATH9K_SLOT_TIME_9; @@ -521,7 +353,7 @@ static void ath9k_init_misc(struct ath_softc *sc) sc->spec_config.fft_period = 0xF; } -static void ath9k_init_platform(struct ath_softc *sc) +static void ath9k_init_pcoem_platform(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath9k_hw_capabilities *pCap = &ah->caps; @@ -547,6 +379,29 @@ static void ath9k_init_platform(struct ath_softc *sc) if (sc->driver_data & ATH9K_PCI_CUS217) ath_info(common, "CUS217 card detected\n"); + if (sc->driver_data & ATH9K_PCI_CUS252) + ath_info(common, "CUS252 card detected\n"); + + if (sc->driver_data & ATH9K_PCI_AR9565_1ANT) + ath_info(common, "WB335 1-ANT card detected\n"); + + if (sc->driver_data & ATH9K_PCI_AR9565_2ANT) + ath_info(common, "WB335 2-ANT card detected\n"); + + if (sc->driver_data & ATH9K_PCI_KILLER) + ath_info(common, "Killer Wireless card detected\n"); + + /* + * Some WB335 cards do not support antenna diversity. Since + * we use a hardcoded value for AR9565 instead of using the + * EEPROM/OTP data, remove the combining feature from + * the HW capabilities bitmap. + */ + if (sc->driver_data & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { + if (!(sc->driver_data & ATH9K_PCI_BT_ANT_DIV)) + pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB; + } + if (sc->driver_data & ATH9K_PCI_BT_ANT_DIV) { pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; ath_info(common, "Set BT/WLAN RX diversity capability\n"); @@ -556,6 +411,11 @@ static void ath9k_init_platform(struct ath_softc *sc) ah->config.pcie_waen = 0x0040473b; ath_info(common, "Enable WAR for ASPM D3/L1\n"); } + + if (sc->driver_data & ATH9K_PCI_NO_PLL_PWRSAVE) { + ah->config.no_pll_pwrsave = true; + ath_info(common, "Disable PLL PowerSave\n"); + } } static void ath9k_eeprom_request_cb(const struct firmware *eeprom_blob, @@ -603,6 +463,27 @@ static void ath9k_eeprom_release(struct ath_softc *sc) release_firmware(sc->sc_ah->eeprom_blob); } +static int ath9k_init_soc_platform(struct ath_softc *sc) +{ + struct ath9k_platform_data *pdata = sc->dev->platform_data; + struct ath_hw *ah = sc->sc_ah; + int ret = 0; + + if (!pdata) + return 0; + + if (pdata->eeprom_name) { + ret = ath9k_eeprom_request(sc, pdata->eeprom_name); + if (ret) + return ret; + } + + if (pdata->tx_gain_buffalo) + ah->config.tx_gain_buffalo = true; + + return ret; +} + static int ath9k_init_softc(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops) { @@ -623,13 +504,15 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->reg_ops.read = ath9k_ioread32; ah->reg_ops.write = ath9k_iowrite32; ah->reg_ops.rmw = ath9k_reg_rmw; - atomic_set(&ah->intr_ref_cnt, -1); sc->sc_ah = ah; pCap = &ah->caps; - sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET); + common = ath9k_hw_common(ah); + sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); + sc->tx99_power = MAX_RATE_POWER + 1; + init_waitqueue_head(&sc->tx_wait); - if (!pdata) { + if (!pdata || pdata->use_eeprom) { ah->ah_flags |= AH_USE_EEPROM; sc->sc_ah->led_pin = -1; } else { @@ -641,7 +524,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->external_reset = pdata->external_reset; } - common = ath9k_hw_common(ah); common->ops = &ah->reg_ops; common->bus_ops = bus_ops; common->ah = ah; @@ -654,7 +536,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, /* * Platform quirks. */ - ath9k_init_platform(sc); + ath9k_init_pcoem_platform(sc); + + ret = ath9k_init_soc_platform(sc); + if (ret) + return ret; /* * Enable WLAN/BT RX Antenna diversity only when: @@ -668,7 +554,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, common->bt_ant_diversity = 1; spin_lock_init(&common->cc_lock); - spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); @@ -676,11 +561,10 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, (unsigned long)sc); + setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc); INIT_WORK(&sc->hw_reset_work, ath_reset_work); - INIT_WORK(&sc->hw_check_work, ath_hw_check); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work); - setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc); /* * Cache line size is used to size and align various @@ -689,12 +573,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ath_read_cachesize(common, &csz); common->cachelsz = csz << 2; /* convert to bytes */ - if (pdata && pdata->eeprom_name) { - ret = ath9k_eeprom_request(sc, pdata->eeprom_name); - if (ret) - return ret; - } - /* Initializes the hardware for all supported chipsets */ ret = ath9k_hw_init(ah); if (ret) @@ -711,10 +589,13 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (ret) goto err_btcoex; - ret = ath9k_init_channels_rates(sc); + ret = ath9k_cmn_init_channels_rates(common); if (ret) goto err_btcoex; + sc->p2p_ps_timer = ath_gen_timer_alloc(sc->sc_ah, ath9k_p2p_ps_timer, + NULL, sc, AR_FIRST_NDP_TIMER); + ath9k_cmn_init_crypto(sc->sc_ah); ath9k_init_misc(sc); ath_fill_led_pin(sc); @@ -732,6 +613,7 @@ err_queues: ath9k_hw_deinit(ah); err_hw: ath9k_eeprom_release(sc); + dev_kfree_skb_any(sc->tx99_skb); return ret; } @@ -740,15 +622,16 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band) struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct cfg80211_chan_def chandef; int i; - sband = &sc->sbands[band]; + sband = &common->sbands[band]; for (i = 0; i < sband->n_channels; i++) { chan = &sband->channels[i]; ah->curchan = &ah->channels[chan->hw_value]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); - ath9k_cmn_update_ichannel(ah->curchan, &chandef); + ath9k_cmn_get_channel(sc->hw, ah, &chandef); ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true); } } @@ -766,32 +649,27 @@ static void ath9k_init_txpower_limits(struct ath_softc *sc) ah->curchan = curchan; } -void ath9k_reload_chainmask_settings(struct ath_softc *sc) -{ - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)) - return; - - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) - setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) - setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); -} - static const struct ieee80211_iface_limit if_limits[] = { - { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_WDS) }, + { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) }, { .max = 8, .types = #ifdef CONFIG_MAC80211_MESH BIT(NL80211_IFTYPE_MESH_POINT) | #endif - BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_AP) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) }, }; +static const struct ieee80211_iface_limit wds_limits[] = { + { .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) }, +}; static const struct ieee80211_iface_limit if_dfs_limits[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_ADHOC) }, }; static const struct ieee80211_iface_combination if_comb[] = { @@ -803,26 +681,26 @@ static const struct ieee80211_iface_combination if_comb[] = { .beacon_int_infra_match = true, }, { + .limits = wds_limits, + .n_limits = ARRAY_SIZE(wds_limits), + .max_interfaces = 2048, + .num_different_channels = 1, + .beacon_int_infra_match = true, + }, +#ifdef CONFIG_ATH9K_DFS_CERTIFIED + { .limits = if_dfs_limits, .n_limits = ARRAY_SIZE(if_dfs_limits), .max_interfaces = 1, .num_different_channels = 1, .beacon_int_infra_match = true, - .radar_detect_widths = BIT(NL80211_CHAN_NO_HT) | - BIT(NL80211_CHAN_HT20), + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20), } -}; - -#ifdef CONFIG_PM -static const struct wiphy_wowlan_support ath9k_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, - .n_patterns = MAX_NUM_USER_PATTERN, - .pattern_min_len = 1, - .pattern_max_len = MAX_PATTERN_SIZE, -}; #endif +}; -void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) +static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -830,13 +708,15 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK | IEEE80211_HW_SPECTRUM_MGMT | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SUPPORTS_RC_TABLE | IEEE80211_HW_SUPPORTS_HT_CCK_RATES; + if (ath9k_ps_enable) + hw->flags |= IEEE80211_HW_SUPPORTS_PS; + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; @@ -848,19 +728,24 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || ath9k_modparam_nohwcrypt) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_WDS) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - - hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + hw->wiphy->features |= (NL80211_FEATURE_ACTIVE_MONITOR | + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE); + + if (!config_enabled(CONFIG_ATH9K_TX99)) { + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->iface_combinations = if_comb; + if (!ath9k_use_chanctx) { + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS); + } else + hw->wiphy->n_iface_combinations = 1; + } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -869,20 +754,10 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ; hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; - -#ifdef CONFIG_PM_SLEEP - if ((ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && - (sc->driver_data & ATH9K_PCI_WOW) && - device_can_wakeup(sc->dev)) - hw->wiphy->wowlan = &ath9k_wowlan_support; - - atomic_set(&sc->wow_sleep_proc_intr, -1); - atomic_set(&sc->wow_got_bmiss_intr, -1); -#endif + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; hw->queues = 4; hw->max_rates = 4; - hw->channel_change_time = 5000; hw->max_listen_interval = 1; hw->max_rate_tries = 10; hw->sta_data_size = sizeof(struct ath_node); @@ -900,12 +775,13 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &sc->sbands[IEEE80211_BAND_2GHZ]; + &common->sbands[IEEE80211_BAND_2GHZ]; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &sc->sbands[IEEE80211_BAND_5GHZ]; + &common->sbands[IEEE80211_BAND_5GHZ]; - ath9k_reload_chainmask_settings(sc); + ath9k_init_wow(hw); + ath9k_cmn_reload_chainmask(ah); SET_IEEE80211_PERM_ADDR(hw, common->macaddr); } @@ -928,6 +804,9 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, common = ath9k_hw_common(ah); ath9k_set_hw_capab(sc, hw); + /* Will be cleared in ath9k_start() */ + set_bit(ATH_OP_INVALID, &common->op_flags); + /* Initialize regulatory */ error = ath_regd_init(&common->regulatory, sc->hw->wiphy, ath9k_reg_notifier); @@ -997,12 +876,16 @@ static void ath9k_deinit_softc(struct ath_softc *sc) { int i = 0; + if (sc->p2p_ps_timer) + ath_gen_timer_free(sc->sc_ah, sc->p2p_ps_timer); + ath9k_deinit_btcoex(sc); for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); + del_timer_sync(&sc->sleep_timer); ath9k_hw_deinit(sc->sc_ah); if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); @@ -1035,19 +918,11 @@ static int __init ath9k_init(void) { int error; - /* Register rate control algorithm */ - error = ath_rate_control_register(); - if (error != 0) { - pr_err("Unable to register rate control algorithm: %d\n", - error); - goto err_out; - } - error = ath_pci_init(); if (error < 0) { pr_err("No PCI devices found, driver not installed\n"); error = -ENODEV; - goto err_rate_unregister; + goto err_out; } error = ath_ahb_init(); @@ -1060,9 +935,6 @@ static int __init ath9k_init(void) err_pci_exit: ath_pci_exit(); - - err_rate_unregister: - ath_rate_control_unregister(); err_out: return error; } @@ -1073,7 +945,6 @@ static void __exit ath9k_exit(void) is_ath9k_unloaded = true; ath_ahb_exit(); ath_pci_exit(); - ath_rate_control_unregister(); pr_info("%s: Driver unloaded\n", dev_info); } module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 2f831db396a..72a715fe8f2 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -28,6 +28,13 @@ void ath_tx_complete_poll_work(struct work_struct *work) int i; bool needreset = false; + + if (sc->tx99_state) { + ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, + "skip tx hung detection on tx99\n"); + return; + } + for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; @@ -58,50 +65,26 @@ void ath_tx_complete_poll_work(struct work_struct *work) /* * Checks if the BB/MAC is hung. */ -void ath_hw_check(struct work_struct *work) +bool ath_hw_check(struct ath_softc *sc) { - struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work); struct ath_common *common = ath9k_hw_common(sc->sc_ah); - unsigned long flags; - int busy; - u8 is_alive, nbeacon = 1; enum ath_reset_type type; + bool is_alive; ath9k_ps_wakeup(sc); + is_alive = ath9k_hw_check_alive(sc->sc_ah); - if (is_alive && !AR_SREV_9300(sc->sc_ah)) - goto out; - else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { + if (!is_alive) { ath_dbg(common, RESET, - "DCU stuck is detected. Schedule chip reset\n"); + "HW hang detected, schedule chip reset\n"); type = RESET_TYPE_MAC_HANG; - goto sched_reset; - } - - spin_lock_irqsave(&common->cc_lock, flags); - busy = ath_update_survey_stats(sc); - spin_unlock_irqrestore(&common->cc_lock, flags); - - ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n", - busy, sc->hw_busy_count + 1); - if (busy >= 99) { - if (++sc->hw_busy_count >= 3) { - type = RESET_TYPE_BB_HANG; - goto sched_reset; - } - } else if (busy >= 0) { - sc->hw_busy_count = 0; - nbeacon = 3; + ath9k_queue_reset(sc, type); } - ath_start_rx_poll(sc, nbeacon); - goto out; - -sched_reset: - ath9k_queue_reset(sc, type); -out: ath9k_ps_restore(sc); + + return is_alive; } /* @@ -132,13 +115,17 @@ 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) return; ath9k_ps_wakeup(sc); @@ -152,29 +139,6 @@ void ath_hw_pll_work(struct work_struct *work) } /* - * RX Polling - monitors baseband hangs. - */ -void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) -{ - if (!AR_SREV_9300(sc->sc_ah)) - return; - - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) - return; - - mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies - (nbeacon * sc->cur_beacon_conf.beacon_interval)); -} - -void ath_rx_poll(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *)data; - - if (!test_bit(SC_OP_INVALID, &sc->sc_flags)) - ieee80211_queue_work(sc->hw, &sc->hw_check_work); -} - -/* * PA Pre-distortion. */ static void ath_paprd_activate(struct ath_softc *sc) @@ -184,7 +148,7 @@ static void ath_paprd_activate(struct ath_softc *sc) struct ath9k_hw_cal_data *caldata = ah->caldata; int chain; - if (!caldata || !caldata->paprd_done) { + if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) { ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n"); return; } @@ -256,7 +220,9 @@ void ath_paprd_calibrate(struct work_struct *work) int len = 1800; int ret; - if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) { + if (!caldata || + !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) || + test_bit(PAPRD_DONE, &caldata->cal_flags)) { ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n"); return; } @@ -316,7 +282,7 @@ void ath_paprd_calibrate(struct work_struct *work) kfree_skb(skb); if (chain_ok) { - caldata->paprd_done = true; + set_bit(PAPRD_DONE, &caldata->cal_flags); ath_paprd_activate(sc); } @@ -343,7 +309,7 @@ void ath_ani_calibrate(unsigned long data) u32 cal_interval, short_cal_interval, long_cal_interval; unsigned long flags; - if (ah->caldata && ah->caldata->nfcal_interference) + if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags)) long_cal_interval = ATH_LONG_CALINTERVAL_INT; else long_cal_interval = ATH_LONG_CALINTERVAL; @@ -397,10 +363,10 @@ void ath_ani_calibrate(unsigned long data) /* Call ANI routine if necessary */ if (aniflag) { - spin_lock_irqsave(&common->cc_lock, flags); + spin_lock(&common->cc_lock); ath9k_hw_ani_monitor(ah, ah->curchan); ath_update_survey_stats(sc); - spin_unlock_irqrestore(&common->cc_lock, flags); + spin_unlock(&common->cc_lock); } /* Perform calibration if necessary */ @@ -432,7 +398,7 @@ set_timer: mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); if (ar9003_is_paprd_enabled(ah) && ah->caldata) { - if (!ah->caldata->paprd_done) { + if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) { ieee80211_queue_work(sc->hw, &sc->paprd_work); } else if (!ah->paprd_table_write_done) { ath9k_ps_wakeup(sc); @@ -449,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; @@ -473,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; /* @@ -488,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); } @@ -516,7 +483,8 @@ void ath_update_survey_nf(struct ath_softc *sc, int channel) if (chan->noisefloor) { survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = ath9k_hw_getchan_noise(ah, chan); + survey->noise = ath9k_hw_getchan_noise(ah, chan, + chan->noisefloor); } } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index a3eff0986a3..275205ab5f1 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -374,7 +374,6 @@ EXPORT_SYMBOL(ath9k_hw_releasetxqueue); bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) { struct ath_common *common = ath9k_hw_common(ah); - struct ath9k_channel *chan = ah->curchan; struct ath9k_tx_queue_info *qi; u32 cwMin, chanCwMin, value; @@ -387,10 +386,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q); if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { - if (chan && IS_CHAN_B(chan)) - chanCwMin = INIT_CWMIN_11B; - else - chanCwMin = INIT_CWMIN; + chanCwMin = INIT_CWMIN; for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1); } else @@ -485,8 +481,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) | AR_Q_MISC_CBR_INCR_DIS0); value = (qi->tqi_readyTime - (ah->config.sw_beacon_response_time - - ah->config.dma_beacon_response_time) - - ah->config.additional_swba_backoff) * 1024; + ah->config.dma_beacon_response_time)) * 1024; REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_EN); REG_SET_BIT(ah, AR_DMISC(q), @@ -554,25 +549,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) { rs->rs_rssi = ATH9K_RSSI_BAD; - rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD; - rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD; - rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD; - rs->rs_rssi_ext0 = ATH9K_RSSI_BAD; - rs->rs_rssi_ext1 = ATH9K_RSSI_BAD; - rs->rs_rssi_ext2 = ATH9K_RSSI_BAD; + rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD; + rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD; + rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD; + rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD; + rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD; + rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD; } else { rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); - rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0, + rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00); - rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0, + rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01); - rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0, + rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02); - rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4, + rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10); - rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4, + rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11); - rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4, + rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12); } if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) @@ -832,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; @@ -927,11 +922,29 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah) mask2 |= AR_IMR_S2_CST; } + if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) { + if (ints & ATH9K_INT_BB_WATCHDOG) { + mask |= AR_IMR_BCNMISC; + mask2 |= AR_IMR_S2_BB_WATCHDOG; + } + } + ath_dbg(common, INTERRUPT, "new IMR 0x%x\n", mask); REG_WRITE(ah, AR_IMR, mask); - ah->imrs2_reg &= ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC | - AR_IMR_S2_CABEND | AR_IMR_S2_CABTO | - AR_IMR_S2_TSFOOR | AR_IMR_S2_GTT | AR_IMR_S2_CST); + ah->imrs2_reg &= ~(AR_IMR_S2_TIM | + AR_IMR_S2_DTIM | + AR_IMR_S2_DTIMSYNC | + AR_IMR_S2_CABEND | + AR_IMR_S2_CABTO | + AR_IMR_S2_TSFOOR | + AR_IMR_S2_GTT | + AR_IMR_S2_CST); + + if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) { + if (ints & ATH9K_INT_BB_WATCHDOG) + ah->imrs2_reg &= ~AR_IMR_S2_BB_WATCHDOG; + } + ah->imrs2_reg |= mask2; REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg); @@ -945,3 +958,25 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah) return; } EXPORT_SYMBOL(ath9k_hw_set_interrupts); + +#define ATH9K_HW_MAX_DCU 10 +#define ATH9K_HW_SLICE_PER_DCU 16 +#define ATH9K_HW_BIT_IN_SLICE 16 +void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set) +{ + int dcu_idx; + u32 filter; + + for (dcu_idx = 0; dcu_idx < 10; dcu_idx++) { + filter = SM(set, AR_D_TXBLK_WRITE_COMMAND); + filter |= SM(dcu_idx, AR_D_TXBLK_WRITE_DCU); + filter |= SM((destidx / ATH9K_HW_SLICE_PER_DCU), + AR_D_TXBLK_WRITE_SLICE); + filter |= BIT(destidx % ATH9K_HW_BIT_IN_SLICE); + ath_dbg(ath9k_hw_common(ah), PS, + "DCU%d staid %d set %d txfilter %08x\n", + dcu_idx, destidx, set, filter); + REG_WRITE(ah, AR_D_TXBLK_BASE, filter); + } +} +EXPORT_SYMBOL(ath9k_hw_set_tx_filter); diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index bfccaceed44..da768675753 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -133,12 +133,8 @@ struct ath_rx_status { u8 rs_rate; u8 rs_antenna; u8 rs_more; - int8_t rs_rssi_ctl0; - int8_t rs_rssi_ctl1; - int8_t rs_rssi_ctl2; - int8_t rs_rssi_ext0; - int8_t rs_rssi_ext1; - int8_t rs_rssi_ext2; + int8_t rs_rssi_ctl[3]; + int8_t rs_rssi_ext[3]; u8 rs_isaggr; u8 rs_firstaggr; u8 rs_moreaggr; @@ -159,12 +155,8 @@ struct ath_htc_rx_status { u8 rs_status; u8 rs_phyerr; int8_t rs_rssi; - int8_t rs_rssi_ctl0; - int8_t rs_rssi_ctl1; - int8_t rs_rssi_ctl2; - int8_t rs_rssi_ext0; - int8_t rs_rssi_ext1; - int8_t rs_rssi_ext2; + int8_t rs_rssi_ctl[3]; + int8_t rs_rssi_ext[3]; u8 rs_keyix; u8 rs_rate; u8 rs_antenna; @@ -174,6 +166,7 @@ struct ath_htc_rx_status { u8 rs_num_delims; u8 rs_flags; u8 rs_dummy; + /* FIXME: evm* never used? */ __be32 evm0; __be32 evm1; __be32 evm2; @@ -603,8 +596,6 @@ enum ath9k_tx_queue_flags { #define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define ATH9K_DECOMP_MASK_SIZE 128 -#define ATH9K_READY_TIME_LO_BOUND 50 -#define ATH9K_READY_TIME_HI_BOUND 96 enum ath9k_pkt_type { ATH9K_PKT_TYPE_NORMAL = 0, @@ -738,6 +729,7 @@ void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); void ath9k_hw_abortpcurecv(struct ath_hw *ah); bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset); int ath9k_hw_beaconq_setup(struct ath_hw *ah); +void ath9k_hw_set_tx_filter(struct ath_hw *ah, u8 destidx, bool set); /* Interrupt Handling */ bool ath9k_hw_intrpend(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 709301f88dc..62ac95d6bb9 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -82,6 +82,22 @@ static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) return ret; } +void ath_ps_full_sleep(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + bool reset; + + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + spin_unlock(&common->cc_lock); + + ath9k_hw_setrxabort(sc->sc_ah, 1); + ath9k_hw_stopdmarecv(sc->sc_ah, &reset); + + ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); +} + void ath9k_ps_wakeup(struct ath_softc *sc) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -92,6 +108,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc) if (++sc->ps_usecount != 1) goto unlock; + del_timer_sync(&sc->sleep_timer); power_mode = sc->sc_ah->power_mode; ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); @@ -117,17 +134,17 @@ void ath9k_ps_restore(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); enum ath9k_power_mode mode; unsigned long flags; - bool reset; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (--sc->ps_usecount != 0) goto unlock; if (sc->ps_idle) { - ath9k_hw_setrxabort(sc->sc_ah, 1); - ath9k_hw_stopdmarecv(sc->sc_ah, &reset); - mode = ATH9K_PM_FULL_SLEEP; - } else if (sc->ps_enabled && + mod_timer(&sc->sleep_timer, jiffies + HZ / 10); + goto unlock; + } + + if (sc->ps_enabled && !(sc->ps_flags & (PS_WAIT_FOR_BEACON | PS_WAIT_FOR_CAB | PS_WAIT_FOR_PSPOLL_DATA | @@ -153,7 +170,6 @@ void ath9k_ps_restore(struct ath_softc *sc) static void __ath_cancel_work(struct ath_softc *sc) { cancel_work_sync(&sc->paprd_work); - cancel_work_sync(&sc->hw_check_work); cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); @@ -163,13 +179,13 @@ static void __ath_cancel_work(struct ath_softc *sc) #endif } -static void ath_cancel_work(struct ath_softc *sc) +void ath_cancel_work(struct ath_softc *sc) { __ath_cancel_work(sc); cancel_work_sync(&sc->hw_reset_work); } -static void ath_restart_work(struct ath_softc *sc) +void ath_restart_work(struct ath_softc *sc) { ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); @@ -177,7 +193,6 @@ static void ath_restart_work(struct ath_softc *sc) ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, msecs_to_jiffies(ATH_PLL_WORK_INTERVAL)); - ath_start_rx_poll(sc, 3); ath_start_ani(sc); } @@ -187,11 +202,7 @@ static bool ath_prepare_reset(struct ath_softc *sc) bool ret = true; ieee80211_stop_queues(sc->hw); - - sc->hw_busy_count = 0; ath_stop_ani(sc); - del_timer_sync(&sc->rx_poll_timer); - ath9k_hw_disable_interrupts(ah); if (!ath_drain_all_txq(sc)) @@ -218,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); @@ -247,8 +258,11 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) } } + sc->gtt_cnt = 0; ieee80211_wake_queues(sc->hw); + ath9k_p2p_ps_timer(sc); + return true; } @@ -312,17 +326,90 @@ out: * by reseting the chip. To accomplish this we must first cleanup any pending * DMA, then restart stuff. */ -static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, - struct ath9k_channel *hchan) +static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef) { + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hw *hw = sc->hw; + struct ath9k_channel *hchan; + struct ieee80211_channel *chan = chandef->chan; + bool offchannel; + int pos = chan->hw_value; + 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); + + if (ah->curchan) + old_pos = ah->curchan - &ah->channels[0]; + + ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", + chan->center_freq, chandef->width); + + /* update survey stats for the old channel before switching */ + spin_lock_bh(&common->cc_lock); + ath_update_survey_stats(sc); + spin_unlock_bh(&common->cc_lock); + + ath9k_cmn_get_channel(hw, ah, chandef); + + /* + * If the operating channel changes, change the survey in-use flags + * along with it. + * Reset the survey data for the new channel, unless we're switching + * back to the operating channel from an off-channel operation. + */ + if (!offchannel && sc->cur_survey != &sc->survey[pos]) { + if (sc->cur_survey) + sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; + + sc->cur_survey = &sc->survey[pos]; + + memset(sc->cur_survey, 0, sizeof(struct survey_info)); + sc->cur_survey->filled |= SURVEY_INFO_IN_USE; + } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + + hchan = &sc->sc_ah->channels[pos]; r = ath_reset_internal(sc, hchan); + if (r) + return r; - return r; + /* + * The most recent snapshot of channel->noisefloor for the old + * channel is only available after the hardware reset. Copy it to + * the survey stats now. + */ + if (old_pos >= 0) + ath_update_survey_nf(sc, old_pos); + + /* + * Enable radar pulse detection if on a DFS channel. Spectral + * scanning and radar detection can not be used concurrently. + */ + if (hw->conf.radar_enabled) { + u32 rxfilter; + + /* set HW specific DFS configuration */ + ath9k_hw_set_radar_params(ah); + rxfilter = ath9k_hw_getrxfilter(ah); + rxfilter |= ATH9K_RX_FILTER_PHYRADAR | + ATH9K_RX_FILTER_PHYERR; + ath9k_hw_setrxfilter(ah, rxfilter); + ath_dbg(common, DFS, "DFS enabled at freq %d\n", + chan->center_freq); + } else { + /* perform spectral scan if requested. */ + if (test_bit(ATH_OP_SCANNING, &common->op_flags) && + sc->spectral_mode == SPECTRAL_CHANSCAN) + ath9k_spectral_scan_trigger(hw); + } + + return 0; } static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, @@ -334,14 +421,9 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, an->sc = sc; an->sta = sta; an->vif = vif; + memset(&an->key_idx, 0, sizeof(an->key_idx)); ath_tx_node_init(sc, an); - - if (sta->ht_cap.ht_supported) { - an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + - sta->ht_cap.ampdu_factor); - an->mpdudensity = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density); - } } static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) @@ -363,16 +445,52 @@ void ath9k_tasklet(unsigned long data) ath9k_ps_wakeup(sc); spin_lock(&sc->sc_pcu_lock); - if ((status & ATH9K_INT_FATAL) || + if (status & ATH9K_INT_FATAL) { + type = RESET_TYPE_FATAL_INT; + ath9k_queue_reset(sc, type); + + /* + * Increment the ref. counter here so that + * interrupts are enabled in the reset routine. + */ + atomic_inc(&ah->intr_ref_cnt); + ath_dbg(common, RESET, "FATAL: Skipping interrupts\n"); + goto out; + } + + if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) && (status & ATH9K_INT_BB_WATCHDOG)) { + spin_lock(&common->cc_lock); + ath_hw_cycle_counters_update(common); + ar9003_hw_bb_watchdog_dbg_info(ah); + spin_unlock(&common->cc_lock); - if (status & ATH9K_INT_FATAL) - type = RESET_TYPE_FATAL_INT; - else + if (ar9003_hw_bb_watchdog_check(ah)) { type = RESET_TYPE_BB_WATCHDOG; + ath9k_queue_reset(sc, type); - ath9k_queue_reset(sc, type); - goto out; + /* + * Increment the ref. counter here so that + * interrupts are enabled in the reset routine. + */ + atomic_inc(&ah->intr_ref_cnt); + ath_dbg(common, RESET, + "BB_WATCHDOG: Skipping interrupts\n"); + goto out; + } + } + + if (status & ATH9K_INT_GTT) { + sc->gtt_cnt++; + + if ((sc->gtt_cnt >= MAX_GTT_CNT) && !ath9k_hw_check_alive(ah)) { + type = RESET_TYPE_TX_GTT; + ath9k_queue_reset(sc, type); + atomic_inc(&ah->intr_ref_cnt); + ath_dbg(common, RESET, + "GTT: Skipping interrupts\n"); + goto out; + } } spin_lock_irqsave(&sc->sc_pm_lock, flags); @@ -402,18 +520,31 @@ void ath9k_tasklet(unsigned long data) } if (status & ATH9K_INT_TX) { - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) + if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { + /* + * For EDMA chips, TX completion is enabled for the + * beacon queue, so if a beacon has been transmitted + * successfully after a GTT interrupt, the GTT counter + * gets reset to zero here. + */ + sc->gtt_cnt = 0; + ath_tx_edma_tasklet(sc); - else + } else { ath_tx_tasklet(sc); + } + + wake_up(&sc->tx_wait); } + if (status & ATH9K_INT_GENTIMER) + ath_gen_timer_isr(sc->sc_ah); + ath9k_btcoex_handle_interrupt(sc, status); -out: /* re-enable hardware interrupt */ ath9k_hw_enable_interrupts(ah); - +out: spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); } @@ -431,6 +562,7 @@ irqreturn_t ath_isr(int irq, void *dev) ATH9K_INT_TX | \ ATH9K_INT_BMISS | \ ATH9K_INT_CST | \ + ATH9K_INT_GTT | \ ATH9K_INT_TSFOOR | \ ATH9K_INT_GENTIMER | \ ATH9K_INT_MCI) @@ -439,6 +571,7 @@ irqreturn_t ath_isr(int irq, void *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; /* @@ -446,7 +579,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 */ @@ -454,7 +587,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; } @@ -465,7 +598,8 @@ irqreturn_t ath_isr(int irq, void *dev) * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ - ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ + ath9k_hw_getisr(ah, &status, &sync_cause); /* NB: clears ISR too */ + ath9k_debug_sync_cause(sc, sync_cause); status &= ah->imask; /* discard unasked-for bits */ /* @@ -489,25 +623,19 @@ irqreturn_t ath_isr(int irq, void *dev) !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA))) goto chip_reset; - if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && - (status & ATH9K_INT_BB_WATCHDOG)) { - - spin_lock(&common->cc_lock); - ath_hw_cycle_counters_update(common); - ar9003_hw_bb_watchdog_dbg_info(ah); - spin_unlock(&common->cc_lock); - + if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) && + (status & ATH9K_INT_BB_WATCHDOG)) goto chip_reset; - } -#ifdef CONFIG_PM_SLEEP + +#ifdef CONFIG_ATH9K_WOW if (status & ATH9K_INT_BMISS) { if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { - ath_dbg(common, ANY, "during WoW we got a BMISS\n"); atomic_inc(&sc->wow_got_bmiss_intr); atomic_dec(&sc->wow_sleep_proc_intr); } } #endif + if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); @@ -547,7 +675,7 @@ chip_reset: #undef SCHED_INTR } -static int ath_reset(struct ath_softc *sc) +int ath_reset(struct ath_softc *sc) { int r; @@ -560,10 +688,11 @@ static 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); } @@ -594,7 +723,7 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); - init_channel = ath9k_cmn_get_curchannel(hw, ah); + init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -625,19 +754,26 @@ static int ath9k_start(struct ieee80211_hw *hw) if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ah->imask |= ATH9K_INT_RXHP | - ATH9K_INT_RXLP | - ATH9K_INT_BB_WATCHDOG; + ATH9K_INT_RXLP; else ah->imask |= ATH9K_INT_RX; - ah->imask |= ATH9K_INT_GTT; + if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) + ah->imask |= ATH9K_INT_BB_WATCHDOG; + + /* + * Enable GTT interrupts only for AR9003/AR9004 chips + * for now. + */ + if (AR_SREV_9300_20_OR_LATER(ah)) + ah->imask |= ATH9K_INT_GTT; if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) ah->imask |= ATH9K_INT_CST; 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)) @@ -655,6 +791,8 @@ static int ath9k_start(struct ieee80211_hw *hw) */ ath9k_cmn_init_crypto(sc->sc_ah); + ath9k_hw_reset_tsf(ah); + spin_unlock_bh(&sc->sc_pcu_lock); mutex_unlock(&sc->mutex); @@ -751,9 +889,8 @@ static void ath9k_stop(struct ieee80211_hw *hw) mutex_lock(&sc->mutex); ath_cancel_work(sc); - del_timer_sync(&sc->rx_poll_timer); - 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; @@ -797,7 +934,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) } if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); + ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); ath9k_hw_phy_disable(ah); @@ -808,7 +945,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); @@ -816,7 +953,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath_dbg(common, CONFIG, "Driver halt\n"); } -bool ath9k_uses_beacons(int type) +static bool ath9k_uses_beacons(int type) { switch (type) { case NL80211_IFTYPE_AP: @@ -885,8 +1022,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(ah); /* - * Use the hardware MAC address as reference, the hardware uses it - * together with the BSSID mask when matching addresses. + * Pick the MAC address of the first interface as the new hardware + * MAC address. The hardware will use it together with the BSSID mask + * when matching addresses. */ memset(iter_data, 0, sizeof(*iter_data)); memset(&iter_data->mask, 0xff, ETH_ALEN); @@ -948,7 +1086,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); @@ -966,6 +1104,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + if (sc->nvifs >= 1) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + sc->tx99_vif = vif; + } + ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; @@ -976,6 +1122,8 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, if (ath9k_uses_beacons(vif->type)) ath9k_beacon_assign_slot(sc, vif); + avp->vif = vif; + an->sc = sc; an->sta = NULL; an->vif = vif; @@ -994,9 +1142,15 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_dbg(common, CONFIG, "Change Interface\n"); mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + + ath_dbg(common, CONFIG, "Change Interface\n"); + if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1014,6 +1168,29 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, return 0; } +static void +ath9k_update_p2p_ps_timer(struct ath_softc *sc, struct ath_vif *avp) +{ + struct ath_hw *ah = sc->sc_ah; + s32 tsf, target_tsf; + + if (!avp || !avp->noa.has_next_tsf) + return; + + ath9k_hw_gen_timer_stop(ah, sc->p2p_ps_timer); + + tsf = ath9k_hw_gettsf32(sc->sc_ah); + + target_tsf = avp->noa.next_tsf; + if (!avp->noa.absent) + target_tsf -= ATH_P2P_PS_STOP_TIME; + + if (target_tsf - tsf < ATH_P2P_PS_STOP_TIME) + target_tsf = tsf + ATH_P2P_PS_STOP_TIME; + + ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, (u32) target_tsf, 1000000); +} + static void ath9k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -1025,14 +1202,19 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); + spin_lock_bh(&sc->sc_pcu_lock); + if (avp == sc->p2p_ps_vif) { + sc->p2p_ps_vif = NULL; + ath9k_update_p2p_ps_timer(sc, NULL); + } + spin_unlock_bh(&sc->sc_pcu_lock); + sc->nvifs--; + sc->tx99_vif = NULL; if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); - if (sc->csa_vif == vif) - sc->csa_vif = NULL; - ath9k_ps_wakeup(sc); ath9k_calculate_summary_state(hw, NULL); ath9k_ps_restore(sc); @@ -1047,6 +1229,9 @@ static void ath9k_enable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = true; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { @@ -1063,6 +1248,9 @@ static void ath9k_disable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = false; ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { @@ -1086,6 +1274,9 @@ void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); u32 rxfilter; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { ath_err(common, "spectrum analyzer not implemented on this hardware\n"); return; @@ -1201,81 +1392,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { - struct ieee80211_channel *curchan = hw->conf.chandef.chan; - int pos = curchan->hw_value; - int old_pos = -1; - unsigned long flags; - - if (ah->curchan) - old_pos = ah->curchan - &ah->channels[0]; - - ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", - curchan->center_freq, hw->conf.chandef.width); - - /* update survey stats for the old channel before switching */ - spin_lock_irqsave(&common->cc_lock, flags); - ath_update_survey_stats(sc); - spin_unlock_irqrestore(&common->cc_lock, flags); - - ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], - &conf->chandef); - - /* - * If the operating channel changes, change the survey in-use flags - * along with it. - * Reset the survey data for the new channel, unless we're switching - * back to the operating channel from an off-channel operation. - */ - if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && - sc->cur_survey != &sc->survey[pos]) { - - if (sc->cur_survey) - sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; - - sc->cur_survey = &sc->survey[pos]; - - memset(sc->cur_survey, 0, sizeof(struct survey_info)); - sc->cur_survey->filled |= SURVEY_INFO_IN_USE; - } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { - memset(&sc->survey[pos], 0, sizeof(struct survey_info)); - } - - if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { + if (ath_set_channel(sc, &hw->conf.chandef) < 0) { ath_err(common, "Unable to set channel\n"); mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); return -EINVAL; } - - /* - * The most recent snapshot of channel->noisefloor for the old - * channel is only available after the hardware reset. Copy it to - * the survey stats now. - */ - if (old_pos >= 0) - ath_update_survey_nf(sc, old_pos); - - /* - * Enable radar pulse detection if on a DFS channel. Spectral - * scanning and radar detection can not be used concurrently. - */ - if (hw->conf.radar_enabled) { - u32 rxfilter; - - /* set HW specific DFS configuration */ - ath9k_hw_set_radar_params(ah); - rxfilter = ath9k_hw_getrxfilter(ah); - rxfilter |= ATH9K_RX_FILTER_PHYRADAR | - ATH9K_RX_FILTER_PHYERR; - ath9k_hw_setrxfilter(ah, rxfilter); - ath_dbg(common, DFS, "DFS enabled at freq %d\n", - curchan->center_freq); - } else { - /* perform spectral scan if requested. */ - if (test_bit(SC_OP_SCANNING, &sc->sc_flags) && - sc->spectral_mode == SPECTRAL_CHANSCAN) - ath9k_spectral_scan_trigger(hw); - } } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -1340,8 +1462,10 @@ static int ath9k_sta_add(struct ieee80211_hw *hw, return 0; key = ath_key_config(common, vif, sta, &ps_key); - if (key > 0) + if (key > 0) { an->ps_key = key; + an->key_idx[0] = key; + } return 0; } @@ -1359,6 +1483,7 @@ static void ath9k_del_ps_key(struct ath_softc *sc, ath_key_delete(common, &ps_key); an->ps_key = 0; + an->key_idx[0] = 0; } static int ath9k_sta_remove(struct ieee80211_hw *hw, @@ -1373,6 +1498,19 @@ static int ath9k_sta_remove(struct ieee80211_hw *hw, return 0; } +static void ath9k_sta_set_tx_filter(struct ath_hw *ah, + struct ath_node *an, + bool set) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { + if (!an->key_idx[i]) + continue; + ath9k_hw_set_tx_filter(ah, an->key_idx[i], set); + } +} + static void ath9k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd cmd, @@ -1385,8 +1523,10 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, case STA_NOTIFY_SLEEP: an->sleeping = true; ath_tx_aggr_sleep(sta, sc, an); + ath9k_sta_set_tx_filter(sc->sc_ah, an, true); break; case STA_NOTIFY_AWAKE: + ath9k_sta_set_tx_filter(sc->sc_ah, an, false); an->sleeping = false; ath_tx_aggr_wakeup(sc, an); break; @@ -1442,7 +1582,8 @@ static int ath9k_set_key(struct ieee80211_hw *hw, { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - int ret = 0; + struct ath_node *an = NULL; + int ret = 0, i; if (ath9k_modparam_nohwcrypt) return -ENOSPC; @@ -1464,13 +1605,16 @@ static int ath9k_set_key(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); - ath_dbg(common, CONFIG, "Set HW Key\n"); + ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd); + if (sta) + an = (struct ath_node *)sta->drv_priv; switch (cmd) { case SET_KEY: if (sta) ath9k_del_ps_key(sc, vif, sta); + key->hw_key_idx = 0; ret = ath_key_config(common, vif, sta, key); if (ret >= 0) { key->hw_key_idx = ret; @@ -1483,9 +1627,27 @@ static int ath9k_set_key(struct ieee80211_hw *hw, key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; ret = 0; } + if (an && key->hw_key_idx) { + for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { + if (an->key_idx[i]) + continue; + an->key_idx[i] = key->hw_key_idx; + break; + } + WARN_ON(i == ARRAY_SIZE(an->key_idx)); + } break; case DISABLE_KEY: ath_key_delete(common, key); + if (an) { + for (i = 0; i < ARRAY_SIZE(an->key_idx); i++) { + if (an->key_idx[i] != key->hw_key_idx) + continue; + an->key_idx[i] = 0; + break; + } + } + key->hw_key_idx = 0; break; default: ret = -EINVAL; @@ -1505,7 +1667,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; /* @@ -1521,7 +1683,7 @@ static void ath9k_set_assoc_state(struct ath_softc *sc, common->curaid = bss_conf->aid; ath9k_hw_write_associd(sc->sc_ah); - sc->last_rssi = ATH_RSSI_DUMMY_MARKER; + common->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; spin_lock_irqsave(&sc->sc_pm_lock, flags); @@ -1540,14 +1702,75 @@ 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) ath9k_set_assoc_state(sc, vif); } +void ath9k_p2p_ps_timer(void *priv) +{ + struct ath_softc *sc = priv; + struct ath_vif *avp = sc->p2p_ps_vif; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct ath_node *an; + u32 tsf; + + if (!avp) + return; + + tsf = ath9k_hw_gettsf32(sc->sc_ah); + if (!avp->noa.absent) + tsf += ATH_P2P_PS_STOP_TIME; + + if (!avp->noa.has_next_tsf || + avp->noa.next_tsf - tsf > BIT(31)) + ieee80211_update_p2p_noa(&avp->noa, tsf); + + ath9k_update_p2p_ps_timer(sc, avp); + + rcu_read_lock(); + + vif = avp->vif; + sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (!sta) + goto out; + + an = (void *) sta->drv_priv; + if (an->sleeping == !!avp->noa.absent) + goto out; + + an->sleeping = avp->noa.absent; + if (an->sleeping) + ath_tx_aggr_sleep(sta, sc, an); + else + ath_tx_aggr_wakeup(sc, an); + +out: + rcu_read_unlock(); +} + +void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif) +{ + struct ath_vif *avp = (void *)vif->drv_priv; + u32 tsf; + + if (!sc->p2p_ps_timer) + return; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->p2p) + return; + + sc->p2p_ps_vif = avp; + tsf = ath9k_hw_gettsf32(sc->sc_ah); + ieee80211_parse_p2p_noa(&vif->bss_conf.p2p_noa_attr, &avp->noa, tsf); + ath9k_update_p2p_ps_timer(sc, avp); +} + static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -1562,6 +1785,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; + unsigned long flags; int slottime; ath9k_ps_wakeup(sc); @@ -1572,18 +1796,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; @@ -1600,13 +1824,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if ((changed & BSS_CHANGED_BEACON_ENABLED) || - (changed & BSS_CHANGED_BEACON_INT)) { - if (ah->opmode == NL80211_IFTYPE_AP && - bss_conf->enable_beacon) - ath9k_set_tsfadjust(sc, vif); - if (ath9k_allow_beacon_config(sc, vif)) - ath9k_beacon_config(sc, vif, changed); - } + (changed & BSS_CHANGED_BEACON_INT)) + ath9k_beacon_config(sc, vif, changed); if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) @@ -1627,6 +1846,15 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_P2P_PS) { + spin_lock_bh(&sc->sc_pcu_lock); + spin_lock_irqsave(&sc->sc_pm_lock, flags); + if (!(sc->ps_flags & PS_BEACON_SYNC)) + ath9k_update_p2p_ps(sc, vif); + spin_unlock_irqrestore(&sc->sc_pm_lock, flags); + spin_unlock_bh(&sc->sc_pcu_lock); + } + if (changed & CHECK_ANI) ath_check_ani(sc); @@ -1731,10 +1959,12 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; - unsigned long flags; int pos; - spin_lock_irqsave(&common->cc_lock, flags); + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + + spin_lock_bh(&common->cc_lock); if (idx == 0) ath_update_survey_stats(sc); @@ -1748,7 +1978,7 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; if (!sband || idx >= sband->n_channels) { - spin_unlock_irqrestore(&common->cc_lock, flags); + spin_unlock_bh(&common->cc_lock); return -ENOENT; } @@ -1756,7 +1986,7 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, pos = chan->hw_value; memcpy(survey, &sc->survey[pos], sizeof(*survey)); survey->channel = chan; - spin_unlock_irqrestore(&common->cc_lock, flags); + spin_unlock_bh(&common->cc_lock); return 0; } @@ -1766,6 +1996,9 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + mutex_lock(&sc->mutex); ah->coverage_class = coverage_class; @@ -1776,13 +2009,32 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) mutex_unlock(&sc->mutex); } -static void ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static bool ath9k_has_tx_pending(struct ath_softc *sc) +{ + int i, npend = 0; + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { + if (!ATH_TXQ_SETUP(sc, i)) + continue; + + if (!sc->tx.txq[i].axq_depth) + continue; + + npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]); + if (npend) + break; + } + + return !!npend; +} + +static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - int timeout = 200; /* ms */ - int i, j; + int timeout = HZ / 5; /* 200 ms */ bool drain_txq; mutex_lock(&sc->mutex); @@ -1794,31 +2046,15 @@ 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; } - for (j = 0; j < timeout; j++) { - bool npend = false; - - if (j) - usleep_range(1000, 2000); - - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { - if (!ATH_TXQ_SETUP(sc, i)) - continue; - - npend = ath9k_has_pending_frames(sc, &sc->tx.txq[i]); - - if (npend) - break; - } - - if (!npend) - break; - } + if (wait_event_timeout(sc->tx_wait, !ath9k_has_tx_pending(sc), + timeout) > 0) + drop = false; if (drop) { ath9k_ps_wakeup(sc); @@ -1966,7 +2202,7 @@ static int ath9k_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) ah->rxchainmask = fill_chainmask(ah->caps.rx_chainmask, rx_ant); ah->txchainmask = fill_chainmask(ah->caps.tx_chainmask, tx_ant); - ath9k_reload_chainmask_settings(sc); + ath9k_cmn_reload_chainmask(ah); return 0; } @@ -1980,356 +2216,18 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) return 0; } -#ifdef CONFIG_PM_SLEEP - -static void ath9k_wow_map_triggers(struct ath_softc *sc, - struct cfg80211_wowlan *wowlan, - u32 *wow_triggers) -{ - if (wowlan->disconnect) - *wow_triggers |= AH_WOW_LINK_CHANGE | - AH_WOW_BEACON_MISS; - if (wowlan->magic_pkt) - *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; - - if (wowlan->n_patterns) - *wow_triggers |= AH_WOW_USER_PATTERN_EN; - - sc->wow_enabled = *wow_triggers; - -} - -static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - int pattern_count = 0; - int i, byte_cnt; - u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; - u8 dis_deauth_mask[MAX_PATTERN_SIZE]; - - memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE); - memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE); - - /* - * Create Dissassociate / Deauthenticate packet filter - * - * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes - * +--------------+----------+---------+--------+--------+---- - * + Frame Control+ Duration + DA + SA + BSSID + - * +--------------+----------+---------+--------+--------+---- - * - * The above is the management frame format for disassociate/ - * deauthenticate pattern, from this we need to match the first byte - * of 'Frame Control' and DA, SA, and BSSID fields - * (skipping 2nd byte of FC and Duration feild. - * - * Disassociate pattern - * -------------------- - * Frame control = 00 00 1010 - * DA, SA, BSSID = x:x:x:x:x:x - * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x - * | x:x:x:x:x:x -- 22 bytes - * - * Deauthenticate pattern - * ---------------------- - * Frame control = 00 00 1100 - * DA, SA, BSSID = x:x:x:x:x:x - * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x - * | x:x:x:x:x:x -- 22 bytes - */ - - /* Create Disassociate Pattern first */ - - byte_cnt = 0; - - /* Fill out the mask with all FF's */ - - for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) - dis_deauth_mask[i] = 0xff; - - /* copy the first byte of frame control field */ - dis_deauth_pattern[byte_cnt] = 0xa0; - byte_cnt++; - - /* skip 2nd byte of frame control and Duration field */ - byte_cnt += 3; - - /* - * need not match the destination mac address, it can be a broadcast - * mac address or an unicast to this station - */ - byte_cnt += 6; - - /* copy the source mac address */ - memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); - - byte_cnt += 6; - - /* copy the bssid, its same as the source mac address */ - - memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); - - /* Create Disassociate pattern mask */ - - dis_deauth_mask[0] = 0xfe; - dis_deauth_mask[1] = 0x03; - dis_deauth_mask[2] = 0xc0; - - ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); - - ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, - pattern_count, byte_cnt); - - pattern_count++; - /* - * for de-authenticate pattern, only the first byte of the frame - * control field gets changed from 0xA0 to 0xC0 - */ - dis_deauth_pattern[0] = 0xC0; - - ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, - pattern_count, byte_cnt); - -} - -static void ath9k_wow_add_pattern(struct ath_softc *sc, - struct cfg80211_wowlan *wowlan) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath9k_wow_pattern *wow_pattern = NULL; - struct cfg80211_pkt_pattern *patterns = wowlan->patterns; - int mask_len; - s8 i = 0; - - if (!wowlan->n_patterns) - return; - - /* - * Add the new user configured patterns - */ - for (i = 0; i < wowlan->n_patterns; i++) { - - wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL); - - if (!wow_pattern) - return; - - /* - * TODO: convert the generic user space pattern to - * appropriate chip specific/802.11 pattern. - */ - - mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE); - memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE); - memcpy(wow_pattern->pattern_bytes, patterns[i].pattern, - patterns[i].pattern_len); - memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len); - wow_pattern->pattern_len = patterns[i].pattern_len; - - /* - * just need to take care of deauth and disssoc pattern, - * make sure we don't overwrite them. - */ - - ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes, - wow_pattern->mask_bytes, - i + 2, - wow_pattern->pattern_len); - kfree(wow_pattern); - - } - -} - -static int ath9k_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan) -{ - struct ath_softc *sc = hw->priv; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - u32 wow_triggers_enabled = 0; - int ret = 0; - - mutex_lock(&sc->mutex); - - ath_cancel_work(sc); - ath_stop_ani(sc); - del_timer_sync(&sc->rx_poll_timer); - - if (test_bit(SC_OP_INVALID, &sc->sc_flags)) { - ath_dbg(common, ANY, "Device not present\n"); - ret = -EINVAL; - goto fail_wow; - } - - if (WARN_ON(!wowlan)) { - ath_dbg(common, WOW, "None of the WoW triggers enabled\n"); - ret = -EINVAL; - goto fail_wow; - } - - if (!device_can_wakeup(sc->dev)) { - ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n"); - ret = 1; - goto fail_wow; - } - - /* - * none of the sta vifs are associated - * and we are not currently handling multivif - * cases, for instance we have to seperately - * configure 'keep alive frame' for each - * STA. - */ - - if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { - ath_dbg(common, WOW, "None of the STA vifs are associated\n"); - ret = 1; - goto fail_wow; - } - - if (sc->nvifs > 1) { - ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); - ret = 1; - goto fail_wow; - } - - ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled); - - ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n", - wow_triggers_enabled); - - ath9k_ps_wakeup(sc); - - ath9k_stop_btcoex(sc); - - /* - * Enable wake up on recieving disassoc/deauth - * frame by default. - */ - ath9k_wow_add_disassoc_deauth_pattern(sc); - - if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN) - ath9k_wow_add_pattern(sc, wowlan); - - spin_lock_bh(&sc->sc_pcu_lock); - /* - * To avoid false wake, we enable beacon miss interrupt only - * when we go to sleep. We save the current interrupt mask - * so we can restore it after the system wakes up - */ - sc->wow_intr_before_sleep = ah->imask; - ah->imask &= ~ATH9K_INT_GLOBAL; - ath9k_hw_disable_interrupts(ah); - ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL; - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); - - spin_unlock_bh(&sc->sc_pcu_lock); - - /* - * we can now sync irq and kill any running tasklets, since we already - * disabled interrupts and not holding a spin lock - */ - synchronize_irq(sc->irq); - tasklet_kill(&sc->intr_tq); - - ath9k_hw_wow_enable(ah, wow_triggers_enabled); - - ath9k_ps_restore(sc); - ath_dbg(common, ANY, "WoW enabled in ath9k\n"); - atomic_inc(&sc->wow_sleep_proc_intr); - -fail_wow: - mutex_unlock(&sc->mutex); - return ret; -} - -static int ath9k_resume(struct ieee80211_hw *hw) -{ - struct ath_softc *sc = hw->priv; - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - u32 wow_status; - - mutex_lock(&sc->mutex); - - ath9k_ps_wakeup(sc); - - spin_lock_bh(&sc->sc_pcu_lock); - - ath9k_hw_disable_interrupts(ah); - ah->imask = sc->wow_intr_before_sleep; - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); - - spin_unlock_bh(&sc->sc_pcu_lock); - - wow_status = ath9k_hw_wow_wakeup(ah); - - if (atomic_read(&sc->wow_got_bmiss_intr) == 0) { - /* - * some devices may not pick beacon miss - * as the reason they woke up so we add - * that here for that shortcoming. - */ - wow_status |= AH_WOW_BEACON_MISS; - atomic_dec(&sc->wow_got_bmiss_intr); - ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n"); - } - - atomic_dec(&sc->wow_sleep_proc_intr); - - if (wow_status) { - ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n", - ath9k_hw_wow_event_to_string(wow_status), wow_status); - } - - ath_restart_work(sc); - ath9k_start_btcoex(sc); - - ath9k_ps_restore(sc); - mutex_unlock(&sc->mutex); - - return 0; -} - -static void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) -{ - struct ath_softc *sc = hw->priv; - - mutex_lock(&sc->mutex); - device_init_wakeup(sc->dev, 1); - device_set_wakeup_enable(sc->dev, enabled); - mutex_unlock(&sc->mutex); -} - -#endif 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); -} - -static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_chan_def *chandef) -{ - struct ath_softc *sc = hw->priv; - - /* mac80211 does not support CSA in multi-if cases (yet) */ - if (WARN_ON(sc->csa_vif)) - return; - - sc->csa_vif = vif; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + clear_bit(ATH_OP_SCANNING, &common->op_flags); } struct ieee80211_ops ath9k_ops = { @@ -2362,7 +2260,7 @@ struct ieee80211_ops ath9k_ops = { .set_antenna = ath9k_set_antenna, .get_antenna = ath9k_get_antenna, -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_ATH9K_WOW .suspend = ath9k_suspend, .resume = ath9k_resume, .set_wakeup = ath9k_set_wakeup, @@ -2374,10 +2272,9 @@ struct ieee80211_ops ath9k_ops = { .get_et_strings = ath9k_get_et_strings, #endif -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_STATION_STATISTICS) .sta_add_debugfs = ath9k_sta_add_debugfs, #endif .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, - .channel_switch_beacon = ath9k_channel_switch_beacon, }; diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 815bee21c19..a0dbcc41238 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -200,7 +200,7 @@ skip_tuning: if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; - btcoex->btcoex_no_stomp = btcoex->btcoex_period * 1000 * + btcoex->btcoex_no_stomp = btcoex->btcoex_period * (100 - btcoex->duty_cycle) / 100; ath9k_hw_btcoex_enable(sc->sc_ah); @@ -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; @@ -661,9 +661,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) chan_start = wlan_chan - 10; chan_end = wlan_chan + 10; - if (chan->chanmode == CHANNEL_G_HT40PLUS) + if (IS_CHAN_HT40PLUS(chan)) chan_end += 20; - else if (chan->chanmode == CHANNEL_G_HT40MINUS) + else if (IS_CHAN_HT40MINUS(chan)) chan_start -= 20; /* adjust side band */ @@ -707,11 +707,11 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, if (setchannel) { struct ath9k_hw_cal_data *caldata = &sc->caldata; - if ((caldata->chanmode == CHANNEL_G_HT40PLUS) && + if (IS_CHAN_HT40PLUS(ah->curchan) && (ah->curchan->channel > caldata->channel) && (ah->curchan->channel <= caldata->channel + 20)) return; - if ((caldata->chanmode == CHANNEL_G_HT40MINUS) && + if (IS_CHAN_HT40MINUS(ah->curchan) && (ah->curchan->channel < caldata->channel) && (ah->curchan->channel >= caldata->channel - 20)) return; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index d089a7cf01c..4dec09e565e 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -87,6 +87,19 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */ { PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x002E) }, /* PCI-E */ + + /* Killer Wireless (3x3) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2000), + .driver_data = ATH9K_PCI_KILLER }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2001), + .driver_data = ATH9K_PCI_KILLER }, + { PCI_VDEVICE(ATHEROS, 0x0030) }, /* PCI-E AR9300 */ /* PCI-E CUS198 */ @@ -195,6 +208,93 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { 0x3219), .driver_data = ATH9K_PCI_BT_ANT_DIV }, + /* AR9485 cards with PLL power-save disabled by default. */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2C97), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2100), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1C56, /* ASKEY */ + 0x4001), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x11AD, /* LITEON */ + 0x6627), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x11AD, /* LITEON */ + 0x6628), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE04E), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE04F), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x144F, /* ASKEY */ + 0x7197), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x2000), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x2001), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1186), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1F86), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1195), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1F95), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x1C00), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x1C01), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ASUSTEK, + 0x850D), + .driver_data = ATH9K_PCI_NO_PLL_PWRSAVE }, + { PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */ { PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */ @@ -267,9 +367,297 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { 0x1783), .driver_data = ATH9K_PCI_WOW }, + /* Killer Wireless (2x2) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2003), + .driver_data = ATH9K_PCI_KILLER }, + { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */ - { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E AR9565 */ + + /* CUS252 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3028), + .driver_data = ATH9K_PCI_CUS252 | + ATH9K_PCI_AR9565_2ANT | + ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2176), + .driver_data = ATH9K_PCI_CUS252 | + ATH9K_PCI_AR9565_2ANT | + ATH9K_PCI_BT_ANT_DIV }, + + /* WB335 1-ANT */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE068), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA119), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0632), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x06B2), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0842), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x6671), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2811), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2812), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x28A1), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x218A), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + + /* WB335 1-ANT / Antenna Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3025), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3026), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302B), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE069), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3028), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0622), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0672), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0662), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x06A2), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0682), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213A), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x18E3), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x217F), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x2005), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020C), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + + /* WB335 2-ANT / Antenna-Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411A), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411B), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411C), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411D), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411E), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3027), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302C), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0642), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0652), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0612), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0832), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0692), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2130), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213B), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2182), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x144F, /* ASKEY */ + 0x7202), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2810), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x28A2), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA120), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE07F), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE081), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x3026), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x4026), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ASUSTEK, + 0x85F2), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020E), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + + /* PCI-E AR9565 (WB335) */ + { PCI_VDEVICE(ATHEROS, 0x0036), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { 0 } }; @@ -298,7 +686,7 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) struct ath_softc *sc = (struct ath_softc *) common->priv; struct ath9k_platform_data *pdata = sc->dev->platform_data; - if (pdata) { + if (pdata && !pdata->use_eeprom) { if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { ath_err(common, "%s: eeprom read failed, offset %08x is out of range\n", @@ -470,9 +858,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"); @@ -529,6 +914,7 @@ static int ath_pci_suspend(struct device *device) */ ath9k_stop_btcoex(sc); ath9k_hw_disable(sc->sc_ah); + del_timer_sync(&sc->sleep_timer); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); return 0; diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c deleted file mode 100644 index d3d7c51fa6c..00000000000 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ /dev/null @@ -1,1495 +0,0 @@ -/* - * Copyright (c) 2004 Video54 Technologies, Inc. - * Copyright (c) 2004-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 <linux/slab.h> -#include <linux/export.h> - -#include "ath9k.h" - -static const struct ath_rate_table ar5416_11na_ratetable = { - 68, - 8, /* MCS start */ - { - [0] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, - 5400, 0, 12 }, /* 6 Mb */ - [1] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, - 7800, 1, 18 }, /* 9 Mb */ - [2] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, - 10000, 2, 24 }, /* 12 Mb */ - [3] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, - 13900, 3, 36 }, /* 18 Mb */ - [4] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, - 17300, 4, 48 }, /* 24 Mb */ - [5] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, - 23000, 5, 72 }, /* 36 Mb */ - [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, - 27400, 6, 96 }, /* 48 Mb */ - [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, - 29300, 7, 108 }, /* 54 Mb */ - [8] = { RC_HT_SDT_2040, WLAN_RC_PHY_HT_20_SS, 6500, - 6400, 0, 0 }, /* 6.5 Mb */ - [9] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000, - 12700, 1, 1 }, /* 13 Mb */ - [10] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500, - 18800, 2, 2 }, /* 19.5 Mb */ - [11] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000, - 25000, 3, 3 }, /* 26 Mb */ - [12] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000, - 36700, 4, 4 }, /* 39 Mb */ - [13] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000, - 48100, 5, 5 }, /* 52 Mb */ - [14] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500, - 53500, 6, 6 }, /* 58.5 Mb */ - [15] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000, - 59000, 7, 7 }, /* 65 Mb */ - [16] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200, - 65400, 7, 7 }, /* 75 Mb */ - [17] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000, - 12700, 8, 8 }, /* 13 Mb */ - [18] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000, - 24800, 9, 9 }, /* 26 Mb */ - [19] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000, - 36600, 10, 10 }, /* 39 Mb */ - [20] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000, - 48100, 11, 11 }, /* 52 Mb */ - [21] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000, - 69500, 12, 12 }, /* 78 Mb */ - [22] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000, - 89500, 13, 13 }, /* 104 Mb */ - [23] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000, - 98900, 14, 14 }, /* 117 Mb */ - [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000, - 108300, 15, 15 }, /* 130 Mb */ - [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400, - 120000, 15, 15 }, /* 144.4 Mb */ - [26] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500, - 17400, 16, 16 }, /* 19.5 Mb */ - [27] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000, - 35100, 17, 17 }, /* 39 Mb */ - [28] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500, - 52600, 18, 18 }, /* 58.5 Mb */ - [29] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000, - 70400, 19, 19 }, /* 78 Mb */ - [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000, - 104900, 20, 20 }, /* 117 Mb */ - [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000, - 115800, 20, 20 }, /* 130 Mb*/ - [32] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000, - 137200, 21, 21 }, /* 156 Mb */ - [33] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300, - 151100, 21, 21 }, /* 173.3 Mb */ - [34] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500, - 152800, 22, 22 }, /* 175.5 Mb */ - [35] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000, - 168400, 22, 22 }, /* 195 Mb*/ - [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000, - 168400, 23, 23 }, /* 195 Mb */ - [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700, - 185000, 23, 23 }, /* 216.7 Mb */ - [38] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500, - 13200, 0, 0 }, /* 13.5 Mb*/ - [39] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500, - 25900, 1, 1 }, /* 27.0 Mb*/ - [40] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500, - 38600, 2, 2 }, /* 40.5 Mb*/ - [41] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000, - 49800, 3, 3 }, /* 54 Mb */ - [42] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500, - 72200, 4, 4 }, /* 81 Mb */ - [43] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 108000, - 92900, 5, 5 }, /* 108 Mb */ - [44] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500, - 102700, 6, 6 }, /* 121.5 Mb*/ - [45] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000, - 112000, 7, 7 }, /* 135 Mb */ - [46] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, - 122000, 7, 7 }, /* 150 Mb */ - [47] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000, - 25800, 8, 8 }, /* 27 Mb */ - [48] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000, - 49800, 9, 9 }, /* 54 Mb */ - [49] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000, - 71900, 10, 10 }, /* 81 Mb */ - [50] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000, - 92500, 11, 11 }, /* 108 Mb */ - [51] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000, - 130300, 12, 12 }, /* 162 Mb */ - [52] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000, - 162800, 13, 13 }, /* 216 Mb */ - [53] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000, - 178200, 14, 14 }, /* 243 Mb */ - [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000, - 192100, 15, 15 }, /* 270 Mb */ - [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000, - 207000, 15, 15 }, /* 300 Mb */ - [56] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500, - 36100, 16, 16 }, /* 40.5 Mb */ - [57] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000, - 72900, 17, 17 }, /* 81 Mb */ - [58] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500, - 108300, 18, 18 }, /* 121.5 Mb */ - [59] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000, - 142000, 19, 19 }, /* 162 Mb */ - [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000, - 205100, 20, 20 }, /* 243 Mb */ - [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000, - 224700, 20, 20 }, /* 270 Mb */ - [62] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000, - 263100, 21, 21 }, /* 324 Mb */ - [63] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000, - 288000, 21, 21 }, /* 360 Mb */ - [64] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500, - 290700, 22, 22 }, /* 364.5 Mb */ - [65] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000, - 317200, 22, 22 }, /* 405 Mb */ - [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000, - 317200, 23, 23 }, /* 405 Mb */ - [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000, - 346400, 23, 23 }, /* 450 Mb */ - }, - 50, /* probe interval */ - WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ -}; - -/* 4ms frame limit not used for NG mode. The values filled - * for HT are the 64K max aggregate limit */ - -static const struct ath_rate_table ar5416_11ng_ratetable = { - 72, - 12, /* MCS start */ - { - [0] = { RC_ALL, WLAN_RC_PHY_CCK, 1000, - 900, 0, 2 }, /* 1 Mb */ - [1] = { RC_ALL, WLAN_RC_PHY_CCK, 2000, - 1900, 1, 4 }, /* 2 Mb */ - [2] = { RC_ALL, WLAN_RC_PHY_CCK, 5500, - 4900, 2, 11 }, /* 5.5 Mb */ - [3] = { RC_ALL, WLAN_RC_PHY_CCK, 11000, - 8100, 3, 22 }, /* 11 Mb */ - [4] = { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, - 5400, 4, 12 }, /* 6 Mb */ - [5] = { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, - 7800, 5, 18 }, /* 9 Mb */ - [6] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, - 10100, 6, 24 }, /* 12 Mb */ - [7] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, - 14100, 7, 36 }, /* 18 Mb */ - [8] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, - 17700, 8, 48 }, /* 24 Mb */ - [9] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, - 23700, 9, 72 }, /* 36 Mb */ - [10] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, - 27400, 10, 96 }, /* 48 Mb */ - [11] = { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, - 30900, 11, 108 }, /* 54 Mb */ - [12] = { RC_INVALID, WLAN_RC_PHY_HT_20_SS, 6500, - 6400, 0, 0 }, /* 6.5 Mb */ - [13] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 13000, - 12700, 1, 1 }, /* 13 Mb */ - [14] = { RC_HT_SDT_20, WLAN_RC_PHY_HT_20_SS, 19500, - 18800, 2, 2 }, /* 19.5 Mb*/ - [15] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 26000, - 25000, 3, 3 }, /* 26 Mb */ - [16] = { RC_HT_SD_20, WLAN_RC_PHY_HT_20_SS, 39000, - 36700, 4, 4 }, /* 39 Mb */ - [17] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 52000, - 48100, 5, 5 }, /* 52 Mb */ - [18] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 58500, - 53500, 6, 6 }, /* 58.5 Mb */ - [19] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS, 65000, - 59000, 7, 7 }, /* 65 Mb */ - [20] = { RC_HT_S_20, WLAN_RC_PHY_HT_20_SS_HGI, 72200, - 65400, 7, 7 }, /* 65 Mb*/ - [21] = { RC_INVALID, WLAN_RC_PHY_HT_20_DS, 13000, - 12700, 8, 8 }, /* 13 Mb */ - [22] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 26000, - 24800, 9, 9 }, /* 26 Mb */ - [23] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_DS, 39000, - 36600, 10, 10 }, /* 39 Mb */ - [24] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 52000, - 48100, 11, 11 }, /* 52 Mb */ - [25] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 78000, - 69500, 12, 12 }, /* 78 Mb */ - [26] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 104000, - 89500, 13, 13 }, /* 104 Mb */ - [27] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 117000, - 98900, 14, 14 }, /* 117 Mb */ - [28] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS, 130000, - 108300, 15, 15 }, /* 130 Mb */ - [29] = { RC_HT_DT_20, WLAN_RC_PHY_HT_20_DS_HGI, 144400, - 120000, 15, 15 }, /* 144.4 Mb */ - [30] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 19500, - 17400, 16, 16 }, /* 19.5 Mb */ - [31] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 39000, - 35100, 17, 17 }, /* 39 Mb */ - [32] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 58500, - 52600, 18, 18 }, /* 58.5 Mb */ - [33] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 78000, - 70400, 19, 19 }, /* 78 Mb */ - [34] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS, 117000, - 104900, 20, 20 }, /* 117 Mb */ - [35] = { RC_INVALID, WLAN_RC_PHY_HT_20_TS_HGI, 130000, - 115800, 20, 20 }, /* 130 Mb */ - [36] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 156000, - 137200, 21, 21 }, /* 156 Mb */ - [37] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 173300, - 151100, 21, 21 }, /* 173.3 Mb */ - [38] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 175500, - 152800, 22, 22 }, /* 175.5 Mb */ - [39] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 195000, - 168400, 22, 22 }, /* 195 Mb */ - [40] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS, 195000, - 168400, 23, 23 }, /* 195 Mb */ - [41] = { RC_HT_T_20, WLAN_RC_PHY_HT_20_TS_HGI, 216700, - 185000, 23, 23 }, /* 216.7 Mb */ - [42] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 13500, - 13200, 0, 0 }, /* 13.5 Mb */ - [43] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 27500, - 25900, 1, 1 }, /* 27.0 Mb */ - [44] = { RC_HT_SDT_40, WLAN_RC_PHY_HT_40_SS, 40500, - 38600, 2, 2 }, /* 40.5 Mb */ - [45] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 54000, - 49800, 3, 3 }, /* 54 Mb */ - [46] = { RC_HT_SD_40, WLAN_RC_PHY_HT_40_SS, 81500, - 72200, 4, 4 }, /* 81 Mb */ - [47] = { RC_HT_S_40 , WLAN_RC_PHY_HT_40_SS, 108000, - 92900, 5, 5 }, /* 108 Mb */ - [48] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 121500, - 102700, 6, 6 }, /* 121.5 Mb */ - [49] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS, 135000, - 112000, 7, 7 }, /* 135 Mb */ - [50] = { RC_HT_S_40, WLAN_RC_PHY_HT_40_SS_HGI, 150000, - 122000, 7, 7 }, /* 150 Mb */ - [51] = { RC_INVALID, WLAN_RC_PHY_HT_40_DS, 27000, - 25800, 8, 8 }, /* 27 Mb */ - [52] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 54000, - 49800, 9, 9 }, /* 54 Mb */ - [53] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_DS, 81000, - 71900, 10, 10 }, /* 81 Mb */ - [54] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 108000, - 92500, 11, 11 }, /* 108 Mb */ - [55] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 162000, - 130300, 12, 12 }, /* 162 Mb */ - [56] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 216000, - 162800, 13, 13 }, /* 216 Mb */ - [57] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 243000, - 178200, 14, 14 }, /* 243 Mb */ - [58] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS, 270000, - 192100, 15, 15 }, /* 270 Mb */ - [59] = { RC_HT_DT_40, WLAN_RC_PHY_HT_40_DS_HGI, 300000, - 207000, 15, 15 }, /* 300 Mb */ - [60] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 40500, - 36100, 16, 16 }, /* 40.5 Mb */ - [61] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 81000, - 72900, 17, 17 }, /* 81 Mb */ - [62] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 121500, - 108300, 18, 18 }, /* 121.5 Mb */ - [63] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 162000, - 142000, 19, 19 }, /* 162 Mb */ - [64] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS, 243000, - 205100, 20, 20 }, /* 243 Mb */ - [65] = { RC_INVALID, WLAN_RC_PHY_HT_40_TS_HGI, 270000, - 224700, 20, 20 }, /* 270 Mb */ - [66] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 324000, - 263100, 21, 21 }, /* 324 Mb */ - [67] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 360000, - 288000, 21, 21 }, /* 360 Mb */ - [68] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 364500, - 290700, 22, 22 }, /* 364.5 Mb */ - [69] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 405000, - 317200, 22, 22 }, /* 405 Mb */ - [70] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS, 405000, - 317200, 23, 23 }, /* 405 Mb */ - [71] = { RC_HT_T_40, WLAN_RC_PHY_HT_40_TS_HGI, 450000, - 346400, 23, 23 }, /* 450 Mb */ - }, - 50, /* probe interval */ - WLAN_RC_HT_FLAG, /* Phy rates allowed initially */ -}; - -static const struct ath_rate_table ar5416_11a_ratetable = { - 8, - 0, - { - { RC_L_SDT, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ - 5400, 0, 12}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ - 7800, 1, 18}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ - 10000, 2, 24}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ - 13900, 3, 36}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ - 17300, 4, 48}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ - 23000, 5, 72}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ - 27400, 6, 96}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ - 29300, 7, 108}, - }, - 50, /* probe interval */ - 0, /* Phy rates allowed initially */ -}; - -static const struct ath_rate_table ar5416_11g_ratetable = { - 12, - 0, - { - { RC_L_SDT, WLAN_RC_PHY_CCK, 1000, /* 1 Mb */ - 900, 0, 2}, - { RC_L_SDT, WLAN_RC_PHY_CCK, 2000, /* 2 Mb */ - 1900, 1, 4}, - { RC_L_SDT, WLAN_RC_PHY_CCK, 5500, /* 5.5 Mb */ - 4900, 2, 11}, - { RC_L_SDT, WLAN_RC_PHY_CCK, 11000, /* 11 Mb */ - 8100, 3, 22}, - { RC_INVALID, WLAN_RC_PHY_OFDM, 6000, /* 6 Mb */ - 5400, 4, 12}, - { RC_INVALID, WLAN_RC_PHY_OFDM, 9000, /* 9 Mb */ - 7800, 5, 18}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 12000, /* 12 Mb */ - 10000, 6, 24}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 18000, /* 18 Mb */ - 13900, 7, 36}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 24000, /* 24 Mb */ - 17300, 8, 48}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 36000, /* 36 Mb */ - 23000, 9, 72}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 48000, /* 48 Mb */ - 27400, 10, 96}, - { RC_L_SDT, WLAN_RC_PHY_OFDM, 54000, /* 54 Mb */ - 29300, 11, 108}, - }, - 50, /* probe interval */ - 0, /* Phy rates allowed initially */ -}; - -static int ath_rc_get_rateindex(struct ath_rate_priv *ath_rc_priv, - struct ieee80211_tx_rate *rate) -{ - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - int rix, i, idx = 0; - - if (!(rate->flags & IEEE80211_TX_RC_MCS)) - return rate->idx; - - for (i = 0; i < ath_rc_priv->max_valid_rate; i++) { - idx = ath_rc_priv->valid_rate_index[i]; - - if (WLAN_RC_PHY_HT(rate_table->info[idx].phy) && - rate_table->info[idx].ratecode == rate->idx) - break; - } - - rix = idx; - - if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - rix++; - - return rix; -} - -static void ath_rc_sort_validrates(struct ath_rate_priv *ath_rc_priv) -{ - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - u8 i, j, idx, idx_next; - - for (i = ath_rc_priv->max_valid_rate - 1; i > 0; i--) { - for (j = 0; j <= i-1; j++) { - idx = ath_rc_priv->valid_rate_index[j]; - idx_next = ath_rc_priv->valid_rate_index[j+1]; - - if (rate_table->info[idx].ratekbps > - rate_table->info[idx_next].ratekbps) { - ath_rc_priv->valid_rate_index[j] = idx_next; - ath_rc_priv->valid_rate_index[j+1] = idx; - } - } - } -} - -static inline -int ath_rc_get_nextvalid_txrate(const struct ath_rate_table *rate_table, - struct ath_rate_priv *ath_rc_priv, - u8 cur_valid_txrate, - u8 *next_idx) -{ - u8 i; - - for (i = 0; i < ath_rc_priv->max_valid_rate - 1; i++) { - if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) { - *next_idx = ath_rc_priv->valid_rate_index[i+1]; - return 1; - } - } - - /* No more valid rates */ - *next_idx = 0; - - return 0; -} - -/* Return true only for single stream */ - -static int ath_rc_valid_phyrate(u32 phy, u32 capflag, int ignore_cw) -{ - if (WLAN_RC_PHY_HT(phy) && !(capflag & WLAN_RC_HT_FLAG)) - return 0; - if (WLAN_RC_PHY_DS(phy) && !(capflag & WLAN_RC_DS_FLAG)) - return 0; - if (WLAN_RC_PHY_TS(phy) && !(capflag & WLAN_RC_TS_FLAG)) - return 0; - if (WLAN_RC_PHY_SGI(phy) && !(capflag & WLAN_RC_SGI_FLAG)) - return 0; - if (!ignore_cw && WLAN_RC_PHY_HT(phy)) - if (WLAN_RC_PHY_40(phy) && !(capflag & WLAN_RC_40_FLAG)) - return 0; - return 1; -} - -static inline int -ath_rc_get_lower_rix(struct ath_rate_priv *ath_rc_priv, - u8 cur_valid_txrate, u8 *next_idx) -{ - int8_t i; - - for (i = 1; i < ath_rc_priv->max_valid_rate ; i++) { - if (ath_rc_priv->valid_rate_index[i] == cur_valid_txrate) { - *next_idx = ath_rc_priv->valid_rate_index[i-1]; - return 1; - } - } - - return 0; -} - -static u8 ath_rc_init_validrates(struct ath_rate_priv *ath_rc_priv) -{ - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - u8 i, hi = 0; - - for (i = 0; i < rate_table->rate_cnt; i++) { - if (rate_table->info[i].rate_flags & RC_LEGACY) { - u32 phy = rate_table->info[i].phy; - u8 valid_rate_count = 0; - - if (!ath_rc_valid_phyrate(phy, ath_rc_priv->ht_cap, 0)) - continue; - - valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy]; - - ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = i; - ath_rc_priv->valid_phy_ratecnt[phy] += 1; - ath_rc_priv->valid_rate_index[i] = true; - hi = i; - } - } - - return hi; -} - -static inline bool ath_rc_check_legacy(u8 rate, u8 dot11rate, u16 rate_flags, - u32 phy, u32 capflag) -{ - if (rate != dot11rate || WLAN_RC_PHY_HT(phy)) - return false; - - if ((rate_flags & WLAN_RC_CAP_MODE(capflag)) != WLAN_RC_CAP_MODE(capflag)) - return false; - - if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag))) - return false; - - return true; -} - -static inline bool ath_rc_check_ht(u8 rate, u8 dot11rate, u16 rate_flags, - u32 phy, u32 capflag) -{ - if (rate != dot11rate || !WLAN_RC_PHY_HT(phy)) - return false; - - if (!WLAN_RC_PHY_HT_VALID(rate_flags, capflag)) - return false; - - if (!(rate_flags & WLAN_RC_CAP_STREAM(capflag))) - return false; - - return true; -} - -static u8 ath_rc_setvalid_rates(struct ath_rate_priv *ath_rc_priv, bool legacy) -{ - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - struct ath_rateset *rateset; - u32 phy, capflag = ath_rc_priv->ht_cap; - u16 rate_flags; - u8 i, j, hi = 0, rate, dot11rate, valid_rate_count; - - if (legacy) - rateset = &ath_rc_priv->neg_rates; - else - rateset = &ath_rc_priv->neg_ht_rates; - - for (i = 0; i < rateset->rs_nrates; i++) { - for (j = 0; j < rate_table->rate_cnt; j++) { - phy = rate_table->info[j].phy; - rate_flags = rate_table->info[j].rate_flags; - rate = rateset->rs_rates[i]; - dot11rate = rate_table->info[j].dot11rate; - - if (legacy && - !ath_rc_check_legacy(rate, dot11rate, - rate_flags, phy, capflag)) - continue; - - if (!legacy && - !ath_rc_check_ht(rate, dot11rate, - rate_flags, phy, capflag)) - continue; - - if (!ath_rc_valid_phyrate(phy, capflag, 0)) - continue; - - valid_rate_count = ath_rc_priv->valid_phy_ratecnt[phy]; - ath_rc_priv->valid_phy_rateidx[phy][valid_rate_count] = j; - ath_rc_priv->valid_phy_ratecnt[phy] += 1; - ath_rc_priv->valid_rate_index[j] = true; - hi = max(hi, j); - } - } - - return hi; -} - -static u8 ath_rc_get_highest_rix(struct ath_rate_priv *ath_rc_priv, - int *is_probing) -{ - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - u32 best_thruput, this_thruput, now_msec; - u8 rate, next_rate, best_rate, maxindex, minindex; - int8_t index = 0; - - now_msec = jiffies_to_msecs(jiffies); - *is_probing = 0; - best_thruput = 0; - maxindex = ath_rc_priv->max_valid_rate-1; - minindex = 0; - best_rate = minindex; - - /* - * Try the higher rate first. It will reduce memory moving time - * if we have very good channel characteristics. - */ - for (index = maxindex; index >= minindex ; index--) { - u8 per_thres; - - rate = ath_rc_priv->valid_rate_index[index]; - if (rate > ath_rc_priv->rate_max_phy) - continue; - - /* - * For TCP the average collision rate is around 11%, - * so we ignore PERs less than this. This is to - * prevent the rate we are currently using (whose - * PER might be in the 10-15 range because of TCP - * collisions) looking worse than the next lower - * rate whose PER has decayed close to 0. If we - * used to next lower rate, its PER would grow to - * 10-15 and we would be worse off then staying - * at the current rate. - */ - per_thres = ath_rc_priv->per[rate]; - if (per_thres < 12) - per_thres = 12; - - this_thruput = rate_table->info[rate].user_ratekbps * - (100 - per_thres); - - if (best_thruput <= this_thruput) { - best_thruput = this_thruput; - best_rate = rate; - } - } - - rate = best_rate; - - /* - * Must check the actual rate (ratekbps) to account for - * non-monoticity of 11g's rate table - */ - - if (rate >= ath_rc_priv->rate_max_phy) { - rate = ath_rc_priv->rate_max_phy; - - /* Probe the next allowed phy state */ - if (ath_rc_get_nextvalid_txrate(rate_table, - ath_rc_priv, rate, &next_rate) && - (now_msec - ath_rc_priv->probe_time > - rate_table->probe_interval) && - (ath_rc_priv->hw_maxretry_pktcnt >= 1)) { - rate = next_rate; - ath_rc_priv->probe_rate = rate; - ath_rc_priv->probe_time = now_msec; - ath_rc_priv->hw_maxretry_pktcnt = 0; - *is_probing = 1; - } - } - - if (rate > (ath_rc_priv->rate_table_size - 1)) - rate = ath_rc_priv->rate_table_size - 1; - - if (RC_TS_ONLY(rate_table->info[rate].rate_flags) && - (ath_rc_priv->ht_cap & WLAN_RC_TS_FLAG)) - return rate; - - if (RC_DS_OR_LATER(rate_table->info[rate].rate_flags) && - (ath_rc_priv->ht_cap & (WLAN_RC_DS_FLAG | WLAN_RC_TS_FLAG))) - return rate; - - if (RC_SS_OR_LEGACY(rate_table->info[rate].rate_flags)) - return rate; - - /* This should not happen */ - WARN_ON_ONCE(1); - - rate = ath_rc_priv->valid_rate_index[0]; - - return rate; -} - -static void ath_rc_rate_set_series(const struct ath_rate_table *rate_table, - struct ieee80211_tx_rate *rate, - struct ieee80211_tx_rate_control *txrc, - u8 tries, u8 rix, int rtsctsenable) -{ - rate->count = tries; - rate->idx = rate_table->info[rix].ratecode; - - if (txrc->rts || rtsctsenable) - rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; - - if (WLAN_RC_PHY_HT(rate_table->info[rix].phy)) { - rate->flags |= IEEE80211_TX_RC_MCS; - if (WLAN_RC_PHY_40(rate_table->info[rix].phy) && - conf_is_ht40(&txrc->hw->conf)) - rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (WLAN_RC_PHY_SGI(rate_table->info[rix].phy)) - rate->flags |= IEEE80211_TX_RC_SHORT_GI; - } -} - -static void ath_rc_rate_set_rtscts(struct ath_softc *sc, - const struct ath_rate_table *rate_table, - struct ieee80211_tx_info *tx_info) -{ - struct ieee80211_bss_conf *bss_conf; - - if (!tx_info->control.vif) - return; - /* - * For legacy frames, mac80211 takes care of CTS protection. - */ - if (!(tx_info->control.rates[0].flags & IEEE80211_TX_RC_MCS)) - return; - - bss_conf = &tx_info->control.vif->bss_conf; - - if (!bss_conf->basic_rates) - return; - - /* - * For now, use the lowest allowed basic rate for HT frames. - */ - tx_info->control.rts_cts_rate_idx = __ffs(bss_conf->basic_rates); -} - -static void ath_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, - struct ieee80211_tx_rate_control *txrc) -{ - struct ath_softc *sc = priv; - struct ath_rate_priv *ath_rc_priv = priv_sta; - const struct ath_rate_table *rate_table; - struct sk_buff *skb = txrc->skb; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *rates = tx_info->control.rates; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - __le16 fc = hdr->frame_control; - u8 try_per_rate, i = 0, rix; - int is_probe = 0; - - if (rate_control_send_low(sta, priv_sta, txrc)) - return; - - /* - * For Multi Rate Retry we use a different number of - * retry attempt counts. This ends up looking like this: - * - * MRR[0] = 4 - * MRR[1] = 4 - * MRR[2] = 4 - * MRR[3] = 8 - * - */ - try_per_rate = 4; - - rate_table = ath_rc_priv->rate_table; - rix = ath_rc_get_highest_rix(ath_rc_priv, &is_probe); - - if (conf_is_ht(&sc->hw->conf) && - (sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) - tx_info->flags |= IEEE80211_TX_CTL_LDPC; - - if (conf_is_ht(&sc->hw->conf) && - (sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)) - tx_info->flags |= (1 << IEEE80211_TX_CTL_STBC_SHIFT); - - if (is_probe) { - /* - * Set one try for probe rates. For the - * probes don't enable RTS. - */ - ath_rc_rate_set_series(rate_table, &rates[i++], txrc, - 1, rix, 0); - /* - * Get the next tried/allowed rate. - * No RTS for the next series after the probe rate. - */ - ath_rc_get_lower_rix(ath_rc_priv, rix, &rix); - ath_rc_rate_set_series(rate_table, &rates[i++], txrc, - try_per_rate, rix, 0); - - tx_info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; - } else { - /* - * Set the chosen rate. No RTS for first series entry. - */ - ath_rc_rate_set_series(rate_table, &rates[i++], txrc, - try_per_rate, rix, 0); - } - - for ( ; i < 4; i++) { - /* - * Use twice the number of tries for the last MRR segment. - */ - if (i + 1 == 4) - try_per_rate = 8; - - ath_rc_get_lower_rix(ath_rc_priv, rix, &rix); - - /* - * All other rates in the series have RTS enabled. - */ - ath_rc_rate_set_series(rate_table, &rates[i], txrc, - try_per_rate, rix, 1); - } - - /* - * NB:Change rate series to enable aggregation when operating - * at lower MCS rates. When first rate in series is MCS2 - * in HT40 @ 2.4GHz, series should look like: - * - * {MCS2, MCS1, MCS0, MCS0}. - * - * When first rate in series is MCS3 in HT20 @ 2.4GHz, series should - * look like: - * - * {MCS3, MCS2, MCS1, MCS1} - * - * So, set fourth rate in series to be same as third one for - * above conditions. - */ - if ((sc->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) && - (conf_is_ht(&sc->hw->conf))) { - u8 dot11rate = rate_table->info[rix].dot11rate; - u8 phy = rate_table->info[rix].phy; - if (i == 4 && - ((dot11rate == 2 && phy == WLAN_RC_PHY_HT_40_SS) || - (dot11rate == 3 && phy == WLAN_RC_PHY_HT_20_SS))) { - rates[3].idx = rates[2].idx; - rates[3].flags = rates[2].flags; - } - } - - /* - * Force hardware to use computed duration for next - * fragment by disabling multi-rate retry, which - * updates duration based on the multi-rate duration table. - * - * FIXME: Fix duration - */ - if (ieee80211_has_morefrags(fc) || - (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)) { - rates[1].count = rates[2].count = rates[3].count = 0; - rates[1].idx = rates[2].idx = rates[3].idx = 0; - rates[0].count = ATH_TXMAXTRY; - } - - ath_rc_rate_set_rtscts(sc, rate_table, tx_info); -} - -static void ath_rc_update_per(struct ath_softc *sc, - const struct ath_rate_table *rate_table, - struct ath_rate_priv *ath_rc_priv, - struct ieee80211_tx_info *tx_info, - int tx_rate, int xretries, int retries, - u32 now_msec) -{ - int count, n_bad_frames; - u8 last_per; - static const u32 nretry_to_per_lookup[10] = { - 100 * 0 / 1, - 100 * 1 / 4, - 100 * 1 / 2, - 100 * 3 / 4, - 100 * 4 / 5, - 100 * 5 / 6, - 100 * 6 / 7, - 100 * 7 / 8, - 100 * 8 / 9, - 100 * 9 / 10 - }; - - last_per = ath_rc_priv->per[tx_rate]; - n_bad_frames = tx_info->status.ampdu_len - tx_info->status.ampdu_ack_len; - - if (xretries) { - if (xretries == 1) { - ath_rc_priv->per[tx_rate] += 30; - if (ath_rc_priv->per[tx_rate] > 100) - ath_rc_priv->per[tx_rate] = 100; - } else { - /* xretries == 2 */ - count = ARRAY_SIZE(nretry_to_per_lookup); - if (retries >= count) - retries = count - 1; - - /* new_PER = 7/8*old_PER + 1/8*(currentPER) */ - ath_rc_priv->per[tx_rate] = - (u8)(last_per - (last_per >> 3) + (100 >> 3)); - } - - /* xretries == 1 or 2 */ - - if (ath_rc_priv->probe_rate == tx_rate) - ath_rc_priv->probe_rate = 0; - - } else { /* xretries == 0 */ - count = ARRAY_SIZE(nretry_to_per_lookup); - if (retries >= count) - retries = count - 1; - - if (n_bad_frames) { - /* new_PER = 7/8*old_PER + 1/8*(currentPER) - * Assuming that n_frames is not 0. The current PER - * from the retries is 100 * retries / (retries+1), - * since the first retries attempts failed, and the - * next one worked. For the one that worked, - * n_bad_frames subframes out of n_frames wored, - * so the PER for that part is - * 100 * n_bad_frames / n_frames, and it contributes - * 100 * n_bad_frames / (n_frames * (retries+1)) to - * the above PER. The expression below is a - * simplified version of the sum of these two terms. - */ - if (tx_info->status.ampdu_len > 0) { - int n_frames, n_bad_tries; - u8 cur_per, new_per; - - n_bad_tries = retries * tx_info->status.ampdu_len + - n_bad_frames; - n_frames = tx_info->status.ampdu_len * (retries + 1); - cur_per = (100 * n_bad_tries / n_frames) >> 3; - new_per = (u8)(last_per - (last_per >> 3) + cur_per); - ath_rc_priv->per[tx_rate] = new_per; - } - } else { - ath_rc_priv->per[tx_rate] = - (u8)(last_per - (last_per >> 3) + - (nretry_to_per_lookup[retries] >> 3)); - } - - - /* - * If we got at most one retry then increase the max rate if - * this was a probe. Otherwise, ignore the probe. - */ - if (ath_rc_priv->probe_rate && ath_rc_priv->probe_rate == tx_rate) { - if (retries > 0 || 2 * n_bad_frames > tx_info->status.ampdu_len) { - /* - * Since we probed with just a single attempt, - * any retries means the probe failed. Also, - * if the attempt worked, but more than half - * the subframes were bad then also consider - * the probe a failure. - */ - ath_rc_priv->probe_rate = 0; - } else { - u8 probe_rate = 0; - - ath_rc_priv->rate_max_phy = - ath_rc_priv->probe_rate; - probe_rate = ath_rc_priv->probe_rate; - - if (ath_rc_priv->per[probe_rate] > 30) - ath_rc_priv->per[probe_rate] = 20; - - ath_rc_priv->probe_rate = 0; - - /* - * Since this probe succeeded, we allow the next - * probe twice as soon. This allows the maxRate - * to move up faster if the probes are - * successful. - */ - ath_rc_priv->probe_time = - now_msec - rate_table->probe_interval / 2; - } - } - - if (retries > 0) { - /* - * Don't update anything. We don't know if - * this was because of collisions or poor signal. - */ - ath_rc_priv->hw_maxretry_pktcnt = 0; - } else { - /* - * It worked with no retries. First ignore bogus (small) - * rssi_ack values. - */ - if (tx_rate == ath_rc_priv->rate_max_phy && - ath_rc_priv->hw_maxretry_pktcnt < 255) { - ath_rc_priv->hw_maxretry_pktcnt++; - } - - } - } -} - -static void ath_rc_update_ht(struct ath_softc *sc, - struct ath_rate_priv *ath_rc_priv, - struct ieee80211_tx_info *tx_info, - int tx_rate, int xretries, int retries) -{ - u32 now_msec = jiffies_to_msecs(jiffies); - int rate; - u8 last_per; - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - int size = ath_rc_priv->rate_table_size; - - if ((tx_rate < 0) || (tx_rate > rate_table->rate_cnt)) - return; - - last_per = ath_rc_priv->per[tx_rate]; - - /* Update PER first */ - ath_rc_update_per(sc, rate_table, ath_rc_priv, - tx_info, tx_rate, xretries, - retries, now_msec); - - /* - * If this rate looks bad (high PER) then stop using it for - * a while (except if we are probing). - */ - if (ath_rc_priv->per[tx_rate] >= 55 && tx_rate > 0 && - rate_table->info[tx_rate].ratekbps <= - rate_table->info[ath_rc_priv->rate_max_phy].ratekbps) { - ath_rc_get_lower_rix(ath_rc_priv, (u8)tx_rate, - &ath_rc_priv->rate_max_phy); - - /* Don't probe for a little while. */ - ath_rc_priv->probe_time = now_msec; - } - - /* Make sure the rates below this have lower PER */ - /* Monotonicity is kept only for rates below the current rate. */ - if (ath_rc_priv->per[tx_rate] < last_per) { - for (rate = tx_rate - 1; rate >= 0; rate--) { - - if (ath_rc_priv->per[rate] > - ath_rc_priv->per[rate+1]) { - ath_rc_priv->per[rate] = - ath_rc_priv->per[rate+1]; - } - } - } - - /* Maintain monotonicity for rates above the current rate */ - for (rate = tx_rate; rate < size - 1; rate++) { - if (ath_rc_priv->per[rate+1] < - ath_rc_priv->per[rate]) - ath_rc_priv->per[rate+1] = - ath_rc_priv->per[rate]; - } - - /* Every so often, we reduce the thresholds - * and PER (different for CCK and OFDM). */ - if (now_msec - ath_rc_priv->per_down_time >= - rate_table->probe_interval) { - for (rate = 0; rate < size; rate++) { - ath_rc_priv->per[rate] = - 7 * ath_rc_priv->per[rate] / 8; - } - - ath_rc_priv->per_down_time = now_msec; - } - - ath_debug_stat_retries(ath_rc_priv, tx_rate, xretries, retries, - ath_rc_priv->per[tx_rate]); - -} - -static void ath_rc_tx_status(struct ath_softc *sc, - struct ath_rate_priv *ath_rc_priv, - struct sk_buff *skb) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_tx_rate *rates = tx_info->status.rates; - struct ieee80211_tx_rate *rate; - int final_ts_idx = 0, xretries = 0, long_retry = 0; - u8 flags; - u32 i = 0, rix; - - for (i = 0; i < sc->hw->max_rates; i++) { - rate = &tx_info->status.rates[i]; - if (rate->idx < 0 || !rate->count) - break; - - final_ts_idx = i; - long_retry = rate->count - 1; - } - - if (!(tx_info->flags & IEEE80211_TX_STAT_ACK)) - xretries = 1; - - /* - * If the first rate is not the final index, there - * are intermediate rate failures to be processed. - */ - if (final_ts_idx != 0) { - for (i = 0; i < final_ts_idx ; i++) { - if (rates[i].count != 0 && (rates[i].idx >= 0)) { - flags = rates[i].flags; - - /* If HT40 and we have switched mode from - * 40 to 20 => don't update */ - - if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && - !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG)) - return; - - rix = ath_rc_get_rateindex(ath_rc_priv, &rates[i]); - ath_rc_update_ht(sc, ath_rc_priv, tx_info, - rix, xretries ? 1 : 2, - rates[i].count); - } - } - } - - flags = rates[final_ts_idx].flags; - - /* If HT40 and we have switched mode from 40 to 20 => don't update */ - if ((flags & IEEE80211_TX_RC_40_MHZ_WIDTH) && - !(ath_rc_priv->ht_cap & WLAN_RC_40_FLAG)) - return; - - rix = ath_rc_get_rateindex(ath_rc_priv, &rates[final_ts_idx]); - ath_rc_update_ht(sc, ath_rc_priv, tx_info, rix, xretries, long_retry); - ath_debug_stat_rc(ath_rc_priv, rix); -} - -static const -struct ath_rate_table *ath_choose_rate_table(struct ath_softc *sc, - enum ieee80211_band band, - bool is_ht) -{ - switch(band) { - case IEEE80211_BAND_2GHZ: - if (is_ht) - return &ar5416_11ng_ratetable; - return &ar5416_11g_ratetable; - case IEEE80211_BAND_5GHZ: - if (is_ht) - return &ar5416_11na_ratetable; - return &ar5416_11a_ratetable; - default: - return NULL; - } -} - -static void ath_rc_init(struct ath_softc *sc, - struct ath_rate_priv *ath_rc_priv) -{ - const struct ath_rate_table *rate_table = ath_rc_priv->rate_table; - struct ath_rateset *rateset = &ath_rc_priv->neg_rates; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u8 i, j, k, hi = 0, hthi = 0; - - ath_rc_priv->rate_table_size = RATE_TABLE_SIZE; - - for (i = 0 ; i < ath_rc_priv->rate_table_size; i++) { - ath_rc_priv->per[i] = 0; - ath_rc_priv->valid_rate_index[i] = 0; - } - - for (i = 0; i < WLAN_RC_PHY_MAX; i++) { - for (j = 0; j < RATE_TABLE_SIZE; j++) - ath_rc_priv->valid_phy_rateidx[i][j] = 0; - ath_rc_priv->valid_phy_ratecnt[i] = 0; - } - - if (!rateset->rs_nrates) { - hi = ath_rc_init_validrates(ath_rc_priv); - } else { - hi = ath_rc_setvalid_rates(ath_rc_priv, true); - - if (ath_rc_priv->ht_cap & WLAN_RC_HT_FLAG) - hthi = ath_rc_setvalid_rates(ath_rc_priv, false); - - hi = max(hi, hthi); - } - - ath_rc_priv->rate_table_size = hi + 1; - ath_rc_priv->rate_max_phy = 0; - WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE); - - for (i = 0, k = 0; i < WLAN_RC_PHY_MAX; i++) { - for (j = 0; j < ath_rc_priv->valid_phy_ratecnt[i]; j++) { - ath_rc_priv->valid_rate_index[k++] = - ath_rc_priv->valid_phy_rateidx[i][j]; - } - - if (!ath_rc_valid_phyrate(i, rate_table->initial_ratemax, 1) || - !ath_rc_priv->valid_phy_ratecnt[i]) - continue; - - ath_rc_priv->rate_max_phy = ath_rc_priv->valid_phy_rateidx[i][j-1]; - } - WARN_ON(ath_rc_priv->rate_table_size > RATE_TABLE_SIZE); - WARN_ON(k > RATE_TABLE_SIZE); - - ath_rc_priv->max_valid_rate = k; - ath_rc_sort_validrates(ath_rc_priv); - ath_rc_priv->rate_max_phy = (k > 4) ? - ath_rc_priv->valid_rate_index[k-4] : - ath_rc_priv->valid_rate_index[k-1]; - - ath_dbg(common, CONFIG, "RC Initialized with capabilities: 0x%x\n", - ath_rc_priv->ht_cap); -} - -static u8 ath_rc_build_ht_caps(struct ath_softc *sc, struct ieee80211_sta *sta) -{ - u8 caps = 0; - - if (sta->ht_cap.ht_supported) { - caps = WLAN_RC_HT_FLAG; - if (sta->ht_cap.mcs.rx_mask[1] && sta->ht_cap.mcs.rx_mask[2]) - caps |= WLAN_RC_TS_FLAG | WLAN_RC_DS_FLAG; - else if (sta->ht_cap.mcs.rx_mask[1]) - caps |= WLAN_RC_DS_FLAG; - if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) { - caps |= WLAN_RC_40_FLAG; - if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) - caps |= WLAN_RC_SGI_FLAG; - } else { - if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) - caps |= WLAN_RC_SGI_FLAG; - } - } - - return caps; -} - -static bool ath_tx_aggr_check(struct ath_softc *sc, struct ieee80211_sta *sta, - u8 tidno) -{ - struct ath_node *an = (struct ath_node *)sta->drv_priv; - struct ath_atx_tid *txtid; - - if (!sta->ht_cap.ht_supported) - return false; - - txtid = ATH_AN_2_TID(an, tidno); - return !txtid->active; -} - - -/***********************************/ -/* mac80211 Rate Control callbacks */ -/***********************************/ - -static void ath_tx_status(void *priv, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta, - struct sk_buff *skb) -{ - struct ath_softc *sc = priv; - struct ath_rate_priv *ath_rc_priv = priv_sta; - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - __le16 fc = hdr->frame_control; - - if (!priv_sta || !ieee80211_is_data(fc)) - return; - - /* This packet was aggregated but doesn't carry status info */ - if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && - !(tx_info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) - return; - - ath_rc_tx_status(sc, ath_rc_priv, skb); - - /* Check if aggregation has to be enabled for this tid */ - if (conf_is_ht(&sc->hw->conf) && - !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { - if (ieee80211_is_data_qos(fc) && - skb_get_queue_mapping(skb) != IEEE80211_AC_VO) { - u8 *qc, tid; - - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & 0xf; - - if(ath_tx_aggr_check(sc, sta, tid)) - ieee80211_start_tx_ba_session(sta, tid, 0); - } - } -} - -static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *priv_sta) -{ - struct ath_softc *sc = priv; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_rate_priv *ath_rc_priv = priv_sta; - int i, j = 0; - u32 rate_flags = ieee80211_chandef_rate_flags(&sc->hw->conf.chandef); - - for (i = 0; i < sband->n_bitrates; i++) { - if (sta->supp_rates[sband->band] & BIT(i)) { - if ((rate_flags & sband->bitrates[i].flags) - != rate_flags) - continue; - - ath_rc_priv->neg_rates.rs_rates[j] - = (sband->bitrates[i].bitrate * 2) / 10; - j++; - } - } - ath_rc_priv->neg_rates.rs_nrates = j; - - if (sta->ht_cap.ht_supported) { - for (i = 0, j = 0; i < 77; i++) { - if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) - ath_rc_priv->neg_ht_rates.rs_rates[j++] = i; - if (j == ATH_RATE_MAX) - break; - } - ath_rc_priv->neg_ht_rates.rs_nrates = j; - } - - ath_rc_priv->rate_table = ath_choose_rate_table(sc, sband->band, - sta->ht_cap.ht_supported); - if (!ath_rc_priv->rate_table) { - ath_err(common, "No rate table chosen\n"); - return; - } - - ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta); - ath_rc_init(sc, priv_sta); -} - -static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, - struct cfg80211_chan_def *chandef, - struct ieee80211_sta *sta, void *priv_sta, - u32 changed) -{ - struct ath_softc *sc = priv; - struct ath_rate_priv *ath_rc_priv = priv_sta; - - if (changed & IEEE80211_RC_BW_CHANGED) { - ath_rc_priv->ht_cap = ath_rc_build_ht_caps(sc, sta); - ath_rc_init(sc, priv_sta); - - ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG, - "Operating Bandwidth changed to: %d\n", - sc->hw->conf.chandef.width); - } -} - -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) - -void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) -{ - struct ath_rc_stats *stats; - - stats = &rc->rcstats[final_rate]; - stats->success++; -} - -void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, - int xretries, int retries, u8 per) -{ - struct ath_rc_stats *stats = &rc->rcstats[rix]; - - stats->xretries += xretries; - stats->retries += retries; - stats->per = per; -} - -static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_rate_priv *rc = file->private_data; - char *buf; - unsigned int len = 0, max; - int rix; - ssize_t retval; - - if (rc->rate_table == NULL) - return 0; - - max = 80 + rc->rate_table_size * 1024 + 1; - buf = kmalloc(max, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - len += sprintf(buf, "%6s %6s %6s " - "%10s %10s %10s %10s\n", - "HT", "MCS", "Rate", - "Success", "Retries", "XRetries", "PER"); - - for (rix = 0; rix < rc->max_valid_rate; rix++) { - u8 i = rc->valid_rate_index[rix]; - u32 ratekbps = rc->rate_table->info[i].ratekbps; - struct ath_rc_stats *stats = &rc->rcstats[i]; - char mcs[5]; - char htmode[5]; - int used_mcs = 0, used_htmode = 0; - - if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) { - used_mcs = snprintf(mcs, 5, "%d", - rc->rate_table->info[i].ratecode); - - if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy)) - used_htmode = snprintf(htmode, 5, "HT40"); - else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy)) - used_htmode = snprintf(htmode, 5, "HT20"); - else - used_htmode = snprintf(htmode, 5, "????"); - } - - mcs[used_mcs] = '\0'; - htmode[used_htmode] = '\0'; - - len += snprintf(buf + len, max - len, - "%6s %6s %3u.%d: " - "%10u %10u %10u %10u\n", - htmode, - mcs, - ratekbps / 1000, - (ratekbps % 1000) / 100, - stats->success, - stats->retries, - stats->xretries, - stats->per); - } - - if (len > max) - len = max; - - retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); - kfree(buf); - return retval; -} - -static const struct file_operations fops_rcstat = { - .read = read_file_rcstat, - .open = simple_open, - .owner = THIS_MODULE -}; - -static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta, - struct dentry *dir) -{ - struct ath_rate_priv *rc = priv_sta; - rc->debugfs_rcstats = debugfs_create_file("rc_stats", S_IRUGO, - dir, rc, &fops_rcstat); -} - -static void ath_rate_remove_sta_debugfs(void *priv, void *priv_sta) -{ - struct ath_rate_priv *rc = priv_sta; - debugfs_remove(rc->debugfs_rcstats); -} - -#endif /* CONFIG_MAC80211_DEBUGFS && CONFIG_ATH9K_DEBUGFS */ - -static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -{ - return hw->priv; -} - -static void ath_rate_free(void *priv) -{ - return; -} - -static void *ath_rate_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) -{ - return kzalloc(sizeof(struct ath_rate_priv), gfp); -} - -static void ath_rate_free_sta(void *priv, struct ieee80211_sta *sta, - void *priv_sta) -{ - struct ath_rate_priv *rate_priv = priv_sta; - kfree(rate_priv); -} - -static struct rate_control_ops ath_rate_ops = { - .module = NULL, - .name = "ath9k_rate_control", - .tx_status = ath_tx_status, - .get_rate = ath_get_rate, - .rate_init = ath_rate_init, - .rate_update = ath_rate_update, - .alloc = ath_rate_alloc, - .free = ath_rate_free, - .alloc_sta = ath_rate_alloc_sta, - .free_sta = ath_rate_free_sta, - -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) - .add_sta_debugfs = ath_rate_add_sta_debugfs, - .remove_sta_debugfs = ath_rate_remove_sta_debugfs, -#endif -}; - -int ath_rate_control_register(void) -{ - return ieee80211_rate_control_register(&ath_rate_ops); -} - -void ath_rate_control_unregister(void) -{ - ieee80211_rate_control_unregister(&ath_rate_ops); -} diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h deleted file mode 100644 index b9a87383cb4..00000000000 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright (c) 2004 Sam Leffler, Errno Consulting - * Copyright (c) 2004 Video54 Technologies, Inc. - * 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. - */ - -#ifndef RC_H -#define RC_H - -#include "hw.h" - -struct ath_softc; - -#define ATH_RATE_MAX 30 -#define RATE_TABLE_SIZE 72 - -#define RC_INVALID 0x0000 -#define RC_LEGACY 0x0001 -#define RC_SS 0x0002 -#define RC_DS 0x0004 -#define RC_TS 0x0008 -#define RC_HT_20 0x0010 -#define RC_HT_40 0x0020 - -#define RC_STREAM_MASK 0xe -#define RC_DS_OR_LATER(f) ((((f) & RC_STREAM_MASK) == RC_DS) || \ - (((f) & RC_STREAM_MASK) == (RC_DS | RC_TS))) -#define RC_TS_ONLY(f) (((f) & RC_STREAM_MASK) == RC_TS) -#define RC_SS_OR_LEGACY(f) ((f) & (RC_SS | RC_LEGACY)) - -#define RC_HT_2040 (RC_HT_20 | RC_HT_40) -#define RC_ALL_STREAM (RC_SS | RC_DS | RC_TS) -#define RC_L_SD (RC_LEGACY | RC_SS | RC_DS) -#define RC_L_SDT (RC_LEGACY | RC_SS | RC_DS | RC_TS) -#define RC_HT_S_20 (RC_HT_20 | RC_SS) -#define RC_HT_D_20 (RC_HT_20 | RC_DS) -#define RC_HT_T_20 (RC_HT_20 | RC_TS) -#define RC_HT_S_40 (RC_HT_40 | RC_SS) -#define RC_HT_D_40 (RC_HT_40 | RC_DS) -#define RC_HT_T_40 (RC_HT_40 | RC_TS) - -#define RC_HT_SD_20 (RC_HT_20 | RC_SS | RC_DS) -#define RC_HT_DT_20 (RC_HT_20 | RC_DS | RC_TS) -#define RC_HT_SD_40 (RC_HT_40 | RC_SS | RC_DS) -#define RC_HT_DT_40 (RC_HT_40 | RC_DS | RC_TS) - -#define RC_HT_SD_2040 (RC_HT_2040 | RC_SS | RC_DS) -#define RC_HT_SDT_2040 (RC_HT_2040 | RC_SS | RC_DS | RC_TS) - -#define RC_HT_SDT_20 (RC_HT_20 | RC_SS | RC_DS | RC_TS) -#define RC_HT_SDT_40 (RC_HT_40 | RC_SS | RC_DS | RC_TS) - -#define RC_ALL (RC_LEGACY | RC_HT_2040 | RC_ALL_STREAM) - -enum { - WLAN_RC_PHY_OFDM, - WLAN_RC_PHY_CCK, - WLAN_RC_PHY_HT_20_SS, - WLAN_RC_PHY_HT_20_DS, - WLAN_RC_PHY_HT_20_TS, - WLAN_RC_PHY_HT_40_SS, - WLAN_RC_PHY_HT_40_DS, - WLAN_RC_PHY_HT_40_TS, - WLAN_RC_PHY_HT_20_SS_HGI, - WLAN_RC_PHY_HT_20_DS_HGI, - WLAN_RC_PHY_HT_20_TS_HGI, - WLAN_RC_PHY_HT_40_SS_HGI, - WLAN_RC_PHY_HT_40_DS_HGI, - WLAN_RC_PHY_HT_40_TS_HGI, - WLAN_RC_PHY_MAX -}; - -#define WLAN_RC_PHY_DS(_phy) ((_phy == WLAN_RC_PHY_HT_20_DS) \ - || (_phy == WLAN_RC_PHY_HT_40_DS) \ - || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_DS_HGI)) -#define WLAN_RC_PHY_TS(_phy) ((_phy == WLAN_RC_PHY_HT_20_TS) \ - || (_phy == WLAN_RC_PHY_HT_40_TS) \ - || (_phy == WLAN_RC_PHY_HT_20_TS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_TS_HGI)) -#define WLAN_RC_PHY_20(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS) \ - || (_phy == WLAN_RC_PHY_HT_20_DS) \ - || (_phy == WLAN_RC_PHY_HT_20_TS) \ - || (_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_20_TS_HGI)) -#define WLAN_RC_PHY_40(_phy) ((_phy == WLAN_RC_PHY_HT_40_SS) \ - || (_phy == WLAN_RC_PHY_HT_40_DS) \ - || (_phy == WLAN_RC_PHY_HT_40_TS) \ - || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_DS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_TS_HGI)) -#define WLAN_RC_PHY_SGI(_phy) ((_phy == WLAN_RC_PHY_HT_20_SS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_20_DS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_20_TS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_SS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_DS_HGI) \ - || (_phy == WLAN_RC_PHY_HT_40_TS_HGI)) - -#define WLAN_RC_PHY_HT(_phy) (_phy >= WLAN_RC_PHY_HT_20_SS) - -#define WLAN_RC_CAP_MODE(capflag) (((capflag & WLAN_RC_HT_FLAG) ? \ - ((capflag & WLAN_RC_40_FLAG) ? RC_HT_40 : RC_HT_20) : RC_LEGACY)) - -#define WLAN_RC_CAP_STREAM(capflag) (((capflag & WLAN_RC_TS_FLAG) ? \ - (RC_TS) : ((capflag & WLAN_RC_DS_FLAG) ? RC_DS : RC_SS))) - -/* Return TRUE if flag supports HT20 && client supports HT20 or - * return TRUE if flag supports HT40 && client supports HT40. - * This is used becos some rates overlap between HT20/HT40. - */ -#define WLAN_RC_PHY_HT_VALID(flag, capflag) \ - (((flag & RC_HT_20) && !(capflag & WLAN_RC_40_FLAG)) || \ - ((flag & RC_HT_40) && (capflag & WLAN_RC_40_FLAG))) - -#define WLAN_RC_DS_FLAG (0x01) -#define WLAN_RC_TS_FLAG (0x02) -#define WLAN_RC_40_FLAG (0x04) -#define WLAN_RC_SGI_FLAG (0x08) -#define WLAN_RC_HT_FLAG (0x10) - -/** - * struct ath_rate_table - Rate Control table - * @rate_cnt: total number of rates for the given wireless mode - * @mcs_start: MCS rate index offset - * @rate_flags: Rate Control flags - * @phy: CCK/OFDM/HT20/HT40 - * @ratekbps: rate in Kbits per second - * @user_ratekbps: user rate in Kbits per second - * @ratecode: rate that goes into HW descriptors - * @dot11rate: value that goes into supported - * rates info element of MLME - * @ctrl_rate: Index of next lower basic rate, used for duration computation - * @cw40index: Index of rates having 40MHz channel width - * @sgi_index: Index of rates having Short Guard Interval - * @ht_index: high throughput rates having 40MHz channel width and - * Short Guard Interval - * @probe_interval: interval for rate control to probe for other rates - * @initial_ratemax: initial ratemax value - */ -struct ath_rate_table { - int rate_cnt; - int mcs_start; - struct { - u16 rate_flags; - u8 phy; - u32 ratekbps; - u32 user_ratekbps; - u8 ratecode; - u8 dot11rate; - } info[RATE_TABLE_SIZE]; - u32 probe_interval; - u8 initial_ratemax; -}; - -struct ath_rateset { - u8 rs_nrates; - u8 rs_rates[ATH_RATE_MAX]; -}; - -struct ath_rc_stats { - u32 success; - u32 retries; - u32 xretries; - u8 per; -}; - -/** - * struct ath_rate_priv - Rate Control priv data - * @state: RC state - * @probe_rate: rate we are probing at - * @probe_time: msec timestamp for last probe - * @hw_maxretry_pktcnt: num of packets since we got HW max retry error - * @max_valid_rate: maximum number of valid rate - * @per_down_time: msec timestamp for last PER down step - * @valid_phy_ratecnt: valid rate count - * @rate_max_phy: phy index for the max rate - * @per: PER for every valid rate in % - * @probe_interval: interval for ratectrl to probe for other rates - * @ht_cap: HT capabilities - * @neg_rates: Negotatied rates - * @neg_ht_rates: Negotiated HT rates - */ -struct ath_rate_priv { - u8 rate_table_size; - u8 probe_rate; - u8 hw_maxretry_pktcnt; - u8 max_valid_rate; - u8 valid_rate_index[RATE_TABLE_SIZE]; - u8 ht_cap; - u8 valid_phy_ratecnt[WLAN_RC_PHY_MAX]; - u8 valid_phy_rateidx[WLAN_RC_PHY_MAX][RATE_TABLE_SIZE]; - u8 rate_max_phy; - u8 per[RATE_TABLE_SIZE]; - u32 probe_time; - u32 per_down_time; - u32 probe_interval; - struct ath_rateset neg_rates; - struct ath_rateset neg_ht_rates; - const struct ath_rate_table *rate_table; - -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) - struct dentry *debugfs_rcstats; - struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; -#endif -}; - -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) -void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate); -void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, - int xretries, int retries, u8 per); -#else -static inline void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) -{ -} -static inline void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, - int xretries, int retries, u8 per) -{ -} -#endif - -#ifdef CONFIG_ATH9K_LEGACY_RATE_CONTROL -int ath_rate_control_register(void); -void ath_rate_control_unregister(void); -#else -static inline int ath_rate_control_register(void) -{ - return 0; -} - -static inline void ath_rate_control_unregister(void) -{ -} -#endif - -#endif /* RC_H */ diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index ab9e3a8410b..9105a92364f 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -15,11 +15,10 @@ */ #include <linux/dma-mapping.h> -#include <linux/relay.h> #include "ath9k.h" #include "ar9003_mac.h" -#define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb)) +#define SKB_CB_ATHBUF(__skb) (*((struct ath_rxbuf **)__skb->cb)) static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) { @@ -35,7 +34,8 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) * buffer (or rx fifo). This can incorrectly acknowledge packets * to a sender if last desc is self-linked. */ -static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) +static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf, + bool flush) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -60,18 +60,19 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) common->rx_bufsize, 0); - if (sc->rx.rxlink == NULL) - ath9k_hw_putrxbuf(ah, bf->bf_daddr); - else + if (sc->rx.rxlink) *sc->rx.rxlink = bf->bf_daddr; + else if (!flush) + ath9k_hw_putrxbuf(ah, bf->bf_daddr); sc->rx.rxlink = &ds->ds_link; } -static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf) +static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf, + bool flush) { if (sc->rx.buf_hold) - ath_rx_buf_link(sc, sc->rx.buf_hold); + ath_rx_buf_link(sc, sc->rx.buf_hold, flush); sc->rx.buf_hold = bf; } @@ -112,13 +113,13 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc, struct ath_hw *ah = sc->sc_ah; struct ath_rx_edma *rx_edma; struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; rx_edma = &sc->rx.rx_edma[qtype]; if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize) return false; - bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list); list_del_init(&bf->list); skb = bf->bf_mpdu; @@ -138,7 +139,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc, enum ath9k_rx_qtype qtype) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_buf *bf, *tbf; + struct ath_rxbuf *bf, *tbf; if (list_empty(&sc->rx.rxbuf)) { ath_dbg(common, QUEUE, "No free rx buf available\n"); @@ -154,7 +155,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc, static void ath_rx_remove_buffer(struct ath_softc *sc, enum ath9k_rx_qtype qtype) { - struct ath_buf *bf; + struct ath_rxbuf *bf; struct ath_rx_edma *rx_edma; struct sk_buff *skb; @@ -171,7 +172,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - struct ath_buf *bf; + struct ath_rxbuf *bf; ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP); ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP); @@ -199,7 +200,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; int error = 0, i; u32 size; @@ -211,7 +212,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP], ah->caps.rx_hp_qdepth); - size = sizeof(struct ath_buf) * nbufs; + size = sizeof(struct ath_rxbuf) * nbufs; bf = devm_kzalloc(sc->dev, size, GFP_KERNEL); if (!bf) return -ENOMEM; @@ -271,7 +272,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; int error = 0; spin_lock_init(&sc->sc_pcu_lock); @@ -332,7 +333,7 @@ void ath_rx_cleanup(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_rx_edma_cleanup(sc); @@ -375,6 +376,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) { u32 rfilt; + if (config_enabled(CONFIG_ATH9K_TX99)) + return 0; + rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; @@ -417,7 +421,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; } - if (AR_SREV_9550(sc->sc_ah)) + if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; return rfilt; @@ -427,7 +431,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) int ath_startrecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - struct ath_buf *bf, *tbf; + struct ath_rxbuf *bf, *tbf; if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_edma_start_recv(sc); @@ -440,14 +444,14 @@ int ath_startrecv(struct ath_softc *sc) sc->rx.buf_hold = NULL; sc->rx.rxlink = NULL; list_for_each_entry_safe(bf, tbf, &sc->rx.rxbuf, list) { - ath_rx_buf_link(sc, bf); + ath_rx_buf_link(sc, bf, false); } /* We could have deleted elements so the list may be empty now */ if (list_empty(&sc->rx.rxbuf)) goto start_recv; - bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list); ath9k_hw_putrxbuf(ah, bf->bf_daddr); ath9k_hw_rxena(ah); @@ -536,7 +540,10 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) sc->ps_flags &= ~PS_BEACON_SYNC; ath_dbg(common, PS, "Reconfigure beacon timers based on synchronized timestamp\n"); - ath9k_set_beacon(sc); + if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0))) + ath9k_set_beacon(sc); + if (sc->p2p_ps_vif) + ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif); } if (ath_beacon_dtim_pending_cab(skb)) { @@ -603,13 +610,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) static bool ath_edma_get_buffers(struct ath_softc *sc, enum ath9k_rx_qtype qtype, struct ath_rx_status *rs, - struct ath_buf **dest) + struct ath_rxbuf **dest) { struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype]; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; int ret; skb = skb_peek(&rx_edma->rx_fifo); @@ -653,11 +660,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc, return true; } -static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc, +static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs, enum ath9k_rx_qtype qtype) { - struct ath_buf *bf = NULL; + struct ath_rxbuf *bf = NULL; while (ath_edma_get_buffers(sc, qtype, rs, &bf)) { if (!bf) @@ -668,13 +675,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc, return NULL; } -static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, +static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_desc *ds; - struct ath_buf *bf; + struct ath_rxbuf *bf; int ret; if (list_empty(&sc->rx.rxbuf)) { @@ -682,7 +689,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } - bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list); if (bf == sc->rx.buf_hold) return NULL; @@ -702,7 +709,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, ret = ath9k_hw_rxprocdesc(ah, ds, rs); if (ret == -EINPROGRESS) { struct ath_rx_status trs; - struct ath_buf *tbf; + struct ath_rxbuf *tbf; struct ath_desc *tds; memset(&trs, 0, sizeof(trs)); @@ -711,7 +718,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } - tbf = list_entry(bf->list.next, struct ath_buf, list); + tbf = list_entry(bf->list.next, struct ath_rxbuf, list); /* * On some hardware the descriptor status words could @@ -730,11 +737,18 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; /* - * mark descriptor as zero-length and set the 'more' - * flag to ensure that both buffers get discarded + * Re-check previous descriptor, in case it has been filled + * in the mean time. */ - rs->rs_datalen = 0; - rs->rs_more = true; + ret = ath9k_hw_rxprocdesc(ah, ds, rs); + if (ret == -EINPROGRESS) { + /* + * mark descriptor as zero-length and set the 'more' + * flag to ensure that both buffers get discarded + */ + rs->rs_datalen = 0; + rs->rs_more = true; + } } list_del(&bf->list); @@ -753,194 +767,6 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return bf; } -/* Assumes you've already done the endian to CPU conversion */ -static bool ath9k_rx_accept(struct ath_common *common, - struct ieee80211_hdr *hdr, - struct ieee80211_rx_status *rxs, - struct ath_rx_status *rx_stats, - bool *decrypt_error) -{ - struct ath_softc *sc = (struct ath_softc *) common->priv; - bool is_mc, is_valid_tkip, strip_mic, mic_error; - struct ath_hw *ah = common->ah; - __le16 fc; - - fc = hdr->frame_control; - - is_mc = !!is_multicast_ether_addr(hdr->addr1); - is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID && - test_bit(rx_stats->rs_keyix, common->tkip_keymap); - strip_mic = is_valid_tkip && ieee80211_is_data(fc) && - ieee80211_has_protected(fc) && - !(rx_stats->rs_status & - (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | - ATH9K_RXERR_KEYMISS)); - - /* - * Key miss events are only relevant for pairwise keys where the - * descriptor does contain a valid key index. This has been observed - * mostly with CCMP encryption. - */ - if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID || - !test_bit(rx_stats->rs_keyix, common->ccmp_keymap)) - rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; - - mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) && - !ieee80211_has_morefrags(fc) && - !(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) && - (rx_stats->rs_status & ATH9K_RXERR_MIC); - - /* - * The rx_stats->rs_status will not be set until the end of the - * chained descriptors so it can be ignored if rs_more is set. The - * rs_more will be false at the last element of the chained - * descriptors. - */ - if (rx_stats->rs_status != 0) { - u8 status_mask; - - if (rx_stats->rs_status & ATH9K_RXERR_CRC) { - rxs->flag |= RX_FLAG_FAILED_FCS_CRC; - mic_error = false; - } - - if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) || - (!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) { - *decrypt_error = true; - mic_error = false; - } - - /* - * Reject error frames with the exception of - * decryption and MIC failures. For monitor mode, - * we also ignore the CRC error. - */ - status_mask = ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | - ATH9K_RXERR_KEYMISS; - - if (ah->is_monitoring && (sc->rx.rxfilter & FIF_FCSFAIL)) - status_mask |= ATH9K_RXERR_CRC; - - if (rx_stats->rs_status & ~status_mask) - return false; - } - - /* - * For unicast frames the MIC error bit can have false positives, - * so all MIC error reports need to be validated in software. - * False negatives are not common, so skip software verification - * if the hardware considers the MIC valid. - */ - if (strip_mic) - rxs->flag |= RX_FLAG_MMIC_STRIPPED; - else if (is_mc && mic_error) - rxs->flag |= RX_FLAG_MMIC_ERROR; - - return true; -} - -static int ath9k_process_rate(struct ath_common *common, - struct ieee80211_hw *hw, - struct ath_rx_status *rx_stats, - struct ieee80211_rx_status *rxs) -{ - struct ieee80211_supported_band *sband; - enum ieee80211_band band; - unsigned int i = 0; - struct ath_softc __maybe_unused *sc = common->priv; - - band = hw->conf.chandef.chan->band; - sband = hw->wiphy->bands[band]; - - switch (hw->conf.chandef.width) { - case NL80211_CHAN_WIDTH_5: - rxs->flag |= RX_FLAG_5MHZ; - break; - case NL80211_CHAN_WIDTH_10: - rxs->flag |= RX_FLAG_10MHZ; - break; - default: - break; - } - - if (rx_stats->rs_rate & 0x80) { - /* HT rate */ - rxs->flag |= RX_FLAG_HT; - rxs->flag |= rx_stats->flag; - rxs->rate_idx = rx_stats->rs_rate & 0x7f; - return 0; - } - - for (i = 0; i < sband->n_bitrates; i++) { - if (sband->bitrates[i].hw_value == rx_stats->rs_rate) { - rxs->rate_idx = i; - return 0; - } - if (sband->bitrates[i].hw_value_short == rx_stats->rs_rate) { - rxs->flag |= RX_FLAG_SHORTPRE; - rxs->rate_idx = i; - return 0; - } - } - - /* - * No valid hardware bitrate found -- we should not get here - * because hardware has already validated this frame as OK. - */ - ath_dbg(common, ANY, - "unsupported hw bitrate detected 0x%02x using 1 Mbit\n", - rx_stats->rs_rate); - RX_STAT_INC(rx_rate_err); - return -EINVAL; -} - -static void ath9k_process_rssi(struct ath_common *common, - struct ieee80211_hw *hw, - struct ath_rx_status *rx_stats, - struct ieee80211_rx_status *rxs) -{ - struct ath_softc *sc = hw->priv; - struct ath_hw *ah = common->ah; - int last_rssi; - int rssi = rx_stats->rs_rssi; - - /* - * RSSI is not available for subframes in an A-MPDU. - */ - if (rx_stats->rs_moreaggr) { - rxs->flag |= RX_FLAG_NO_SIGNAL_VAL; - return; - } - - /* - * Check if the RSSI for the last subframe in an A-MPDU - * or an unaggregated frame is valid. - */ - if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) { - rxs->flag |= RX_FLAG_NO_SIGNAL_VAL; - return; - } - - /* - * Update Beacon RSSI, this is used by ANI. - */ - if (rx_stats->is_mybeacon && - ((ah->opmode == NL80211_IFTYPE_STATION) || - (ah->opmode == NL80211_IFTYPE_ADHOC))) { - ATH_RSSI_LPF(sc->last_rssi, rx_stats->rs_rssi); - last_rssi = sc->last_rssi; - - if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) - rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER); - if (rssi < 0) - rssi = 0; - - ah->stats.avgbrssi = rssi; - } - - rxs->signal = ah->noise + rx_stats->rs_rssi; -} - static void ath9k_process_tsf(struct ath_rx_status *rs, struct ieee80211_rx_status *rxs, u64 tsf) @@ -957,131 +783,6 @@ static void ath9k_process_tsf(struct ath_rx_status *rs, rxs->mactime += 0x100000000ULL; } -#ifdef CONFIG_ATH9K_DEBUGFS -static s8 fix_rssi_inv_only(u8 rssi_val) -{ - if (rssi_val == 128) - rssi_val = 0; - return (s8) rssi_val; -} -#endif - -/* returns 1 if this was a spectral frame, even if not handled. */ -static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, - struct ath_rx_status *rs, u64 tsf) -{ -#ifdef CONFIG_ATH9K_DEBUGFS - struct ath_hw *ah = sc->sc_ah; - u8 bins[SPECTRAL_HT20_NUM_BINS]; - u8 *vdata = (u8 *)hdr; - struct fft_sample_ht20 fft_sample; - struct ath_radar_info *radar_info; - struct ath_ht20_mag_info *mag_info; - int len = rs->rs_datalen; - int dc_pos; - u16 length, max_magnitude; - - /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer - * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT - * yet, but this is supposed to be possible as well. - */ - if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && - rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && - rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) - return 0; - - /* check if spectral scan bit is set. This does not have to be checked - * if received through a SPECTRAL phy error, but shouldn't hurt. - */ - radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; - if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) - return 0; - - /* Variation in the data length is possible and will be fixed later. - * Note that we only support HT20 for now. - * - * TODO: add HT20_40 support as well. - */ - if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) || - (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1)) - return 1; - - fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; - length = sizeof(fft_sample) - sizeof(fft_sample.tlv); - fft_sample.tlv.length = __cpu_to_be16(length); - - fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq); - fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - fft_sample.noise = ah->noise; - - switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) { - case 0: - /* length correct, nothing to do. */ - memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS); - break; - case -1: - /* first byte missing, duplicate it. */ - memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1); - bins[0] = vdata[0]; - break; - case 2: - /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ - memcpy(bins, vdata, 30); - bins[30] = vdata[31]; - memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31); - break; - case 1: - /* MAC added 2 extra bytes AND first byte is missing. */ - bins[0] = vdata[0]; - memcpy(&bins[0], vdata, 30); - bins[31] = vdata[31]; - memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32); - break; - default: - return 1; - } - - /* DC value (value in the middle) is the blind spot of the spectral - * sample and invalid, interpolate it. - */ - dc_pos = SPECTRAL_HT20_NUM_BINS / 2; - bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; - - /* mag data is at the end of the frame, in front of radar_info */ - mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; - - /* copy raw bins without scaling them */ - memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS); - fft_sample.max_exp = mag_info->max_exp & 0xf; - - max_magnitude = spectral_max_magnitude(mag_info->all_bins); - fft_sample.max_magnitude = __cpu_to_be16(max_magnitude); - fft_sample.max_index = spectral_max_index(mag_info->all_bins); - fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins); - fft_sample.tsf = __cpu_to_be64(tsf); - - ath_debug_send_fft_sample(sc, &fft_sample.tlv); - return 1; -#else - return 0; -#endif -} - -static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - - if (ieee80211_is_beacon(hdr->frame_control)) { - RX_STAT_INC(rx_beacons); - if (!is_zero_ether_addr(common->curbssid) && - ether_addr_equal(hdr->addr3, common->curbssid)) - return true; - } - - return false; -} - /* * For Decrypt or Demic errors, we only mark packet status here and always push * up the frame up to let mac80211 handle the actual error case, be it no @@ -1098,32 +799,32 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_hdr *hdr; bool discard_current = sc->rx.discard_next; - int ret = 0; /* * Discard corrupt descriptors which are marked in * ath_get_next_rx_buf(). */ - sc->rx.discard_next = rx_stats->rs_more; if (discard_current) - return -EINVAL; + goto corrupt; + + sc->rx.discard_next = false; /* * Discard zero-length packets. */ if (!rx_stats->rs_datalen) { RX_STAT_INC(rx_len_err); - return -EINVAL; + goto corrupt; } - /* - * rs_status follows rs_datalen so if rs_datalen is too large - * we can take a hint that hardware corrupted it, so ignore - * those frames. - */ + /* + * rs_status follows rs_datalen so if rs_datalen is too large + * we can take a hint that hardware corrupted it, so ignore + * those frames. + */ if (rx_stats->rs_datalen > (common->rx_bufsize - ah->caps.rx_status_len)) { RX_STAT_INC(rx_len_err); - return -EINVAL; + goto corrupt; } /* Only use status info from the last fragment */ @@ -1137,10 +838,8 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, * This is different from the other corrupt descriptor * condition handled above. */ - if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) { - ret = -EINVAL; - goto exit; - } + if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) + goto corrupt; hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len); @@ -1156,34 +855,42 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime)) RX_STAT_INC(rx_spectral); - ret = -EINVAL; - goto exit; + return -EINVAL; } /* * everything but the rate is checked here, the rate check is done * separately to avoid doing two lookups for a rate for each frame. */ - if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error)) { - ret = -EINVAL; - goto exit; - } + if (!ath9k_cmn_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error, sc->rx.rxfilter)) + return -EINVAL; - rx_stats->is_mybeacon = ath9k_is_mybeacon(sc, hdr); - if (rx_stats->is_mybeacon) { - sc->hw_busy_count = 0; - ath_start_rx_poll(sc, 3); + if (ath_is_mybeacon(common, hdr)) { + RX_STAT_INC(rx_beacons); + rx_stats->is_mybeacon = true; } - if (ath9k_process_rate(common, hw, rx_stats, rx_status)) { - ret =-EINVAL; - goto exit; + /* + * This shouldn't happen, but have a safety check anyway. + */ + if (WARN_ON(!ah->curchan)) + return -EINVAL; + + if (ath9k_cmn_process_rate(common, hw, rx_stats, rx_status)) { + /* + * No valid hardware bitrate found -- we should not get here + * because hardware has already validated this frame as OK. + */ + ath_dbg(common, ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n", + rx_stats->rs_rate); + RX_STAT_INC(rx_rate_err); + return -EINVAL; } - ath9k_process_rssi(common, hw, rx_stats, rx_status); + ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status); - rx_status->band = hw->conf.chandef.chan->band; - rx_status->freq = hw->conf.chandef.chan->center_freq; + rx_status->band = ah->curchan->chan->band; + rx_status->freq = ah->curchan->chan->center_freq; rx_status->antenna = rx_stats->rs_antenna; rx_status->flag |= RX_FLAG_MACTIME_END; @@ -1193,60 +900,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc, sc->rx.num_pkts++; #endif -exit: - sc->rx.discard_next = false; - return ret; -} - -static void ath9k_rx_skb_postprocess(struct ath_common *common, - struct sk_buff *skb, - struct ath_rx_status *rx_stats, - struct ieee80211_rx_status *rxs, - bool decrypt_error) -{ - struct ath_hw *ah = common->ah; - struct ieee80211_hdr *hdr; - int hdrlen, padpos, padsize; - u8 keyix; - __le16 fc; - - /* see if any padding is done by the hw and remove it */ - hdr = (struct ieee80211_hdr *) skb->data; - hdrlen = ieee80211_get_hdrlen_from_skb(skb); - fc = hdr->frame_control; - padpos = ieee80211_hdrlen(fc); - - /* The MAC header is padded to have 32-bit boundary if the - * packet payload is non-zero. The general calculation for - * padsize would take into account odd header lengths: - * padsize = (4 - padpos % 4) % 4; However, since only - * even-length headers are used, padding can only be 0 or 2 - * bytes and we can optimize this a bit. In addition, we must - * not try to remove padding from short control frames that do - * not have payload. */ - padsize = padpos & 3; - if (padsize && skb->len>=padpos+padsize+FCS_LEN) { - memmove(skb->data + padsize, skb->data, padpos); - skb_pull(skb, padsize); - } - - keyix = rx_stats->rs_keyix; - - if (!(keyix == ATH9K_RXKEYIX_INVALID) && !decrypt_error && - ieee80211_has_protected(fc)) { - rxs->flag |= RX_FLAG_DECRYPTED; - } else if (ieee80211_has_protected(fc) - && !decrypt_error && skb->len >= hdrlen + 4) { - keyix = skb->data[hdrlen + 3] >> 6; + return 0; - if (test_bit(keyix, common->keymap)) - rxs->flag |= RX_FLAG_DECRYPTED; - } - if (ah->sw_mgmt_crypto && - (rxs->flag & RX_FLAG_DECRYPTED) && - ieee80211_is_mgmt(fc)) - /* Use software decrypt for management frames. */ - rxs->flag &= ~RX_FLAG_DECRYPTED; +corrupt: + sc->rx.discard_next = rx_stats->rs_more; + return -EINVAL; } /* @@ -1308,7 +966,7 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc, int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { - struct ath_buf *bf; + struct ath_rxbuf *bf; struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb; struct ieee80211_rx_status *rxs; struct ath_hw *ah = sc->sc_ah; @@ -1322,6 +980,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) u64 tsf = 0; unsigned long flags; dma_addr_t new_buf_addr; + unsigned int budget = 512; if (edma) dma_type = DMA_BIDIRECTIONAL; @@ -1398,8 +1057,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) skb_pull(skb, ah->caps.rx_status_len); if (!rs.rs_more) - ath9k_rx_skb_postprocess(common, hdr_skb, &rs, - rxs, decrypt_error); + ath9k_cmn_rx_skb_postprocess(common, hdr_skb, &rs, + rxs, decrypt_error); if (rs.rs_more) { RX_STAT_INC(rx_frags); @@ -1448,8 +1107,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) spin_unlock_irqrestore(&sc->sc_pm_lock, flags); ath9k_antenna_check(sc, &rs); - ath9k_apply_ampdu_details(sc, &rs, rxs); + ath_debug_rate_stats(sc, &rs, skb); ieee80211_rx(hw, skb); @@ -1460,15 +1119,17 @@ requeue_drop_frag: } requeue: list_add_tail(&bf->list, &sc->rx.rxbuf); - if (flush) - continue; - if (edma) { + if (!edma) { + ath_rx_buf_relink(sc, bf, flush); + if (!flush) + ath9k_hw_rxena(ah); + } else if (!flush) { ath_rx_edma_buf_link(sc, qtype); - } else { - ath_rx_buf_relink(sc, bf); - ath9k_hw_rxena(ah); } + + if (!budget--) + break; } while (1); if (!(ah->imask & ATH9K_INT_RXEOL)) { diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index a13b2d143d9..f1bbce3f777 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -304,6 +304,7 @@ #define AR_IMR_S2 0x00ac #define AR_IMR_S2_QCU_TXURN 0x000003FF #define AR_IMR_S2_QCU_TXURN_S 0 +#define AR_IMR_S2_BB_WATCHDOG 0x00010000 #define AR_IMR_S2_CST 0x00400000 #define AR_IMR_S2_GTT 0x00800000 #define AR_IMR_S2_TIM 0x01000000 @@ -504,9 +505,6 @@ #define AR_D_QCUMASK 0x000003FF #define AR_D_QCUMASK_RESV0 0xFFFFFC00 -#define AR_D_TXBLK_CMD 0x1038 -#define AR_D_TXBLK_DATA(i) (AR_D_TXBLK_CMD+(i)) - #define AR_D0_LCL_IFS 0x1040 #define AR_D1_LCL_IFS 0x1044 #define AR_D2_LCL_IFS 0x1048 @@ -809,7 +807,12 @@ #define AR_SREV_REVISION_9462_21 3 #define AR_SREV_VERSION_9565 0x2C0 #define AR_SREV_REVISION_9565_10 0 +#define AR_SREV_REVISION_9565_101 1 +#define AR_SREV_REVISION_9565_11 2 #define AR_SREV_VERSION_9550 0x400 +#define AR_SREV_VERSION_9531 0x500 +#define AR_SREV_REVISION_9531_10 0 +#define AR_SREV_REVISION_9531_11 1 #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ @@ -881,9 +884,6 @@ #define AR_SREV_9330(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9330)) -#define AR_SREV_9330_10(_ah) \ - (AR_SREV_9330((_ah)) && \ - ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_10)) #define AR_SREV_9330_11(_ah) \ (AR_SREV_9330((_ah)) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9330_11)) @@ -927,10 +927,18 @@ #define AR_SREV_9565(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565)) - #define AR_SREV_9565_10(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_10)) +#define AR_SREV_9565_101(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_101)) +#define AR_SREV_9565_11(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9565_11)) +#define AR_SREV_9565_11_OR_LATER(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565) && \ + ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9565_11)) #define AR_SREV_9550(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9550)) @@ -938,11 +946,19 @@ #define AR_SREV_9580(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9580_10)) - #define AR_SREV_9580_10(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9580_10)) +#define AR_SREV_9531(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531)) +#define AR_SREV_9531_10(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_10)) +#define AR_SREV_9531_11(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ + ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11)) + /* NOTE: When adding chips newer than Peacock, add chip check here */ #define AR_SREV_9580_10_OR_LATER(_ah) \ (AR_SREV_9580(_ah)) diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/spectral.c new file mode 100644 index 00000000000..99f4de95c26 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/spectral.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/relay.h> +#include "ath9k.h" + +static s8 fix_rssi_inv_only(u8 rssi_val) +{ + if (rssi_val == 128) + rssi_val = 0; + return (s8) rssi_val; +} + +static void ath_debug_send_fft_sample(struct ath_softc *sc, + struct fft_sample_tlv *fft_sample_tlv) +{ + int length; + if (!sc->rfs_chan_spec_scan) + return; + + length = __be16_to_cpu(fft_sample_tlv->length) + + sizeof(*fft_sample_tlv); + relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length); +} + +/* returns 1 if this was a spectral frame, even if not handled. */ +int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf) +{ + struct ath_hw *ah = sc->sc_ah; + u8 num_bins, *bins, *vdata = (u8 *)hdr; + struct fft_sample_ht20 fft_sample_20; + struct fft_sample_ht20_40 fft_sample_40; + struct fft_sample_tlv *tlv; + struct ath_radar_info *radar_info; + int len = rs->rs_datalen; + int dc_pos; + u16 fft_len, length, freq = ah->curchan->chan->center_freq; + enum nl80211_channel_type chan_type; + + /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer + * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT + * yet, but this is supposed to be possible as well. + */ + if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && + rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && + rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) + return 0; + + /* check if spectral scan bit is set. This does not have to be checked + * if received through a SPECTRAL phy error, but shouldn't hurt. + */ + radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; + if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) + return 0; + + chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef); + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_40_NUM_BINS; + bins = (u8 *)fft_sample_40.data; + } else { + fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_NUM_BINS; + bins = (u8 *)fft_sample_20.data; + } + + /* Variation in the data length is possible and will be fixed later */ + if ((len > fft_len + 2) || (len < fft_len - 1)) + return 1; + + switch (len - fft_len) { + case 0: + /* length correct, nothing to do. */ + memcpy(bins, vdata, num_bins); + break; + case -1: + /* first byte missing, duplicate it. */ + memcpy(&bins[1], vdata, num_bins - 1); + bins[0] = vdata[0]; + break; + case 2: + /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ + memcpy(bins, vdata, 30); + bins[30] = vdata[31]; + memcpy(&bins[31], &vdata[33], num_bins - 31); + break; + case 1: + /* MAC added 2 extra bytes AND first byte is missing. */ + bins[0] = vdata[0]; + memcpy(&bins[1], vdata, 30); + bins[31] = vdata[31]; + memcpy(&bins[32], &vdata[33], num_bins - 32); + break; + default: + return 1; + } + + /* DC value (value in the middle) is the blind spot of the spectral + * sample and invalid, interpolate it. + */ + dc_pos = num_bins / 2; + bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; + + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + s8 lower_rssi, upper_rssi; + s16 ext_nf; + u8 lower_max_index, upper_max_index; + u8 lower_bitmap_w, upper_bitmap_w; + u16 lower_mag, upper_mag; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_ht20_40_mag_info *mag_info; + + if (caldata) + ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, + caldata->nfCalHist[3].privNF); + else + ext_nf = ATH_DEFAULT_NOISE_FLOOR; + + length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); + fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; + fft_sample_40.tlv.length = __cpu_to_be16(length); + fft_sample_40.freq = __cpu_to_be16(freq); + fft_sample_40.channel_type = chan_type; + + if (chan_type == NL80211_CHAN_HT40PLUS) { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); + + fft_sample_40.lower_noise = ah->noise; + fft_sample_40.upper_noise = ext_nf; + } else { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + + fft_sample_40.lower_noise = ext_nf; + fft_sample_40.upper_noise = ah->noise; + } + fft_sample_40.lower_rssi = lower_rssi; + fft_sample_40.upper_rssi = upper_rssi; + + mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1; + lower_mag = spectral_max_magnitude(mag_info->lower_bins); + upper_mag = spectral_max_magnitude(mag_info->upper_bins); + fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); + fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); + lower_max_index = spectral_max_index(mag_info->lower_bins); + upper_max_index = spectral_max_index(mag_info->upper_bins); + fft_sample_40.lower_max_index = lower_max_index; + fft_sample_40.upper_max_index = upper_max_index; + lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); + upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); + fft_sample_40.lower_bitmap_weight = lower_bitmap_w; + fft_sample_40.upper_bitmap_weight = upper_bitmap_w; + fft_sample_40.max_exp = mag_info->max_exp & 0xf; + + fft_sample_40.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_40; + } else { + u8 max_index, bitmap_w; + u16 magnitude; + struct ath_ht20_mag_info *mag_info; + + length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); + fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; + fft_sample_20.tlv.length = __cpu_to_be16(length); + fft_sample_20.freq = __cpu_to_be16(freq); + + fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + fft_sample_20.noise = ah->noise; + + mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); + max_index = spectral_max_index(mag_info->all_bins); + fft_sample_20.max_index = max_index; + bitmap_w = spectral_bitmap_weight(mag_info->all_bins); + fft_sample_20.bitmap_weight = bitmap_w; + fft_sample_20.max_exp = mag_info->max_exp & 0xf; + + fft_sample_20.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_20; + } + + ath_debug_send_fft_sample(sc, tlv); + + return 1; +} + +/*********************/ +/* spectral_scan_ctl */ +/*********************/ + +static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char *mode = ""; + unsigned int len; + + switch (sc->spectral_mode) { + case SPECTRAL_DISABLED: + mode = "disable"; + break; + case SPECTRAL_BACKGROUND: + mode = "background"; + break; + case SPECTRAL_CHANSCAN: + mode = "chanscan"; + break; + case SPECTRAL_MANUAL: + mode = "manual"; + break; + } + len = strlen(mode); + return simple_read_from_buffer(user_buf, count, ppos, mode, len); +} + +static ssize_t write_file_spec_scan_ctl(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + ssize_t len; + + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (strncmp("trigger", buf, 7) == 0) { + ath9k_spectral_scan_trigger(sc->hw); + } else if (strncmp("background", buf, 9) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND); + ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); + } else if (strncmp("chanscan", buf, 8) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN); + ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); + } else if (strncmp("manual", buf, 6) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL); + ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); + } else if (strncmp("disable", buf, 7) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED); + ath_dbg(common, CONFIG, "spectral scan: disabled\n"); + } else { + return -EINVAL; + } + + return count; +} + +static const struct file_operations fops_spec_scan_ctl = { + .read = read_file_spec_scan_ctl, + .write = write_file_spec_scan_ctl, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*************************/ +/* spectral_short_repeat */ +/*************************/ + +static ssize_t read_file_spectral_short_repeat(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.short_repeat); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_short_repeat(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 1) + return -EINVAL; + + sc->spec_config.short_repeat = val; + return count; +} + +static const struct file_operations fops_spectral_short_repeat = { + .read = read_file_spectral_short_repeat, + .write = write_file_spectral_short_repeat, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/******************/ +/* spectral_count */ +/******************/ + +static ssize_t read_file_spectral_count(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.count); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_count(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + sc->spec_config.count = val; + return count; +} + +static const struct file_operations fops_spectral_count = { + .read = read_file_spectral_count, + .write = write_file_spectral_count, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*******************/ +/* spectral_period */ +/*******************/ + +static ssize_t read_file_spectral_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + sc->spec_config.period = val; + return count; +} + +static const struct file_operations fops_spectral_period = { + .read = read_file_spectral_period, + .write = write_file_spectral_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/***********************/ +/* spectral_fft_period */ +/***********************/ + +static ssize_t read_file_spectral_fft_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.fft_period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_fft_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 15) + return -EINVAL; + + sc->spec_config.fft_period = val; + return count; +} + +static const struct file_operations fops_spectral_fft_period = { + .read = read_file_spectral_fft_period, + .write = write_file_spectral_fft_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*******************/ +/* Relay interface */ +/*******************/ + +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *buf_file; + + buf_file = debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); + *is_global = 1; + return buf_file; +} + +static int remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + + return 0; +} + +static struct rchan_callbacks rfs_spec_scan_cb = { + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + +/*********************/ +/* Debug Init/Deinit */ +/*********************/ + +void ath9k_spectral_deinit_debug(struct ath_softc *sc) +{ + if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) { + relay_close(sc->rfs_chan_spec_scan); + sc->rfs_chan_spec_scan = NULL; + } +} + +void ath9k_spectral_init_debug(struct ath_softc *sc) +{ + sc->rfs_chan_spec_scan = relay_open("spectral_scan", + sc->debug.debugfs_phy, + 1024, 256, &rfs_spec_scan_cb, + NULL); + debugfs_create_file("spectral_scan_ctl", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spec_scan_ctl); + debugfs_create_file("spectral_short_repeat", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_short_repeat); + debugfs_create_file("spectral_count", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_count); + debugfs_create_file("spectral_period", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_period); + debugfs_create_file("spectral_fft_period", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_fft_period); +} diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/spectral.h new file mode 100644 index 00000000000..ead63412ee1 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/spectral.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SPECTRAL_H +#define SPECTRAL_H + +/* enum spectral_mode: + * + * @SPECTRAL_DISABLED: spectral mode is disabled + * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with + * something else. + * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples + * is performed manually. + * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels + * during a channel scan. + */ +enum spectral_mode { + SPECTRAL_DISABLED = 0, + SPECTRAL_BACKGROUND, + SPECTRAL_MANUAL, + SPECTRAL_CHANSCAN, +}; + +#define SPECTRAL_SCAN_BITMASK 0x10 +/* Radar info packet format, used for DFS and spectral formats. */ +struct ath_radar_info { + u8 pulse_length_pri; + u8 pulse_length_ext; + u8 pulse_bw_info; +} __packed; + +/* The HT20 spectral data has 4 bytes of additional information at it's end. + * + * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: all bins max_magnitude[9:2] + * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]} + * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) + */ +struct ath_ht20_mag_info { + u8 all_bins[3]; + u8 max_exp; +} __packed; + +#define SPECTRAL_HT20_NUM_BINS 56 + +/* WARNING: don't actually use this struct! MAC may vary the amount of + * data by -1/+2. This struct is for reference only. + */ +struct ath_ht20_fft_packet { + u8 data[SPECTRAL_HT20_NUM_BINS]; + struct ath_ht20_mag_info mag_info; + struct ath_radar_info radar_info; +} __packed; + +#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet)) + +/* Dynamic 20/40 mode: + * + * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: lower bins max_magnitude[9:2] + * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]} + * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: upper bins max_magnitude[9:2] + * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]} + * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) + */ +struct ath_ht20_40_mag_info { + u8 lower_bins[3]; + u8 upper_bins[3]; + u8 max_exp; +} __packed; + +#define SPECTRAL_HT20_40_NUM_BINS 128 + +/* WARNING: don't actually use this struct! MAC may vary the amount of + * data. This struct is for reference only. + */ +struct ath_ht20_40_fft_packet { + u8 data[SPECTRAL_HT20_40_NUM_BINS]; + struct ath_ht20_40_mag_info mag_info; + struct ath_radar_info radar_info; +} __packed; + + +#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet)) + +/* grabs the max magnitude from the all/upper/lower bins */ +static inline u16 spectral_max_magnitude(u8 *bins) +{ + return (bins[0] & 0xc0) >> 6 | + (bins[1] & 0xff) << 2 | + (bins[2] & 0x03) << 10; +} + +/* return the max magnitude from the all/upper/lower bins */ +static inline u8 spectral_max_index(u8 *bins) +{ + s8 m = (bins[2] & 0xfc) >> 2; + + /* TODO: this still doesn't always report the right values ... */ + if (m > 32) + m |= 0xe0; + else + m &= ~0xe0; + + return m + 29; +} + +/* return the bitmap weight from the all/upper/lower bins */ +static inline u8 spectral_bitmap_weight(u8 *bins) +{ + return bins[0] & 0x3f; +} + +/* FFT sample format given to userspace via debugfs. + * + * Please keep the type/length at the front position and change + * other fields after adding another sample type + * + * TODO: this might need rework when switching to nl80211-based + * interface. + */ +enum ath_fft_sample_type { + ATH_FFT_SAMPLE_HT20 = 1, + ATH_FFT_SAMPLE_HT20_40, +}; + +struct fft_sample_tlv { + u8 type; /* see ath_fft_sample */ + __be16 length; + /* type dependent data follows */ +} __packed; + +struct fft_sample_ht20 { + struct fft_sample_tlv tlv; + + u8 max_exp; + + __be16 freq; + s8 rssi; + s8 noise; + + __be16 max_magnitude; + u8 max_index; + u8 bitmap_weight; + + __be64 tsf; + + u8 data[SPECTRAL_HT20_NUM_BINS]; +} __packed; + +struct fft_sample_ht20_40 { + struct fft_sample_tlv tlv; + + u8 channel_type; + __be16 freq; + + s8 lower_rssi; + s8 upper_rssi; + + __be64 tsf; + + s8 lower_noise; + s8 upper_noise; + + __be16 lower_max_magnitude; + __be16 upper_max_magnitude; + + u8 lower_max_index; + u8 upper_max_index; + + u8 lower_bitmap_weight; + u8 upper_bitmap_weight; + + u8 max_exp; + + u8 data[SPECTRAL_HT20_40_NUM_BINS]; +} __packed; + +void ath9k_spectral_init_debug(struct ath_softc *sc); +void ath9k_spectral_deinit_debug(struct ath_softc *sc); + +void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); +int ath9k_spectral_scan_config(struct ieee80211_hw *hw, + enum spectral_mode spectral_mode); + +#ifdef CONFIG_ATH9K_DEBUGFS +int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf); +#else +static inline int ath_process_fft(struct ath_softc *sc, + struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf) +{ + return 0; +} +#endif /* CONFIG_ATH9K_DEBUGFS */ + +#endif /* SPECTRAL_H */ diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c new file mode 100644 index 00000000000..a65cfb91adc --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/tx99.c @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +static void ath9k_tx99_stop(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + ath_drain_all_txq(sc); + ath_startrecv(sc); + + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + ieee80211_wake_queues(sc->hw); + + kfree_skb(sc->tx99_skb); + sc->tx99_skb = NULL; + sc->tx99_state = false; + + ath9k_hw_tx99_stop(sc->sc_ah); + ath_dbg(common, XMIT, "TX99 stopped\n"); +} + +static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) +{ + static u8 PN9Data[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24, + 0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50, + 0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1, + 0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18, + 0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8, + 0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84, + 0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3, + 0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0}; + u32 len = 1200; + struct ieee80211_tx_rate *rate; + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + + skb_put(skb, len); + + memset(skb->data, 0, len); + + hdr = (struct ieee80211_hdr *)skb->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + hdr->duration_id = 0; + + memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + + tx_info = IEEE80211_SKB_CB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + rate = &tx_info->control.rates[0]; + tx_info->band = hw->conf.chandef.chan->band; + tx_info->flags = IEEE80211_TX_CTL_NO_ACK; + tx_info->control.vif = sc->tx99_vif; + rate->count = 1; + if (ah->curchan && IS_CHAN_HT(ah->curchan)) { + rate->flags |= IEEE80211_TX_RC_MCS; + if (IS_CHAN_HT40(ah->curchan)) + rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + } + + memcpy(skb->data + sizeof(*hdr), PN9Data, sizeof(PN9Data)); + + return skb; +} + +static void ath9k_tx99_deinit(struct ath_softc *sc) +{ + ath_reset(sc); + + ath9k_ps_wakeup(sc); + ath9k_tx99_stop(sc); + ath9k_ps_restore(sc); +} + +static int ath9k_tx99_init(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_tx_control txctl; + int r; + + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { + ath_err(common, + "driver is in invalid state unable to use TX99"); + return -EINVAL; + } + + sc->tx99_skb = ath9k_build_tx99_skb(sc); + if (!sc->tx99_skb) + return -ENOMEM; + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + + ath_reset(sc); + + ath9k_ps_wakeup(sc); + + ath9k_hw_disable_interrupts(ah); + atomic_set(&ah->intr_ref_cnt, -1); + ath_drain_all_txq(sc); + ath_stoprecv(sc); + + sc->tx99_state = true; + + ieee80211_stop_queues(hw); + + if (sc->tx99_power == MAX_RATE_POWER + 1) + sc->tx99_power = MAX_RATE_POWER; + + ath9k_hw_tx99_set_txpower(ah, sc->tx99_power); + r = ath9k_tx99_send(sc, sc->tx99_skb, &txctl); + if (r) { + ath_dbg(common, XMIT, "Failed to xmit TX99 skb\n"); + return r; + } + + ath_dbg(common, XMIT, "TX99 xmit started using %d ( %ddBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + /* We leave the harware awake as it will be chugging on */ + + return 0; +} + +static ssize_t read_file_tx99(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[3]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->tx99_state); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + bool start; + ssize_t len; + int r; + + if (sc->nvifs > 1) + return -EOPNOTSUPP; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + if (strtobool(buf, &start)) + return -EINVAL; + + if (start == sc->tx99_state) { + if (!start) + return count; + ath_dbg(common, XMIT, "Resetting TX99\n"); + ath9k_tx99_deinit(sc); + } + + if (!start) { + ath9k_tx99_deinit(sc); + return count; + } + + r = ath9k_tx99_init(sc); + if (r) + return r; + + return count; +} + +static const struct file_operations fops_tx99 = { + .read = read_file_tx99, + .write = write_file_tx99, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_tx99_power(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d (%d dBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99_power(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + int r; + u8 tx_power; + + r = kstrtou8_from_user(user_buf, count, 0, &tx_power); + if (r) + return r; + + if (tx_power > MAX_RATE_POWER) + return -EINVAL; + + sc->tx99_power = tx_power; + + ath9k_ps_wakeup(sc); + ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power); + ath9k_ps_restore(sc); + + return count; +} + +static const struct file_operations fops_tx99_power = { + .read = read_file_tx99_power, + .write = write_file_tx99_power, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath9k_tx99_init_debug(struct ath_softc *sc) +{ + if (!AR_SREV_9300_20_OR_LATER(sc->sc_ah)) + return; + + debugfs_create_file("tx99", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99); + debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99_power); +} diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index fde6da619f3..0db37f23001 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -39,7 +39,7 @@ struct wmi_fw_version { struct wmi_event_swba { __be64 tsf; u8 beacon_pending; -}; +} __packed; /* * 64 - HTC header - WMI header - 1 / txstatus diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 81c88dd606d..2879887f569 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2013 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -14,409 +14,347 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/export.h> #include "ath9k.h" -#include "reg.h" -#include "hw-ops.h" -const char *ath9k_hw_wow_event_to_string(u32 wow_event) -{ - if (wow_event & AH_WOW_MAGIC_PATTERN_EN) - return "Magic pattern"; - if (wow_event & AH_WOW_USER_PATTERN_EN) - return "User pattern"; - if (wow_event & AH_WOW_LINK_CHANGE) - return "Link change"; - if (wow_event & AH_WOW_BEACON_MISS) - return "Beacon miss"; - - return "unknown reason"; -} -EXPORT_SYMBOL(ath9k_hw_wow_event_to_string); +static const struct wiphy_wowlan_support ath9k_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = MAX_NUM_USER_PATTERN, + .pattern_min_len = 1, + .pattern_max_len = MAX_PATTERN_SIZE, +}; -static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) +static void ath9k_wow_map_triggers(struct ath_softc *sc, + struct cfg80211_wowlan *wowlan, + u32 *wow_triggers) { - struct ath_common *common = ath9k_hw_common(ah); + if (wowlan->disconnect) + *wow_triggers |= AH_WOW_LINK_CHANGE | + AH_WOW_BEACON_MISS; + if (wowlan->magic_pkt) + *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; - REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV); + if (wowlan->n_patterns) + *wow_triggers |= AH_WOW_USER_PATTERN_EN; - /* set rx disable bit */ - REG_WRITE(ah, AR_CR, AR_CR_RXD); - - if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) { - ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", - REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW)); - return; - } + sc->wow_enabled = *wow_triggers; - REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } -static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) +static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) { + struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - u8 sta_mac_addr[ETH_ALEN], ap_mac_addr[ETH_ALEN]; - u32 ctl[13] = {0}; - u32 data_word[KAL_NUM_DATA_WORDS]; - u8 i; - u32 wow_ka_data_word0; - - memcpy(sta_mac_addr, common->macaddr, ETH_ALEN); - memcpy(ap_mac_addr, common->curbssid, ETH_ALEN); - - /* set the transmit buffer */ - ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16)); - ctl[1] = 0; - ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */ - ctl[4] = 0; - ctl[7] = (ah->txchainmask) << 2; - ctl[2] = 0xf << 16; /* tx_tries 0 */ - - for (i = 0; i < KAL_NUM_DESC_WORDS; i++) - REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); - - REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); - - data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) | - (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16); - data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) | - (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); - data_word[2] = (sta_mac_addr[1] << 24) | (sta_mac_addr[0] << 16) | - (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); - data_word[3] = (sta_mac_addr[5] << 24) | (sta_mac_addr[4] << 16) | - (sta_mac_addr[3] << 8) | (sta_mac_addr[2]); - data_word[4] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) | - (ap_mac_addr[1] << 8) | (ap_mac_addr[0]); - data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]); - - if (AR_SREV_9462_20(ah)) { - /* AR9462 2.0 has an extra descriptor word (time based - * discard) compared to other chips */ - REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0); - wow_ka_data_word0 = AR_WOW_TXBUF(13); - } else { - wow_ka_data_word0 = AR_WOW_TXBUF(12); - } - - for (i = 0; i < KAL_NUM_DATA_WORDS; i++) - REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]); - -} - -void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, - u8 *user_mask, int pattern_count, - int pattern_len) -{ - int i; - u32 pattern_val, mask_val; - u32 set, clr; + int pattern_count = 0; + int i, byte_cnt; + u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; + u8 dis_deauth_mask[MAX_PATTERN_SIZE]; - /* FIXME: should check count by querying the hardware capability */ - if (pattern_count >= MAX_NUM_PATTERN) - return; - - REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count)); - - /* set the registers for pattern */ - for (i = 0; i < MAX_PATTERN_SIZE; i += 4) { - memcpy(&pattern_val, user_pattern, 4); - REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i), - pattern_val); - user_pattern += 4; - } - - /* set the registers for mask */ - for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) { - memcpy(&mask_val, user_mask, 4); - REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val); - user_mask += 4; - } + memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE); + memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE); - /* set the pattern length to be matched + /* + * Create Dissassociate / Deauthenticate packet filter + * + * 2 bytes 2 byte 6 bytes 6 bytes 6 bytes + * +--------------+----------+---------+--------+--------+---- + * + Frame Control+ Duration + DA + SA + BSSID + + * +--------------+----------+---------+--------+--------+---- * - * AR_WOW_LENGTH1_REG1 - * bit 31:24 pattern 0 length - * bit 23:16 pattern 1 length - * bit 15:8 pattern 2 length - * bit 7:0 pattern 3 length + * The above is the management frame format for disassociate/ + * deauthenticate pattern, from this we need to match the first byte + * of 'Frame Control' and DA, SA, and BSSID fields + * (skipping 2nd byte of FC and Duration feild. * - * AR_WOW_LENGTH1_REG2 - * bit 31:24 pattern 4 length - * bit 23:16 pattern 5 length - * bit 15:8 pattern 6 length - * bit 7:0 pattern 7 length + * Disassociate pattern + * -------------------- + * Frame control = 00 00 1010 + * DA, SA, BSSID = x:x:x:x:x:x + * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x + * | x:x:x:x:x:x -- 22 bytes * - * the below logic writes out the new - * pattern length for the corresponding - * pattern_count, while masking out the - * other fields + * Deauthenticate pattern + * ---------------------- + * Frame control = 00 00 1100 + * DA, SA, BSSID = x:x:x:x:x:x + * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x + * | x:x:x:x:x:x -- 22 bytes */ - ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); - - if (pattern_count < 4) { - /* Pattern 0-3 uses AR_WOW_LENGTH1 register */ - set = (pattern_len & AR_WOW_LENGTH_MAX) << - AR_WOW_LEN1_SHIFT(pattern_count); - clr = AR_WOW_LENGTH1_MASK(pattern_count); - REG_RMW(ah, AR_WOW_LENGTH1, set, clr); - } else { - /* Pattern 4-7 uses AR_WOW_LENGTH2 register */ - set = (pattern_len & AR_WOW_LENGTH_MAX) << - AR_WOW_LEN2_SHIFT(pattern_count); - clr = AR_WOW_LENGTH2_MASK(pattern_count); - REG_RMW(ah, AR_WOW_LENGTH2, set, clr); - } + /* Create Disassociate Pattern first */ -} -EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern); + byte_cnt = 0; -u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) -{ - u32 wow_status = 0; - u32 val = 0, rval; + /* Fill out the mask with all FF's */ - /* - * read the WoW status register to know - * the wakeup reason - */ - rval = REG_READ(ah, AR_WOW_PATTERN); - val = AR_WOW_STATUS(rval); + for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) + dis_deauth_mask[i] = 0xff; - /* - * mask only the WoW events that we have enabled. Sometimes - * we have spurious WoW events from the AR_WOW_PATTERN - * register. This mask will clean it up. - */ + /* copy the first byte of frame control field */ + dis_deauth_pattern[byte_cnt] = 0xa0; + byte_cnt++; - val &= ah->wow_event_mask; - - if (val) { - if (val & AR_WOW_MAGIC_PAT_FOUND) - wow_status |= AH_WOW_MAGIC_PATTERN_EN; - if (AR_WOW_PATTERN_FOUND(val)) - wow_status |= AH_WOW_USER_PATTERN_EN; - if (val & AR_WOW_KEEP_ALIVE_FAIL) - wow_status |= AH_WOW_LINK_CHANGE; - if (val & AR_WOW_BEACON_FAIL) - wow_status |= AH_WOW_BEACON_MISS; - } + /* skip 2nd byte of frame control and Duration field */ + byte_cnt += 3; /* - * set and clear WOW_PME_CLEAR registers for the chip to - * generate next wow signal. - * disable D3 before accessing other registers ? + * need not match the destination mac address, it can be a broadcast + * mac address or an unicast to this station */ + byte_cnt += 6; - /* do we need to check the bit value 0x01000000 (7-10) ?? */ - REG_RMW(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR, - AR_PMCTRL_PWR_STATE_D1D3); + /* copy the source mac address */ + memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); - /* - * clear all events - */ - REG_WRITE(ah, AR_WOW_PATTERN, - AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN))); + byte_cnt += 6; - /* - * restore the beacon threshold to init value - */ - REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR); + /* copy the bssid, its same as the source mac address */ + + memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); + + /* Create Disassociate pattern mask */ + + dis_deauth_mask[0] = 0xfe; + dis_deauth_mask[1] = 0x03; + dis_deauth_mask[2] = 0xc0; + + ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); + ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, + pattern_count, byte_cnt); + + pattern_count++; /* - * Restore the way the PCI-E reset, Power-On-Reset, external - * PCIE_POR_SHORT pins are tied to its original value. - * Previously just before WoW sleep, we untie the PCI-E - * reset to our Chip's Power On Reset so that any PCI-E - * reset from the bus will not reset our chip + * for de-authenticate pattern, only the first byte of the frame + * control field gets changed from 0xA0 to 0xC0 */ - if (ah->is_pciexpress) - ath9k_hw_configpcipowersave(ah, false); + dis_deauth_pattern[0] = 0xC0; - ah->wow_event_mask = 0; + ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, + pattern_count, byte_cnt); - return wow_status; } -EXPORT_SYMBOL(ath9k_hw_wow_wakeup); -void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) +static void ath9k_wow_add_pattern(struct ath_softc *sc, + struct cfg80211_wowlan *wowlan) { - u32 wow_event_mask; - u32 set, clr; + struct ath_hw *ah = sc->sc_ah; + struct ath9k_wow_pattern *wow_pattern = NULL; + struct cfg80211_pkt_pattern *patterns = wowlan->patterns; + int mask_len; + s8 i = 0; - /* - * wow_event_mask is a mask to the AR_WOW_PATTERN register to - * indicate which WoW events we have enabled. The WoW events - * are from the 'pattern_enable' in this function and - * 'pattern_count' of ath9k_hw_wow_apply_pattern() - */ - wow_event_mask = ah->wow_event_mask; + if (!wowlan->n_patterns) + return; /* - * Untie Power-on-Reset from the PCI-E-Reset. When we are in - * WOW sleep, we do want the Reset from the PCI-E to disturb - * our hw state + * Add the new user configured patterns */ - if (ah->is_pciexpress) { + for (i = 0; i < wowlan->n_patterns; i++) { + + wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL); + + if (!wow_pattern) + return; + + /* + * TODO: convert the generic user space pattern to + * appropriate chip specific/802.11 pattern. + */ + + mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE); + memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE); + memcpy(wow_pattern->pattern_bytes, patterns[i].pattern, + patterns[i].pattern_len); + memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len); + wow_pattern->pattern_len = patterns[i].pattern_len; + /* - * we need to untie the internal POR (power-on-reset) - * to the external PCI-E reset. We also need to tie - * the PCI-E Phy reset to the PCI-E reset. + * just need to take care of deauth and disssoc pattern, + * make sure we don't overwrite them. */ - set = AR_WA_RESET_EN | AR_WA_POR_SHORT; - clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE; - REG_RMW(ah, AR_WA, set, clr); + + ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes, + wow_pattern->mask_bytes, + i + 2, + wow_pattern->pattern_len); + kfree(wow_pattern); + } - /* - * set the power states appropriately and enable PME - */ - set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA | - AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR; +} - /* - * set and clear WOW_PME_CLEAR registers for the chip - * to generate next wow signal. - */ - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); - clr = AR_PMCTRL_WOW_PME_CLR; - REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); +int ath9k_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + u32 wow_triggers_enabled = 0; + int ret = 0; - /* - * Setup for: - * - beacon misses - * - magic pattern - * - keep alive timeout - * - pattern matching - */ + mutex_lock(&sc->mutex); - /* - * Program default values for pattern backoff, aifs/slot/KAL count, - * beacon miss timeout, KAL timeout, etc. - */ - set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF); - REG_SET_BIT(ah, AR_WOW_PATTERN, set); + ath_cancel_work(sc); + ath_stop_ani(sc); - set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | - AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | - AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT); - REG_SET_BIT(ah, AR_WOW_COUNT, set); + if (test_bit(ATH_OP_INVALID, &common->op_flags)) { + ath_dbg(common, ANY, "Device not present\n"); + ret = -EINVAL; + goto fail_wow; + } - if (pattern_enable & AH_WOW_BEACON_MISS) - set = AR_WOW_BEACON_TIMO; - /* We are not using beacon miss, program a large value */ - else - set = AR_WOW_BEACON_TIMO_MAX; + if (WARN_ON(!wowlan)) { + ath_dbg(common, WOW, "None of the WoW triggers enabled\n"); + ret = -EINVAL; + goto fail_wow; + } - REG_WRITE(ah, AR_WOW_BCN_TIMO, set); + if (!device_can_wakeup(sc->dev)) { + ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n"); + ret = 1; + goto fail_wow; + } /* - * Keep alive timo in ms except AR9280 + * none of the sta vifs are associated + * and we are not currently handling multivif + * cases, for instance we have to seperately + * configure 'keep alive frame' for each + * STA. */ - if (!pattern_enable) - set = AR_WOW_KEEP_ALIVE_NEVER; - else - set = KAL_TIMEOUT * 32; - REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set); + 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; + } + + if (sc->nvifs > 1) { + ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); + ret = 1; + goto fail_wow; + } + + ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled); + + ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n", + wow_triggers_enabled); + + ath9k_ps_wakeup(sc); + + ath9k_stop_btcoex(sc); /* - * Keep alive delay in us. based on 'power on clock', - * therefore in usec + * Enable wake up on recieving disassoc/deauth + * frame by default. */ - set = KAL_DELAY * 1000; - REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set); + ath9k_wow_add_disassoc_deauth_pattern(sc); + + if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN) + ath9k_wow_add_pattern(sc, wowlan); + spin_lock_bh(&sc->sc_pcu_lock); /* - * Create keep alive pattern to respond to beacons + * To avoid false wake, we enable beacon miss interrupt only + * when we go to sleep. We save the current interrupt mask + * so we can restore it after the system wakes up */ - ath9k_wow_create_keep_alive_pattern(ah); + sc->wow_intr_before_sleep = ah->imask; + ah->imask &= ~ATH9K_INT_GLOBAL; + ath9k_hw_disable_interrupts(ah); + ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL; + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + spin_unlock_bh(&sc->sc_pcu_lock); /* - * Configure MAC WoW Registers + * we can now sync irq and kill any running tasklets, since we already + * disabled interrupts and not holding a spin lock */ - set = 0; - /* Send keep alive timeouts anyway */ - clr = AR_WOW_KEEP_ALIVE_AUTO_DIS; + synchronize_irq(sc->irq); + tasklet_kill(&sc->intr_tq); - if (pattern_enable & AH_WOW_LINK_CHANGE) - wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL; - else - set = AR_WOW_KEEP_ALIVE_FAIL_DIS; + ath9k_hw_wow_enable(ah, wow_triggers_enabled); - set = AR_WOW_KEEP_ALIVE_FAIL_DIS; - REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr); + ath9k_ps_restore(sc); + ath_dbg(common, ANY, "WoW enabled in ath9k\n"); + atomic_inc(&sc->wow_sleep_proc_intr); - /* - * we are relying on a bmiss failure. ensure we have - * enough threshold to prevent false positives - */ - REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, - AR_WOW_BMISSTHRESHOLD); +fail_wow: + mutex_unlock(&sc->mutex); + return ret; +} + +int ath9k_resume(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + u32 wow_status; - set = 0; - clr = 0; + mutex_lock(&sc->mutex); - if (pattern_enable & AH_WOW_BEACON_MISS) { - set = AR_WOW_BEACON_FAIL_EN; - wow_event_mask |= AR_WOW_BEACON_FAIL; - } else { - clr = AR_WOW_BEACON_FAIL_EN; + ath9k_ps_wakeup(sc); + + spin_lock_bh(&sc->sc_pcu_lock); + + ath9k_hw_disable_interrupts(ah); + ah->imask = sc->wow_intr_before_sleep; + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + spin_unlock_bh(&sc->sc_pcu_lock); + + wow_status = ath9k_hw_wow_wakeup(ah); + + if (atomic_read(&sc->wow_got_bmiss_intr) == 0) { + /* + * some devices may not pick beacon miss + * as the reason they woke up so we add + * that here for that shortcoming. + */ + wow_status |= AH_WOW_BEACON_MISS; + atomic_dec(&sc->wow_got_bmiss_intr); + ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n"); } - REG_RMW(ah, AR_WOW_BCN_EN, set, clr); + atomic_dec(&sc->wow_sleep_proc_intr); - set = 0; - clr = 0; - /* - * Enable the magic packet registers - */ - if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) { - set = AR_WOW_MAGIC_EN; - wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND; - } else { - clr = AR_WOW_MAGIC_EN; + if (wow_status) { + ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n", + ath9k_hw_wow_event_to_string(wow_status), wow_status); } - set |= AR_WOW_MAC_INTR_EN; - REG_RMW(ah, AR_WOW_PATTERN, set, clr); - REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B, - AR_WOW_PATTERN_SUPPORTED); + ath_restart_work(sc); + ath9k_start_btcoex(sc); - /* - * Set the power states appropriately and enable PME - */ - clr = 0; - set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | - AR_PMCTRL_PWR_PM_CTRL_ENA; + ath9k_ps_restore(sc); + mutex_unlock(&sc->mutex); - clr = AR_PCIE_PM_CTRL_ENA; - REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr); + return 0; +} - /* - * this is needed to prevent the chip waking up - * the host within 3-4 seconds with certain - * platform/BIOS. The fix is to enable - * D1 & D3 to match original definition and - * also match the OTP value. Anyway this - * is more related to SW WOW. - */ - clr = AR_PMCTRL_PWR_STATE_D1D3; - REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); +void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct ath_softc *sc = hw->priv; - set = AR_PMCTRL_PWR_STATE_D1D3_REAL; - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); + mutex_lock(&sc->mutex); + device_init_wakeup(sc->dev, 1); + device_set_wakeup_enable(sc->dev, enabled); + mutex_unlock(&sc->mutex); +} - REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); +void ath9k_init_wow(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; - /* to bring down WOW power low margin */ - set = BIT(13); - REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); - /* HW WoW */ - clr = BIT(5); - REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr); + if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && + (sc->driver_data & ATH9K_PCI_WOW) && + device_can_wakeup(sc->dev)) + hw->wiphy->wowlan = &ath9k_wowlan_support; - ath9k_hw_set_powermode_wow_sleep(ah); - ah->wow_event_mask = wow_event_mask; + atomic_set(&sc->wow_sleep_proc_intr, -1); + atomic_set(&sc->wow_got_bmiss_intr, -1); } -EXPORT_SYMBOL(ath9k_hw_wow_enable); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index dd30452df96..7c28cb55610 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -47,8 +47,6 @@ static u16 bits_per_symbol[][2] = { { 260, 540 }, /* 7: 64-QAM 5/6 */ }; -#define IS_HT_RATE(_rate) ((_rate) & 0x80) - static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, struct sk_buff *skb); static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, @@ -109,9 +107,6 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid) { struct ath_atx_ac *ac = tid->ac; - if (tid->paused) - return; - if (tid->sched) return; @@ -174,14 +169,7 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, static struct ath_atx_tid * ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb) { - struct ieee80211_hdr *hdr; - u8 tidno = 0; - - hdr = (struct ieee80211_hdr *) skb->data; - if (ieee80211_is_data_qos(hdr->frame_control)) - tidno = ieee80211_get_qos_ctl(hdr)[0]; - - tidno &= IEEE80211_QOS_CTL_TID_MASK; + u8 tidno = skb->priority & IEEE80211_QOS_CTL_TID_MASK; return ATH_AN_2_TID(an, tidno); } @@ -781,11 +769,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf, if (bt_aggr_limit) aggr_limit = bt_aggr_limit; - /* - * h/w can accept aggregates up to 16 bit lengths (65535). - * The IE, however can hold up to 65536, which shows up here - * as zero. Ignore 65536 since we are constrained by hw. - */ if (tid->an->maxampdu) aggr_limit = min(aggr_limit, tid->an->maxampdu); @@ -904,6 +887,15 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq, tx_info = IEEE80211_SKB_CB(skb); tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT; + + /* + * No aggregation session is running, but there may be frames + * from a previous session or a failed attempt in the queue. + * Send them out as normal data frames + */ + if (!tid->active) + tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU; + if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) { bf->bf_state.bf_type = 0; return bf; @@ -1054,11 +1046,11 @@ static int ath_max_framelen(int usec, int mcs, bool ht40, bool sgi) int symbols, bits; int bytes = 0; + usec -= L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); symbols = sgi ? TIME_SYMBOLS_HALFGI(usec) : TIME_SYMBOLS(usec); bits = symbols * bits_per_symbol[mcs % 8][ht40] * streams; bits -= OFDM_PLCP_BITS; bytes = bits / 8; - bytes -= L_STF + L_LTF + L_SIG + HT_SIG + HT_STF + HT_LTF(streams); if (bytes > 65532) bytes = 65532; @@ -1090,6 +1082,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, struct ath_tx_info *info, int len, bool rts) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *rates; @@ -1159,7 +1152,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, } /* legacy rates */ - rate = &sc->sbands[tx_info->band].bitrates[rates[i].idx]; + rate = &common->sbands[tx_info->band].bitrates[rates[i].idx]; if ((tx_info->band == IEEE80211_BAND_2GHZ) && !(rate->flags & IEEE80211_RATE_ERP_G)) phy = WLAN_RC_PHY_CCK; @@ -1241,12 +1234,13 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (bf->bf_next) info.link = bf->bf_next->bf_daddr; else - info.link = 0; + info.link = (sc->tx99_state) ? bf->bf_daddr : 0; if (!bf_first) { bf_first = bf; - info.flags = ATH9K_TXDESC_INTREQ; + if (!sc->tx99_state) + info.flags = ATH9K_TXDESC_INTREQ; if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) || txq == sc->tx.uapsdq) info.flags |= ATH9K_TXDESC_CLRDMASK; @@ -1275,6 +1269,10 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (!rts_thresh || (len > rts_thresh)) rts = true; } + + if (!aggr) + len = fi->framelen; + ath_buf_set_rate(sc, bf, &info, len, rts); } @@ -1405,8 +1403,8 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, * has already been added. */ if (sta->ht_cap.ht_supported) { - an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + - sta->ht_cap.ampdu_factor); + an->maxampdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR + + sta->ht_cap.ampdu_factor)) - 1; density = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density); an->mpdudensity = density; } @@ -1415,7 +1413,6 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta, ath_tx_tid_change_state(sc, txtid); txtid->active = true; - txtid->paused = true; *ssn = txtid->seq_start = txtid->seq_next; txtid->bar_index = -1; @@ -1435,7 +1432,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid) ath_txq_lock(sc, txq); txtid->active = false; - txtid->paused = false; ath_tx_flush_tid(sc, txtid); ath_tx_tid_change_state(sc, txtid); ath_txq_unlock_complete(sc, txq); @@ -1453,14 +1449,16 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc, for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { - if (!tid->sched) - continue; - ac = tid->ac; txq = ac->txq; ath_txq_lock(sc, txq); + if (!tid->sched) { + ath_txq_unlock(sc, txq); + continue; + } + buffered = ath_tid_has_buffered(tid); tid->sched = false; @@ -1493,7 +1491,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an) ath_txq_lock(sc, txq); ac->clear_ps_filter = true; - if (!tid->paused && ath_tid_has_buffered(tid)) { + if (ath_tid_has_buffered(tid)) { ath_tx_queue_tid(txq, tid); ath_txq_schedule(sc, txq); } @@ -1516,7 +1514,6 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta, ath_txq_lock(sc, txq); tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor; - tid->paused = false; if (ath_tid_has_buffered(tid)) { ath_tx_queue_tid(txq, tid); @@ -1550,8 +1547,6 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw, continue; tid = ATH_AN_2_TID(an, i); - if (tid->paused) - continue; ath_txq_lock(sc, tid->ac->txq); while (nframes > 0) { @@ -1704,16 +1699,9 @@ int ath_cabq_update(struct ath_softc *sc) int qnum = sc->beacon.cabq->axq_qnum; ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); - /* - * Ensure the readytime % is within the bounds. - */ - if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND) - sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND; - else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) - sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; - qi.tqi_readyTime = (cur_conf->beacon_interval * - sc->config.cabqReadytime) / 100; + qi.tqi_readyTime = (TU_TO_USEC(cur_conf->beacon_interval) * + ATH_CABQ_READY_TIME) / 100; ath_txq_update(sc, qnum, &qi); return 0; @@ -1782,7 +1770,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); @@ -1792,6 +1780,9 @@ bool ath_drain_all_txq(struct ath_softc *sc) if (!ATH_TXQ_SETUP(sc, i)) continue; + if (!sc->tx.txq[i].axq_depth) + continue; + if (ath9k_hw_numtxpending(ah, sc->tx.txq[i].axq_qnum)) npend |= BIT(i); } @@ -1827,11 +1818,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; @@ -1853,9 +1845,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq) list_del(&tid->list); tid->sched = false; - if (tid->paused) - continue; - if (ath_tx_sched_aggr(sc, txq, tid, &stop)) sent = true; @@ -1948,7 +1937,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } - if (!edma) { + if (!edma || sc->tx99_state) { TX_STAT_INC(txq->axq_qnum, txstart); ath9k_hw_txstart(ah, txq->axq_qnum); } @@ -2027,6 +2016,9 @@ static void setup_frame_info(struct ieee80211_hw *hw, fi->keyix = ATH9K_TXKEYIX_INVALID; fi->keytype = keytype; fi->framelen = framelen; + + if (!rate) + return; fi->rtscts_rate = rate->hw_value; if (short_preamble) fi->rtscts_rate |= rate->hw_value_short; @@ -2037,8 +2029,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate) struct ath_hw *ah = sc->sc_ah; struct ath9k_channel *curchan = ah->curchan; - if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && - (curchan->channelFlags & CHANNEL_5GHZ) && + if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) && (chainmask == 0x7) && (rate < 0x90)) return 0x3; else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) && @@ -2072,7 +2063,7 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, ATH_TXBUF_RESET(bf); - if (tid) { + if (tid && ieee80211_is_data_present(hdr->frame_control)) { fragno = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; seqno = tid->seq_next; hdr->seq_ctrl = cpu_to_le16(tid->seq_next << IEEE80211_SEQ_SEQ_SHIFT); @@ -2195,14 +2186,15 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, txq->stopped = true; } + if (txctl->an && ieee80211_is_data_present(hdr->frame_control)) + tid = ath_get_skb_tid(sc, txctl->an, skb); + if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); } else if (txctl->an && ieee80211_is_data_present(hdr->frame_control)) { - tid = ath_get_skb_tid(sc, txctl->an, skb); - WARN_ON(tid->ac->txq != txctl->txq); if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) @@ -2329,7 +2321,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb); if (sc->sc_ah->caldata) - sc->sc_ah->caldata->paprd_packet_sent = true; + set_bit(PAPRD_PACKET_SENT, &sc->sc_ah->caldata->cal_flags); if (!(tx_flags & ATH_TX_ERROR)) /* Frame was ACKed */ @@ -2379,6 +2371,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); bf->bf_buf_addr = 0; + if (sc->tx99_state) + goto skip_tx_complete; if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, @@ -2391,6 +2385,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, ath_debug_stat_tx(sc, bf, ts, txq, tx_flags); ath_tx_complete(sc, skb, tx_flags, txq); } +skip_tx_complete: /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't * accidentally reference it later. */ @@ -2475,7 +2470,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)) { @@ -2558,7 +2553,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); @@ -2574,7 +2569,7 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) sc->beacon.tx_processed = true; sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK); - ath9k_csa_is_finished(sc); + ath9k_csa_update(sc); continue; } @@ -2701,7 +2696,6 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) tid->baw_size = WME_MAX_BA; tid->baw_head = tid->baw_tail = 0; tid->sched = false; - tid->paused = false; tid->active = false; __skb_queue_head_init(&tid->buf_q); __skb_queue_head_init(&tid->retry_q); @@ -2749,3 +2743,50 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) ath_txq_unlock(sc, txq); } } + +#ifdef CONFIG_ATH9K_TX99 + +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ath_frame_info *fi = get_frame_info(skb); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_buf *bf; + int padpos, padsize; + + padpos = ieee80211_hdrlen(hdr->frame_control); + padsize = padpos & 3; + + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) { + ath_dbg(common, XMIT, + "tx99 padding failed\n"); + return -EINVAL; + } + + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + } + + fi->keyix = ATH9K_TXKEYIX_INVALID; + fi->framelen = skb->len + FCS_LEN; + fi->keytype = ATH9K_KEY_TYPE_CLEAR; + + bf = ath_tx_setup_buffer(sc, txctl->txq, NULL, skb); + if (!bf) { + ath_dbg(common, XMIT, "tx99 buffer setup failed\n"); + return -EINVAL; + } + + ath_set_rates(sc->tx99_vif, NULL, bf); + + ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); + ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum); + + ath_tx_send_normal(sc, txctl->txq, NULL, skb); + + return 0; +} + +#endif /* CONFIG_ATH9K_TX99 */ diff --git a/drivers/net/wireless/ath/carl9170/debug.c b/drivers/net/wireless/ath/carl9170/debug.c index 3d70cd277fd..1c0af9cd9a8 100644 --- a/drivers/net/wireless/ath/carl9170/debug.c +++ b/drivers/net/wireless/ath/carl9170/debug.c @@ -37,7 +37,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/seq_file.h> diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 349fa22a921..f8ded84b7be 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -37,7 +37,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/etherdevice.h> @@ -1708,7 +1707,9 @@ found: return 0; } -static void carl9170_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void carl9170_op_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) { struct ar9170 *ar = hw->priv; unsigned int vid; @@ -1968,18 +1969,6 @@ static int carl9170_parse_eeprom(struct ar9170 *ar) return -ENOMEM; ar->num_channels = chans; - /* - * I measured this, a bandswitch takes roughly - * 135 ms and a frequency switch about 80. - * - * FIXME: measure these values again once EEPROM settings - * are used, that will influence them! - */ - if (bands == 2) - ar->hw->channel_change_time = 135 * 1000; - else - ar->hw->channel_change_time = 80 * 1000; - regulatory->current_rd = le16_to_cpu(ar->eeprom.reg_domain[0]); /* second part of wiphy init */ diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index e935f61c7fa..924135b8e57 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -37,7 +37,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/etherdevice.h> @@ -520,6 +519,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) { struct ieee80211_hdr *hdr = data; struct ieee80211_tim_ie *tim_ie; + struct ath_common *common = &ar->common; u8 *tim; u8 tim_len; bool cam; @@ -527,17 +527,13 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) if (likely(!(ar->hw->conf.flags & IEEE80211_CONF_PS))) return; - /* check if this really is a beacon */ - if (!ieee80211_is_beacon(hdr->frame_control)) - return; - /* min. beacon length + FCS_LEN */ if (len <= 40 + FCS_LEN) return; + /* check if this really is a beacon */ /* and only beacons from the associated BSSID, please */ - if (!ether_addr_equal(hdr->addr3, ar->common.curbssid) || - !ar->common.curaid) + if (!ath_is_mybeacon(common, hdr) || !common->curaid) return; ar->ps.last_beacon = jiffies; @@ -576,7 +572,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) static void carl9170_ba_check(struct ar9170 *ar, void *data, unsigned int len) { - struct ieee80211_bar *bar = (void *) data; + struct ieee80211_bar *bar = data; struct carl9170_bar_list_entry *entry; unsigned int queue; @@ -602,8 +598,8 @@ static void carl9170_ba_check(struct ar9170 *ar, void *data, unsigned int len) if (bar->start_seq_num == entry_bar->start_seq_num && TID_CHECK(bar->control, entry_bar->control) && - ether_addr_equal(bar->ra, entry_bar->ta) && - ether_addr_equal(bar->ta, entry_bar->ra)) { + ether_addr_equal_64bits(bar->ra, entry_bar->ta) && + ether_addr_equal_64bits(bar->ta, entry_bar->ra)) { struct ieee80211_tx_info *tx_info; tx_info = IEEE80211_SKB_CB(entry_skb); diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index e3f696ee4d2..4cadfd48ffd 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -37,7 +37,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/etherdevice.h> diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 307bc0ddff9..f35c7f30f9a 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -773,7 +773,7 @@ void carl9170_usb_stop(struct ar9170 *ar) complete_all(&ar->cmd_wait); /* This is required to prevent an early completion on _start */ - INIT_COMPLETION(ar->cmd_wait); + reinit_completion(&ar->cmd_wait); /* * Note: @@ -1076,8 +1076,14 @@ static int carl9170_usb_probe(struct usb_interface *intf, carl9170_set_state(ar, CARL9170_STOPPED); - return request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, + err = request_firmware_nowait(THIS_MODULE, 1, CARL9170FW_NAME, &ar->udev->dev, GFP_KERNEL, ar, carl9170_usb_firmware_step2); + if (err) { + usb_put_dev(udev); + usb_put_dev(udev); + carl9170_free(ar); + } + return err; } static void carl9170_usb_disconnect(struct usb_interface *intf) diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index 491305c81fc..650be79c7ac 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -19,7 +19,7 @@ #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" -#include "ath9k.h" +#include "ath.h" /* * tolerated deviation of radar time stamp in usecs on both sides @@ -73,9 +73,52 @@ static const struct radar_types etsi_radar_types_v15 = { .radar_types = etsi_radar_ref_types_v15, }; -/* for now, we support ETSI radar types, FCC and JP are TODO */ +#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ +{ \ + ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ + PMIN - PRI_TOLERANCE, \ + PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF, \ + PPB_THRESH(PPB), PRI_TOLERANCE, \ +} + +static const struct radar_detector_specs fcc_radar_ref_types[] = { + FCC_PATTERN(0, 0, 1, 1428, 1428, 1, 18), + FCC_PATTERN(1, 0, 5, 150, 230, 1, 23), + FCC_PATTERN(2, 6, 10, 200, 500, 1, 16), + FCC_PATTERN(3, 11, 20, 200, 500, 1, 12), + FCC_PATTERN(4, 50, 100, 1000, 2000, 20, 1), + FCC_PATTERN(5, 0, 1, 333, 333, 1, 9), +}; + +static const struct radar_types fcc_radar_types = { + .region = NL80211_DFS_FCC, + .num_radar_types = ARRAY_SIZE(fcc_radar_ref_types), + .radar_types = fcc_radar_ref_types, +}; + +#define JP_PATTERN FCC_PATTERN +static const struct radar_detector_specs jp_radar_ref_types[] = { + JP_PATTERN(0, 0, 1, 1428, 1428, 1, 18), + JP_PATTERN(1, 2, 3, 3846, 3846, 1, 18), + JP_PATTERN(2, 0, 1, 1388, 1388, 1, 18), + JP_PATTERN(3, 1, 2, 4000, 4000, 1, 18), + JP_PATTERN(4, 0, 5, 150, 230, 1, 23), + JP_PATTERN(5, 6, 10, 200, 500, 1, 16), + JP_PATTERN(6, 11, 20, 200, 500, 1, 12), + JP_PATTERN(7, 50, 100, 1000, 2000, 20, 1), + JP_PATTERN(5, 0, 1, 333, 333, 1, 9), +}; + +static const struct radar_types jp_radar_types = { + .region = NL80211_DFS_JP, + .num_radar_types = ARRAY_SIZE(jp_radar_ref_types), + .radar_types = jp_radar_ref_types, +}; + static const struct radar_types *dfs_domains[] = { &etsi_radar_types_v15, + &fcc_radar_types, + &jp_radar_types, }; /** @@ -143,7 +186,6 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) { u32 sz, i; struct channel_detector *cd; - struct ath_common *common = ath9k_hw_common(dpd->ah); cd = kmalloc(sizeof(*cd), GFP_ATOMIC); if (cd == NULL) @@ -167,7 +209,7 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) return cd; fail: - ath_dbg(common, DFS, + ath_dbg(dpd->common, DFS, "failed to allocate channel_detector for freq=%d\n", freq); channel_detector_exit(dpd, cd); return NULL; @@ -242,7 +284,7 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) struct pri_detector *pd = cd->detectors[i]; struct pri_sequence *ps = pd->add_pulse(pd, event); if (ps != NULL) { - ath_dbg(ath9k_hw_common(dpd->ah), DFS, + ath_dbg(dpd->common, DFS, "DFS: radar found on freq=%d: id=%d, pri=%d, " "count=%d, count_false=%d\n", event->freq, pd->rs->type_id, @@ -254,6 +296,12 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) return false; } +static struct ath_dfs_pool_stats +dpd_get_stats(struct dfs_pattern_detector *dpd) +{ + return global_dfs_pool_stats; +} + static bool dpd_set_domain(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region) { @@ -284,14 +332,18 @@ static struct dfs_pattern_detector default_dpd = { .exit = dpd_exit, .set_dfs_domain = dpd_set_domain, .add_pulse = dpd_add_pulse, + .get_stats = dpd_get_stats, .region = NL80211_DFS_UNSET, }; struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region) { struct dfs_pattern_detector *dpd; - struct ath_common *common = ath9k_hw_common(ah); + + if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS)) + return NULL; dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); if (dpd == NULL) @@ -300,7 +352,7 @@ dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) *dpd = default_dpd; INIT_LIST_HEAD(&dpd->channel_detectors); - dpd->ah = ah; + dpd->common = common; if (dpd->set_dfs_domain(dpd, region)) return dpd; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h index 90a5abcc426..dde2652b787 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/dfs_pattern_detector.h @@ -22,6 +22,19 @@ #include <linux/nl80211.h> /** + * struct ath_dfs_pool_stats - DFS Statistics for global pools + */ +struct ath_dfs_pool_stats { + u32 pool_reference; + u32 pulse_allocated; + u32 pulse_alloc_error; + u32 pulse_used; + u32 pseq_allocated; + u32 pseq_alloc_error; + u32 pseq_used; +}; + +/** * struct pulse_event - describing pulses reported by PHY * @ts: pulse time stamp in us * @freq: channel frequency in MHz @@ -77,11 +90,12 @@ struct dfs_pattern_detector { bool (*add_pulse)(struct dfs_pattern_detector *dpd, struct pulse_event *pe); + struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd); enum nl80211_dfs_regions region; u8 num_radar_types; u64 last_pulse_ts; /* needed for ath_dbg() */ - struct ath_hw *ah; + struct ath_common *common; const struct radar_detector_specs *radar_spec; struct list_head channel_detectors; @@ -92,15 +106,7 @@ struct dfs_pattern_detector { * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation * @return instance pointer on success, NULL otherwise */ -#if defined(CONFIG_ATH9K_DFS_CERTIFIED) extern struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region); -#else -static inline struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) -{ - return NULL; -} -#endif /* CONFIG_ATH9K_DFS_CERTIFIED */ - +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region); #endif /* DFS_PATTERN_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c index 5ba4b6fe37c..43b60817888 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/dfs_pri_detector.c @@ -17,10 +17,14 @@ #include <linux/slab.h> #include <linux/spinlock.h> -#include "ath9k.h" +#include "ath.h" #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" -#include "dfs_debug.h" + +struct ath_dfs_pool_stats global_dfs_pool_stats = {}; + +#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) +#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) /** * struct pulse_elem - elements in pulse queue @@ -392,7 +396,7 @@ static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de, if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) { pri_detector_reset(de, ts); - return false; + return NULL; } ps = pseq_handler_check_detection(de); diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/dfs_pri_detector.h index 723962d1abc..79f0fff4d1e 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h +++ b/drivers/net/wireless/ath/dfs_pri_detector.h @@ -19,6 +19,8 @@ #include <linux/list.h> +extern struct ath_dfs_pool_stats global_dfs_pool_stats; + /** * struct pri_sequence - sequence of pulses matching one PRI * @head: list_head diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c index 8e99540cd90..8b0ac14d5c3 100644 --- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -59,6 +59,14 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, } EXPORT_SYMBOL(ath_rxbuf_alloc); +bool ath_is_mybeacon(struct ath_common *common, struct ieee80211_hdr *hdr) +{ + return ieee80211_is_beacon(hdr->frame_control) && + !is_zero_ether_addr(common->curbssid) && + ether_addr_equal_64bits(hdr->addr3, common->curbssid); +} +EXPORT_SYMBOL(ath_is_mybeacon); + void ath_printk(const char *level, const struct ath_common* common, const char *fmt, ...) { diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 7d077c752dd..415393dfb6f 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -37,17 +37,18 @@ static int __ath_regd_init(struct ath_regulatory *reg); /* We enable active scan on these a case by case basis by regulatory domain */ #define ATH9K_2GHZ_CH12_13 REG_RULE(2467-10, 2472+10, 40, 0, 20,\ - NL80211_RRF_PASSIVE_SCAN) + NL80211_RRF_NO_IR) #define ATH9K_2GHZ_CH14 REG_RULE(2484-10, 2484+10, 40, 0, 20,\ - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_OFDM) + NL80211_RRF_NO_IR | \ + NL80211_RRF_NO_OFDM) /* We allow IBSS on these on a case by case basis by regulatory domain */ #define ATH9K_5GHZ_5150_5350 REG_RULE(5150-10, 5350+10, 80, 0, 30,\ - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define ATH9K_5GHZ_5470_5850 REG_RULE(5470-10, 5850+10, 80, 0, 30,\ - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define ATH9K_5GHZ_5725_5850 REG_RULE(5725-10, 5850+10, 80, 0, 30,\ - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define ATH9K_2GHZ_ALL ATH9K_2GHZ_CH01_11, \ ATH9K_2GHZ_CH12_13, \ @@ -113,6 +114,87 @@ static const struct ieee80211_regdomain ath_world_regdom_67_68_6A_6C = { } }; +static bool dynamic_country_user_possible(struct ath_regulatory *reg) +{ + if (config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING)) + return true; + + switch (reg->country_code) { + case CTRY_UNITED_STATES: + case CTRY_JAPAN1: + case CTRY_JAPAN2: + case CTRY_JAPAN3: + case CTRY_JAPAN4: + case CTRY_JAPAN5: + case CTRY_JAPAN6: + case CTRY_JAPAN7: + case CTRY_JAPAN8: + case CTRY_JAPAN9: + case CTRY_JAPAN10: + case CTRY_JAPAN11: + case CTRY_JAPAN12: + case CTRY_JAPAN13: + case CTRY_JAPAN14: + case CTRY_JAPAN15: + case CTRY_JAPAN16: + case CTRY_JAPAN17: + case CTRY_JAPAN18: + case CTRY_JAPAN19: + case CTRY_JAPAN20: + case CTRY_JAPAN21: + case CTRY_JAPAN22: + case CTRY_JAPAN23: + case CTRY_JAPAN24: + case CTRY_JAPAN25: + case CTRY_JAPAN26: + case CTRY_JAPAN27: + case CTRY_JAPAN28: + case CTRY_JAPAN29: + case CTRY_JAPAN30: + case CTRY_JAPAN31: + case CTRY_JAPAN32: + case CTRY_JAPAN33: + case CTRY_JAPAN34: + case CTRY_JAPAN35: + case CTRY_JAPAN36: + case CTRY_JAPAN37: + case CTRY_JAPAN38: + case CTRY_JAPAN39: + case CTRY_JAPAN40: + case CTRY_JAPAN41: + case CTRY_JAPAN42: + case CTRY_JAPAN43: + case CTRY_JAPAN44: + case CTRY_JAPAN45: + case CTRY_JAPAN46: + case CTRY_JAPAN47: + case CTRY_JAPAN48: + case CTRY_JAPAN49: + case CTRY_JAPAN50: + case CTRY_JAPAN51: + case CTRY_JAPAN52: + case CTRY_JAPAN53: + case CTRY_JAPAN54: + case CTRY_JAPAN55: + case CTRY_JAPAN56: + case CTRY_JAPAN57: + case CTRY_JAPAN58: + case CTRY_JAPAN59: + return false; + } + + return true; +} + +static bool ath_reg_dyn_country_user_allow(struct ath_regulatory *reg) +{ + if (!config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) + return false; + if (!dynamic_country_user_possible(reg)) + return false; + return true; +} + static inline bool is_wwr_sku(u16 regd) { return ((regd & COUNTRY_ERD_FLAG) != COUNTRY_ERD_FLAG) && @@ -140,7 +222,7 @@ static const struct ieee80211_regdomain *ath_default_world_regdomain(void) static const struct ieee80211_regdomain *ath_world_regdomain(struct ath_regulatory *reg) { - switch (reg->regpair->regDmnEnum) { + switch (reg->regpair->reg_domain) { case 0x60: case 0x61: case 0x62: @@ -177,118 +259,139 @@ static bool ath_is_radar_freq(u16 center_freq) return (center_freq >= 5260 && center_freq <= 5700); } +static void ath_force_clear_no_ir_chan(struct wiphy *wiphy, + struct ieee80211_channel *ch) +{ + const struct ieee80211_reg_rule *reg_rule; + + reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq)); + if (IS_ERR(reg_rule)) + return; + + if (!(reg_rule->flags & NL80211_RRF_NO_IR)) + if (ch->flags & IEEE80211_CHAN_NO_IR) + ch->flags &= ~IEEE80211_CHAN_NO_IR; +} + +static void ath_force_clear_no_ir_freq(struct wiphy *wiphy, u16 center_freq) +{ + struct ieee80211_channel *ch; + + ch = ieee80211_get_channel(wiphy, center_freq); + if (!ch) + return; + + ath_force_clear_no_ir_chan(wiphy, ch); +} + +static void ath_force_no_ir_chan(struct ieee80211_channel *ch) +{ + ch->flags |= IEEE80211_CHAN_NO_IR; +} + +static void ath_force_no_ir_freq(struct wiphy *wiphy, u16 center_freq) +{ + struct ieee80211_channel *ch; + + ch = ieee80211_get_channel(wiphy, center_freq); + if (!ch) + return; + + ath_force_no_ir_chan(ch); +} + +static void +__ath_reg_apply_beaconing_flags(struct wiphy *wiphy, + struct ath_regulatory *reg, + enum nl80211_reg_initiator initiator, + struct ieee80211_channel *ch) +{ + if (ath_is_radar_freq(ch->center_freq) || + (ch->flags & IEEE80211_CHAN_RADAR)) + return; + + switch (initiator) { + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + ath_force_clear_no_ir_chan(wiphy, ch); + break; + case NL80211_REGDOM_SET_BY_USER: + if (ath_reg_dyn_country_user_allow(reg)) + ath_force_clear_no_ir_chan(wiphy, ch); + break; + default: + if (ch->beacon_found) + ch->flags &= ~IEEE80211_CHAN_NO_IR; + } +} + /* - * N.B: These exception rules do not apply radar freqs. + * These exception rules do not apply radar frequencies. * - * - We enable adhoc (or beaconing) if allowed by 11d - * - We enable active scan if the channel is allowed by 11d + * - We enable initiating radiation if the country IE says its fine: * - If no country IE has been processed and a we determine we have - * received a beacon on a channel we can enable active scan and - * adhoc (or beaconing). + * received a beacon on a channel we can enable initiating radiation. */ static void ath_reg_apply_beaconing_flags(struct wiphy *wiphy, + struct ath_regulatory *reg, enum nl80211_reg_initiator initiator) { enum ieee80211_band band; struct ieee80211_supported_band *sband; - const struct ieee80211_reg_rule *reg_rule; struct ieee80211_channel *ch; unsigned int i; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wiphy->bands[band]) continue; - sband = wiphy->bands[band]; - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - - if (ath_is_radar_freq(ch->center_freq) || - (ch->flags & IEEE80211_CHAN_RADAR)) - continue; - - if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (IS_ERR(reg_rule)) - continue; - /* - * If 11d had a rule for this channel ensure - * we enable adhoc/beaconing if it allows us to - * use it. Note that we would have disabled it - * by applying our static world regdomain by - * default during init, prior to calling our - * regulatory_hint(). - */ - if (!(reg_rule->flags & - NL80211_RRF_NO_IBSS)) - ch->flags &= - ~IEEE80211_CHAN_NO_IBSS; - if (!(reg_rule->flags & - NL80211_RRF_PASSIVE_SCAN)) - ch->flags &= - ~IEEE80211_CHAN_PASSIVE_SCAN; - } else { - if (ch->beacon_found) - ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN); - } + __ath_reg_apply_beaconing_flags(wiphy, reg, + initiator, ch); } } - } -/* Allows active scan scan on Ch 12 and 13 */ +/** + * ath_reg_apply_ir_flags() + * @wiphy: the wiphy to use + * @initiator: the regulatory hint initiator + * + * If no country IE has been received always enable passive scan + * and no-ibss on these channels. This is only done for specific + * regulatory SKUs. + * + * If a country IE has been received check its rule for this + * channel first before enabling active scan. The passive scan + * would have been enforced by the initial processing of our + * custom regulatory domain. + */ static void -ath_reg_apply_active_scan_flags(struct wiphy *wiphy, - enum nl80211_reg_initiator initiator) +ath_reg_apply_ir_flags(struct wiphy *wiphy, + struct ath_regulatory *reg, + enum nl80211_reg_initiator initiator) { struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - const struct ieee80211_reg_rule *reg_rule; sband = wiphy->bands[IEEE80211_BAND_2GHZ]; if (!sband) return; - /* - * If no country IE has been received always enable active scan - * on these channels. This is only done for specific regulatory SKUs - */ - if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { - ch = &sband->channels[11]; /* CH 12 */ - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; - ch = &sband->channels[12]; /* CH 13 */ - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; - return; - } - - /* - * If a country IE has been received check its rule for this - * channel first before enabling active scan. The passive scan - * would have been enforced by the initial processing of our - * custom regulatory domain. - */ - - ch = &sband->channels[11]; /* CH 12 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (!IS_ERR(reg_rule)) { - if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; - } - - ch = &sband->channels[12]; /* CH 13 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); - if (!IS_ERR(reg_rule)) { - if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + switch(initiator) { + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + ath_force_clear_no_ir_freq(wiphy, 2467); + ath_force_clear_no_ir_freq(wiphy, 2472); + break; + case NL80211_REGDOM_SET_BY_USER: + if (!ath_reg_dyn_country_user_allow(reg)) + break; + ath_force_clear_no_ir_freq(wiphy, 2467); + ath_force_clear_no_ir_freq(wiphy, 2472); + break; + default: + ath_force_no_ir_freq(wiphy, 2467); + ath_force_no_ir_freq(wiphy, 2472); } } @@ -320,8 +423,7 @@ static void ath_reg_apply_radar_flags(struct wiphy *wiphy) */ if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch->flags |= IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR; } } @@ -329,18 +431,21 @@ static void ath_reg_apply_world_flags(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, struct ath_regulatory *reg) { - switch (reg->regpair->regDmnEnum) { + switch (reg->regpair->reg_domain) { case 0x60: case 0x63: case 0x66: case 0x67: case 0x6C: - ath_reg_apply_beaconing_flags(wiphy, initiator); + ath_reg_apply_beaconing_flags(wiphy, reg, initiator); break; case 0x68: - ath_reg_apply_beaconing_flags(wiphy, initiator); - ath_reg_apply_active_scan_flags(wiphy, initiator); + ath_reg_apply_beaconing_flags(wiphy, reg, initiator); + ath_reg_apply_ir_flags(wiphy, reg, initiator); break; + default: + if (ath_reg_dyn_country_user_allow(reg)) + ath_reg_apply_beaconing_flags(wiphy, reg, initiator); } } @@ -356,14 +461,49 @@ static u16 ath_regd_find_country_by_name(char *alpha2) return -1; } +static int __ath_reg_dyn_country(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + u16 country_code; + + if (request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + !ath_is_world_regd(reg)) + return -EINVAL; + + country_code = ath_regd_find_country_by_name(request->alpha2); + if (country_code == (u16) -1) + return -EINVAL; + + reg->current_rd = COUNTRY_ERD_FLAG; + reg->current_rd |= country_code; + + __ath_regd_init(reg); + + ath_reg_apply_world_flags(wiphy, request->initiator, reg); + + return 0; +} + +static void ath_reg_dyn_country(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + if (__ath_reg_dyn_country(wiphy, reg, request)) + return; + + printk(KERN_DEBUG "ath: regdomain 0x%0x " + "dynamically updated by %s\n", + reg->current_rd, + reg_initiator_name(request->initiator)); +} + void ath_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct ath_regulatory *reg) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); - u16 country_code; - /* We always apply this */ ath_reg_apply_radar_flags(wiphy); @@ -388,25 +528,13 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, sizeof(struct ath_regulatory)); break; case NL80211_REGDOM_SET_BY_DRIVER: + break; case NL80211_REGDOM_SET_BY_USER: + if (ath_reg_dyn_country_user_allow(reg)) + ath_reg_dyn_country(wiphy, reg, request); break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (!ath_is_world_regd(reg)) - break; - - country_code = ath_regd_find_country_by_name(request->alpha2); - if (country_code == (u16) -1) - break; - - reg->current_rd = COUNTRY_ERD_FLAG; - reg->current_rd |= country_code; - - printk(KERN_DEBUG "ath: regdomain 0x%0x updated by CountryIE\n", - reg->current_rd); - __ath_regd_init(reg); - - ath_reg_apply_world_flags(wiphy, request->initiator, reg); - + ath_reg_dyn_country(wiphy, reg, request); break; } } @@ -432,7 +560,7 @@ static bool ath_regd_is_eeprom_valid(struct ath_regulatory *reg) printk(KERN_DEBUG "ath: EEPROM indicates we " "should expect a direct regpair map\n"); for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) - if (regDomainPairs[i].regDmnEnum == rd) + if (regDomainPairs[i].reg_domain == rd) return true; } printk(KERN_DEBUG @@ -489,7 +617,7 @@ ath_get_regpair(int regdmn) if (regdmn == NO_ENUMRD) return NULL; for (i = 0; i < ARRAY_SIZE(regDomainPairs); i++) { - if (regDomainPairs[i].regDmnEnum == regdmn) + if (regDomainPairs[i].reg_domain == regdmn) return ®DomainPairs[i]; } return NULL; @@ -504,7 +632,8 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, const struct ieee80211_regdomain *regd; wiphy->reg_notifier = reg_notifier; - wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; + wiphy->regulatory_flags |= REGULATORY_STRICT_REG | + REGULATORY_CUSTOM_REG; if (ath_is_world_regd(reg)) { /* @@ -512,7 +641,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, * saved on the wiphy orig_* parameters */ regd = ath_world_regdomain(reg); - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_FOLLOW_POWER; } else { /* * This gets applied in the case of the absence of CRDA, @@ -521,6 +650,7 @@ ath_regd_init_wiphy(struct ath_regulatory *reg, */ regd = ath_default_world_regdomain(); } + wiphy_apply_custom_regulatory(wiphy, regd); ath_reg_apply_radar_flags(wiphy); ath_reg_apply_world_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER, reg); @@ -611,7 +741,7 @@ static int __ath_regd_init(struct ath_regulatory *reg) printk(KERN_DEBUG "ath: Country alpha2 being used: %c%c\n", reg->alpha2[0], reg->alpha2[1]); printk(KERN_DEBUG "ath: Regpair used: 0x%0x\n", - reg->regpair->regDmnEnum); + reg->regpair->reg_domain); return 0; } diff --git a/drivers/net/wireless/ath/wcn36xx/Kconfig b/drivers/net/wireless/ath/wcn36xx/Kconfig new file mode 100644 index 00000000000..591ebaea826 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig @@ -0,0 +1,16 @@ +config WCN36XX + tristate "Qualcomm Atheros WCN3660/3680 support" + depends on MAC80211 && HAS_DMA + ---help--- + This module adds support for wireless adapters based on + Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets. + + If you choose to build a module, it'll be called wcn36xx. + +config WCN36XX_DEBUGFS + bool "WCN36XX debugfs support" + depends on WCN36XX + ---help--- + Enabled debugfs support + + If unsure, say Y to make it easier to debug problems. diff --git a/drivers/net/wireless/ath/wcn36xx/Makefile b/drivers/net/wireless/ath/wcn36xx/Makefile new file mode 100644 index 00000000000..50c43b4382b --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_WCN36XX) := wcn36xx.o +wcn36xx-y += main.o \ + dxe.o \ + txrx.o \ + smd.o \ + pmc.o \ + debug.o diff --git a/drivers/net/wireless/ath/wcn36xx/debug.c b/drivers/net/wireless/ath/wcn36xx/debug.c new file mode 100644 index 00000000000..ef44a2da644 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/debug.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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/debugfs.h> +#include <linux/uaccess.h> +#include "wcn36xx.h" +#include "debug.h" +#include "pmc.h" + +#ifdef CONFIG_WCN36XX_DEBUGFS + +static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcn36xx *wcn = file->private_data; + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_vif *vif = NULL; + char buf[3]; + + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (NL80211_IFTYPE_STATION == vif->type) { + if (vif_priv->pw_state == WCN36XX_BMPS) + buf[0] = '1'; + else + buf[0] = '0'; + break; + } + } + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t write_file_bool_bmps(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcn36xx *wcn = file->private_data; + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_vif *vif = NULL; + + char buf[32]; + int buf_size; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + switch (buf[0]) { + case 'y': + case 'Y': + case '1': + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (NL80211_IFTYPE_STATION == vif->type) { + wcn36xx_enable_keep_alive_null_packet(wcn, vif); + wcn36xx_pmc_enter_bmps_state(wcn, vif); + } + } + break; + case 'n': + case 'N': + case '0': + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (NL80211_IFTYPE_STATION == vif->type) + wcn36xx_pmc_exit_bmps_state(wcn, vif); + } + break; + } + + return count; +} + +static const struct file_operations fops_wcn36xx_bmps = { + .open = simple_open, + .read = read_file_bool_bmps, + .write = write_file_bool_bmps, +}; + +static ssize_t write_file_dump(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcn36xx *wcn = file->private_data; + char buf[255], *tmp; + int buf_size; + u32 arg[WCN36xx_MAX_DUMP_ARGS]; + int i; + + memset(buf, 0, sizeof(buf)); + memset(arg, 0, sizeof(arg)); + + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + tmp = buf; + + for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) { + char *begin; + begin = strsep(&tmp, " "); + if (begin == NULL) + break; + + if (kstrtou32(begin, 0, &arg[i]) != 0) + break; + } + + wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2], + arg[3], arg[4]); + wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]); + + return count; +} + +static const struct file_operations fops_wcn36xx_dump = { + .open = simple_open, + .write = write_file_dump, +}; + +#define ADD_FILE(name, mode, fop, priv_data) \ + do { \ + struct dentry *d; \ + d = debugfs_create_file(__stringify(name), \ + mode, dfs->rootdir, \ + priv_data, fop); \ + dfs->file_##name.dentry = d; \ + if (IS_ERR(d)) { \ + wcn36xx_warn("Create the debugfs entry failed");\ + dfs->file_##name.dentry = NULL; \ + } \ + } while (0) + + +void wcn36xx_debugfs_init(struct wcn36xx *wcn) +{ + struct wcn36xx_dfs_entry *dfs = &wcn->dfs; + + dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME, + wcn->hw->wiphy->debugfsdir); + if (IS_ERR(dfs->rootdir)) { + wcn36xx_warn("Create the debugfs failed\n"); + dfs->rootdir = NULL; + } + + ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR, + &fops_wcn36xx_bmps, wcn); + ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn); +} + +void wcn36xx_debugfs_exit(struct wcn36xx *wcn) +{ + struct wcn36xx_dfs_entry *dfs = &wcn->dfs; + debugfs_remove_recursive(dfs->rootdir); +} + +#endif /* CONFIG_WCN36XX_DEBUGFS */ diff --git a/drivers/net/wireless/ath/wcn36xx/debug.h b/drivers/net/wireless/ath/wcn36xx/debug.h new file mode 100644 index 00000000000..46307aa562d --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/debug.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _WCN36XX_DEBUG_H_ +#define _WCN36XX_DEBUG_H_ + +#include <linux/kernel.h> + +#define WCN36xx_MAX_DUMP_ARGS 5 + +#ifdef CONFIG_WCN36XX_DEBUGFS +struct wcn36xx_dfs_file { + struct dentry *dentry; + u32 value; +}; + +struct wcn36xx_dfs_entry { + struct dentry *rootdir; + struct wcn36xx_dfs_file file_bmps_switcher; + struct wcn36xx_dfs_file file_dump; +}; + +void wcn36xx_debugfs_init(struct wcn36xx *wcn); +void wcn36xx_debugfs_exit(struct wcn36xx *wcn); + +#else +static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn) +{ +} +static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn) +{ +} + +#endif /* CONFIG_WCN36XX_DEBUGFS */ + +#endif /* _WCN36XX_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c new file mode 100644 index 00000000000..73f12f196f1 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -0,0 +1,813 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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. + */ + +/* DXE - DMA transfer engine + * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX. + * through low channels data packets are transfered + * through high channels managment packets are transfered + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/interrupt.h> +#include "wcn36xx.h" +#include "txrx.h" + +void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low) +{ + struct wcn36xx_dxe_ch *ch = is_low ? + &wcn->dxe_tx_l_ch : + &wcn->dxe_tx_h_ch; + + return ch->head_blk_ctl->bd_cpu_addr; +} + +static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data) +{ + wcn36xx_dbg(WCN36XX_DBG_DXE, + "wcn36xx_dxe_write_register: addr=%x, data=%x\n", + addr, data); + + writel(data, wcn->mmio + addr); +} + +#define wcn36xx_dxe_write_register_x(wcn, reg, reg_data) \ +do { \ + if (wcn->chip_version == WCN36XX_CHIP_3680) \ + wcn36xx_dxe_write_register(wcn, reg ## _3680, reg_data); \ + else \ + wcn36xx_dxe_write_register(wcn, reg ## _3660, reg_data); \ +} while (0) \ + +static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data) +{ + *data = readl(wcn->mmio + addr); + + wcn36xx_dbg(WCN36XX_DBG_DXE, + "wcn36xx_dxe_read_register: addr=%x, data=%x\n", + addr, *data); +} + +static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next; + int i; + + for (i = 0; i < ch->desc_num && ctl; i++) { + next = ctl->next; + kfree(ctl); + ctl = next; + } +} + +static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *prev_ctl = NULL; + struct wcn36xx_dxe_ctl *cur_ctl = NULL; + int i; + + for (i = 0; i < ch->desc_num; i++) { + cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL); + if (!cur_ctl) + goto out_fail; + + cur_ctl->ctl_blk_order = i; + if (i == 0) { + ch->head_blk_ctl = cur_ctl; + ch->tail_blk_ctl = cur_ctl; + } else if (ch->desc_num - 1 == i) { + prev_ctl->next = cur_ctl; + cur_ctl->next = ch->head_blk_ctl; + } else { + prev_ctl->next = cur_ctl; + } + prev_ctl = cur_ctl; + } + + return 0; + +out_fail: + wcn36xx_dxe_free_ctl_block(ch); + return -ENOMEM; +} + +int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) +{ + int ret; + + wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L; + wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H; + wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L; + wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H; + + wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L; + wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H; + wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L; + wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H; + + wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L; + wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H; + + wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD; + wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD; + + wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB; + wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB; + + wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L; + wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H; + + wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L; + wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H; + + /* DXE control block allocation */ + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch); + if (ret) + goto out_err; + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch); + if (ret) + goto out_err; + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch); + if (ret) + goto out_err; + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch); + if (ret) + goto out_err; + + /* Initialize SMSM state Clear TX Enable RING EMPTY STATE */ + ret = wcn->ctrl_ops->smsm_change_state( + WCN36XX_SMSM_WLAN_TX_ENABLE, + WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); + + return 0; + +out_err: + wcn36xx_err("Failed to allocate DXE control blocks\n"); + wcn36xx_dxe_free_ctl_blks(wcn); + return -ENOMEM; +} + +void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) +{ + wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch); + wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch); + wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch); + wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); +} + +static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch) +{ + struct wcn36xx_dxe_desc *cur_dxe = NULL; + struct wcn36xx_dxe_desc *prev_dxe = NULL; + struct wcn36xx_dxe_ctl *cur_ctl = NULL; + size_t size; + int i; + + size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc); + wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr, + GFP_KERNEL); + if (!wcn_ch->cpu_addr) + return -ENOMEM; + + memset(wcn_ch->cpu_addr, 0, size); + + cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr; + cur_ctl = wcn_ch->head_blk_ctl; + + for (i = 0; i < wcn_ch->desc_num; i++) { + cur_ctl->desc = cur_dxe; + cur_ctl->desc_phy_addr = wcn_ch->dma_addr + + i * sizeof(struct wcn36xx_dxe_desc); + + switch (wcn_ch->ch_type) { + case WCN36XX_DXE_CH_TX_L: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L; + break; + case WCN36XX_DXE_CH_TX_H: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H; + break; + case WCN36XX_DXE_CH_RX_L: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; + cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L; + break; + case WCN36XX_DXE_CH_RX_H: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; + cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H; + break; + } + if (0 == i) { + cur_dxe->phy_next_l = 0; + } else if ((0 < i) && (i < wcn_ch->desc_num - 1)) { + prev_dxe->phy_next_l = + cur_ctl->desc_phy_addr; + } else if (i == (wcn_ch->desc_num - 1)) { + prev_dxe->phy_next_l = + cur_ctl->desc_phy_addr; + cur_dxe->phy_next_l = + wcn_ch->head_blk_ctl->desc_phy_addr; + } + cur_ctl = cur_ctl->next; + prev_dxe = cur_dxe; + cur_dxe++; + } + + return 0; +} + +static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch, + struct wcn36xx_dxe_mem_pool *pool) +{ + int i, chunk_size = pool->chunk_size; + dma_addr_t bd_phy_addr = pool->phy_addr; + void *bd_cpu_addr = pool->virt_addr; + struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl; + + for (i = 0; i < ch->desc_num; i++) { + /* Only every second dxe needs a bd pointer, + the other will point to the skb data */ + if (!(i & 1)) { + cur->bd_phy_addr = bd_phy_addr; + cur->bd_cpu_addr = bd_cpu_addr; + bd_phy_addr += chunk_size; + bd_cpu_addr += chunk_size; + } else { + cur->bd_phy_addr = 0; + cur->bd_cpu_addr = NULL; + } + cur = cur->next; + } +} + +static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch) +{ + int reg_data = 0; + + wcn36xx_dxe_read_register(wcn, + WCN36XX_DXE_INT_MASK_REG, + ®_data); + + reg_data |= wcn_ch; + + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_INT_MASK_REG, + (int)reg_data); + return 0; +} + +static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl) +{ + struct wcn36xx_dxe_desc *dxe = ctl->desc; + struct sk_buff *skb; + + skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC); + if (skb == NULL) + return -ENOMEM; + + dxe->dst_addr_l = dma_map_single(NULL, + skb_tail_pointer(skb), + WCN36XX_PKT_SIZE, + DMA_FROM_DEVICE); + ctl->skb = skb; + + return 0; +} + +static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn, + struct wcn36xx_dxe_ch *wcn_ch) +{ + int i; + struct wcn36xx_dxe_ctl *cur_ctl = NULL; + + cur_ctl = wcn_ch->head_blk_ctl; + + for (i = 0; i < wcn_ch->desc_num; i++) { + wcn36xx_dxe_fill_skb(cur_ctl); + cur_ctl = cur_ctl->next; + } + + return 0; +} + +static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn, + struct wcn36xx_dxe_ch *wcn_ch) +{ + struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl; + int i; + + for (i = 0; i < wcn_ch->desc_num; i++) { + kfree_skb(cur->skb); + cur = cur->next; + } +} + +void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) +{ + struct ieee80211_tx_info *info; + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&wcn->dxe_lock, flags); + skb = wcn->tx_ack_skb; + wcn->tx_ack_skb = NULL; + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + + if (!skb) { + wcn36xx_warn("Spurious TX complete indication\n"); + return; + } + + info = IEEE80211_SKB_CB(skb); + + if (status == 1) + info->flags |= IEEE80211_TX_STAT_ACK; + + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); + + ieee80211_tx_status_irqsafe(wcn->hw, skb); + ieee80211_wake_queues(wcn->hw); +} + +static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl; + struct ieee80211_tx_info *info; + unsigned long flags; + + /* + * Make at least one loop of do-while because in case ring is + * completely full head and tail are pointing to the same element + * and while-do will not make any cycles. + */ + do { + if (ctl->skb) { + dma_unmap_single(NULL, ctl->desc->src_addr_l, + ctl->skb->len, DMA_TO_DEVICE); + info = IEEE80211_SKB_CB(ctl->skb); + if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { + /* Keep frame until TX status comes */ + ieee80211_free_txskb(wcn->hw, ctl->skb); + } + spin_lock_irqsave(&ctl->skb_lock, flags); + if (wcn->queues_stopped) { + wcn->queues_stopped = false; + ieee80211_wake_queues(wcn->hw); + } + spin_unlock_irqrestore(&ctl->skb_lock, flags); + + ctl->skb = NULL; + } + ctl = ctl->next; + } while (ctl != ch->head_blk_ctl && + !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)); + + ch->tail_blk_ctl = ctl; +} + +static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) +{ + struct wcn36xx *wcn = (struct wcn36xx *)dev; + int int_src, int_reason; + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); + + if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) { + wcn36xx_dxe_read_register(wcn, + WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H, + &int_reason); + + /* TODO: Check int_reason */ + + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n"); + reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); + } + + if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) { + wcn36xx_dxe_read_register(wcn, + WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L, + &int_reason); + /* TODO: Check int_reason */ + + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n"); + reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); + } + + return IRQ_HANDLED; +} + +static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev) +{ + struct wcn36xx *wcn = (struct wcn36xx *)dev; + + disable_irq_nosync(wcn->rx_irq); + wcn36xx_dxe_rx_frame(wcn); + enable_irq(wcn->rx_irq); + return IRQ_HANDLED; +} + +static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn) +{ + int ret; + + ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete, + IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn); + if (ret) { + wcn36xx_err("failed to alloc tx irq\n"); + goto out_err; + } + + ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH, + "wcn36xx_rx", wcn); + if (ret) { + wcn36xx_err("failed to alloc rx irq\n"); + goto out_txirq; + } + + enable_irq_wake(wcn->rx_irq); + + return 0; + +out_txirq: + free_irq(wcn->tx_irq, wcn); +out_err: + return ret; + +} + +static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, + struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl; + struct wcn36xx_dxe_desc *dxe = ctl->desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + + while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { + skb = ctl->skb; + dma_addr = dxe->dst_addr_l; + wcn36xx_dxe_fill_skb(ctl); + + switch (ch->ch_type) { + case WCN36XX_DXE_CH_RX_L: + dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, + WCN36XX_DXE_INT_CH1_MASK); + break; + case WCN36XX_DXE_CH_RX_H: + dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, + WCN36XX_DXE_INT_CH3_MASK); + break; + default: + wcn36xx_warn("Unknown channel\n"); + } + + dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE, + DMA_FROM_DEVICE); + wcn36xx_rx_skb(wcn, skb); + ctl = ctl->next; + dxe = ctl->desc; + } + + ch->head_blk_ctl = ctl; + + return 0; +} + +void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn) +{ + int int_src; + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); + + /* RX_LOW_PRI */ + if (int_src & WCN36XX_DXE_INT_CH1_MASK) { + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, + WCN36XX_DXE_INT_CH1_MASK); + wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch)); + } + + /* RX_HIGH_PRI */ + if (int_src & WCN36XX_DXE_INT_CH3_MASK) { + /* Clean up all the INT within this channel */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, + WCN36XX_DXE_INT_CH3_MASK); + wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch)); + } + + if (!int_src) + wcn36xx_warn("No DXE interrupt pending\n"); +} + +int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) +{ + size_t s; + void *cpu_addr; + + /* Allocate BD headers for MGMT frames */ + + /* Where this come from ask QC */ + wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + + 16 - (WCN36XX_BD_CHUNK_SIZE % 8); + + s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H; + cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr, + GFP_KERNEL); + if (!cpu_addr) + goto out_err; + + wcn->mgmt_mem_pool.virt_addr = cpu_addr; + memset(cpu_addr, 0, s); + + /* Allocate BD headers for DATA frames */ + + /* Where this come from ask QC */ + wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + + 16 - (WCN36XX_BD_CHUNK_SIZE % 8); + + s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L; + cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr, + GFP_KERNEL); + if (!cpu_addr) + goto out_err; + + wcn->data_mem_pool.virt_addr = cpu_addr; + memset(cpu_addr, 0, s); + + return 0; + +out_err: + wcn36xx_dxe_free_mem_pools(wcn); + wcn36xx_err("Failed to allocate BD mempool\n"); + return -ENOMEM; +} + +void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn) +{ + if (wcn->mgmt_mem_pool.virt_addr) + dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size * + WCN36XX_DXE_CH_DESC_NUMB_TX_H, + wcn->mgmt_mem_pool.virt_addr, + wcn->mgmt_mem_pool.phy_addr); + + if (wcn->data_mem_pool.virt_addr) { + dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size * + WCN36XX_DXE_CH_DESC_NUMB_TX_L, + wcn->data_mem_pool.virt_addr, + wcn->data_mem_pool.phy_addr); + } +} + +int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, + struct wcn36xx_vif *vif_priv, + struct sk_buff *skb, + bool is_low) +{ + struct wcn36xx_dxe_ctl *ctl = NULL; + struct wcn36xx_dxe_desc *desc = NULL; + struct wcn36xx_dxe_ch *ch = NULL; + unsigned long flags; + + ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch; + + ctl = ch->head_blk_ctl; + + spin_lock_irqsave(&ctl->next->skb_lock, flags); + + /* + * If skb is not null that means that we reached the tail of the ring + * hence ring is full. Stop queues to let mac80211 back off until ring + * has an empty slot again. + */ + if (NULL != ctl->next->skb) { + ieee80211_stop_queues(wcn->hw); + wcn->queues_stopped = true; + spin_unlock_irqrestore(&ctl->next->skb_lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&ctl->next->skb_lock, flags); + + ctl->skb = NULL; + desc = ctl->desc; + + /* Set source address of the BD we send */ + desc->src_addr_l = ctl->bd_phy_addr; + + desc->dst_addr_l = ch->dxe_wq; + desc->fr_len = sizeof(struct wcn36xx_tx_bd); + desc->ctrl = ch->ctrl_bd; + + wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n"); + + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ", + (char *)desc, sizeof(*desc)); + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, + "BD >>> ", (char *)ctl->bd_cpu_addr, + sizeof(struct wcn36xx_tx_bd)); + + /* Set source address of the SKB we send */ + ctl = ctl->next; + ctl->skb = skb; + desc = ctl->desc; + if (ctl->bd_cpu_addr) { + wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n"); + return -EINVAL; + } + + desc->src_addr_l = dma_map_single(NULL, + ctl->skb->data, + ctl->skb->len, + DMA_TO_DEVICE); + + desc->dst_addr_l = ch->dxe_wq; + desc->fr_len = ctl->skb->len; + + /* set dxe descriptor to VALID */ + desc->ctrl = ch->ctrl_skb; + + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ", + (char *)desc, sizeof(*desc)); + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ", + (char *)ctl->skb->data, ctl->skb->len); + + /* Move the head of the ring to the next empty descriptor */ + ch->head_blk_ctl = ctl->next; + + /* + * When connected and trying to send data frame chip can be in sleep + * mode and writing to the register will not wake up the chip. Instead + * notify chip about new frame through SMSM bus. + */ + if (is_low && vif_priv->pw_state == WCN36XX_BMPS) { + wcn->ctrl_ops->smsm_change_state( + 0, + WCN36XX_SMSM_WLAN_TX_ENABLE); + } else { + /* indicate End Of Packet and generate interrupt on descriptor + * done. + */ + wcn36xx_dxe_write_register(wcn, + ch->reg_ctrl, ch->def_ctrl); + } + + return 0; +} + +int wcn36xx_dxe_init(struct wcn36xx *wcn) +{ + int reg_data = 0, ret; + + reg_data = WCN36XX_DXE_REG_RESET; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data); + + /* Setting interrupt path */ + reg_data = WCN36XX_DXE_CCU_INT; + wcn36xx_dxe_write_register_x(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data); + + /***************************************/ + /* Init descriptors for TX LOW channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch); + wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool); + + /* Write channel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L, + wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr); + + /* Program DMA destination addr for TX LOW */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_TX_L, + WCN36XX_DXE_WQ_TX_L); + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L); + + /***************************************/ + /* Init descriptors for TX HIGH channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch); + wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool); + + /* Write channel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H, + wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr); + + /* Program DMA destination addr for TX HIGH */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_TX_H, + WCN36XX_DXE_WQ_TX_H); + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H); + + /***************************************/ + /* Init descriptors for RX LOW channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch); + + /* For RX we need to preallocated buffers */ + wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch); + + /* Write channel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L, + wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr); + + /* Write DMA source address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_SRC_ADDR_RX_L, + WCN36XX_DXE_WQ_RX_L); + + /* Program preallocated destination address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_RX_L, + wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l); + + /* Enable default control registers */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_REG_CTL_RX_L, + WCN36XX_DXE_CH_DEFAULT_CTL_RX_L); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L); + + /***************************************/ + /* Init descriptors for RX HIGH channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch); + + /* For RX we need to prealocat buffers */ + wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch); + + /* Write chanel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H, + wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr); + + /* Write DMA source address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_SRC_ADDR_RX_H, + WCN36XX_DXE_WQ_RX_H); + + /* Program preallocated destination address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_RX_H, + wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l); + + /* Enable default control registers */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_REG_CTL_RX_H, + WCN36XX_DXE_CH_DEFAULT_CTL_RX_H); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H); + + ret = wcn36xx_dxe_request_irqs(wcn); + if (ret < 0) + goto out_err; + + return 0; + +out_err: + return ret; +} + +void wcn36xx_dxe_deinit(struct wcn36xx *wcn) +{ + free_irq(wcn->tx_irq, wcn); + free_irq(wcn->rx_irq, wcn); + + if (wcn->tx_ack_skb) { + ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); + wcn->tx_ack_skb = NULL; + } + + wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch); + wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch); +} diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h new file mode 100644 index 00000000000..35ee7e966bd --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/dxe.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _DXE_H_ +#define _DXE_H_ + +#include "wcn36xx.h" + +/* +TX_LOW = DMA0 +TX_HIGH = DMA4 +RX_LOW = DMA1 +RX_HIGH = DMA3 +H2H_TEST_RX_TX = DMA2 +*/ + +/* DXE registers */ +#define WCN36XX_DXE_MEM_REG 0x202000 + +#define WCN36XX_DXE_CCU_INT 0xA0011 +#define WCN36XX_DXE_REG_CCU_INT_3660 0x200b10 +#define WCN36XX_DXE_REG_CCU_INT_3680 0x2050dc + +/* TODO This must calculated properly but not hardcoded */ +#define WCN36XX_DXE_CTRL_TX_L 0x328a44 +#define WCN36XX_DXE_CTRL_TX_H 0x32ce44 +#define WCN36XX_DXE_CTRL_RX_L 0x12ad2f +#define WCN36XX_DXE_CTRL_RX_H 0x12d12f +#define WCN36XX_DXE_CTRL_TX_H_BD 0x30ce45 +#define WCN36XX_DXE_CTRL_TX_H_SKB 0x32ce4d +#define WCN36XX_DXE_CTRL_TX_L_BD 0x308a45 +#define WCN36XX_DXE_CTRL_TX_L_SKB 0x328a4d + +/* TODO This must calculated properly but not hardcoded */ +#define WCN36XX_DXE_WQ_TX_L 0x17 +#define WCN36XX_DXE_WQ_TX_H 0x17 +#define WCN36XX_DXE_WQ_RX_L 0xB +#define WCN36XX_DXE_WQ_RX_H 0x4 + +/* DXE descriptor control filed */ +#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001) + +/* TODO This must calculated properly but not hardcoded */ +/* DXE default control register values */ +#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L 0x847EAD2F +#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H 0x84FED12F +#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H 0x853ECF4D +#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L 0x843e8b4d + +/* Common DXE registers */ +#define WCN36XX_DXE_MEM_CSR (WCN36XX_DXE_MEM_REG + 0x00) +#define WCN36XX_DXE_REG_CSR_RESET (WCN36XX_DXE_MEM_REG + 0x00) +#define WCN36XX_DXE_ENCH_ADDR (WCN36XX_DXE_MEM_REG + 0x04) +#define WCN36XX_DXE_REG_CH_EN (WCN36XX_DXE_MEM_REG + 0x08) +#define WCN36XX_DXE_REG_CH_DONE (WCN36XX_DXE_MEM_REG + 0x0C) +#define WCN36XX_DXE_REG_CH_ERR (WCN36XX_DXE_MEM_REG + 0x10) +#define WCN36XX_DXE_INT_MASK_REG (WCN36XX_DXE_MEM_REG + 0x18) +#define WCN36XX_DXE_INT_SRC_RAW_REG (WCN36XX_DXE_MEM_REG + 0x20) + /* #define WCN36XX_DXE_INT_CH6_MASK 0x00000040 */ + /* #define WCN36XX_DXE_INT_CH5_MASK 0x00000020 */ + #define WCN36XX_DXE_INT_CH4_MASK 0x00000010 + #define WCN36XX_DXE_INT_CH3_MASK 0x00000008 + /* #define WCN36XX_DXE_INT_CH2_MASK 0x00000004 */ + #define WCN36XX_DXE_INT_CH1_MASK 0x00000002 + #define WCN36XX_DXE_INT_CH0_MASK 0x00000001 +#define WCN36XX_DXE_0_INT_CLR (WCN36XX_DXE_MEM_REG + 0x30) +#define WCN36XX_DXE_0_INT_ED_CLR (WCN36XX_DXE_MEM_REG + 0x34) +#define WCN36XX_DXE_0_INT_DONE_CLR (WCN36XX_DXE_MEM_REG + 0x38) +#define WCN36XX_DXE_0_INT_ERR_CLR (WCN36XX_DXE_MEM_REG + 0x3C) + +#define WCN36XX_DXE_0_CH0_STATUS (WCN36XX_DXE_MEM_REG + 0x404) +#define WCN36XX_DXE_0_CH1_STATUS (WCN36XX_DXE_MEM_REG + 0x444) +#define WCN36XX_DXE_0_CH2_STATUS (WCN36XX_DXE_MEM_REG + 0x484) +#define WCN36XX_DXE_0_CH3_STATUS (WCN36XX_DXE_MEM_REG + 0x4C4) +#define WCN36XX_DXE_0_CH4_STATUS (WCN36XX_DXE_MEM_REG + 0x504) + +#define WCN36XX_DXE_REG_RESET 0x5c89 + +/* Temporary BMU Workqueue 4 */ +#define WCN36XX_DXE_BMU_WQ_RX_LOW 0xB +#define WCN36XX_DXE_BMU_WQ_RX_HIGH 0x4 +/* DMA channel offset */ +#define WCN36XX_DXE_TX_LOW_OFFSET 0x400 +#define WCN36XX_DXE_TX_HIGH_OFFSET 0x500 +#define WCN36XX_DXE_RX_LOW_OFFSET 0x440 +#define WCN36XX_DXE_RX_HIGH_OFFSET 0x4C0 + +/* Address of the next DXE descriptor */ +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR 0x001C +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) + +/* DXE Descriptor source address */ +#define WCN36XX_DXE_CH_SRC_ADDR 0x000C +#define WCN36XX_DXE_CH_SRC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_SRC_ADDR) +#define WCN36XX_DXE_CH_SRC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_SRC_ADDR) + +/* DXE Descriptor address destination address */ +#define WCN36XX_DXE_CH_DEST_ADDR 0x0014 +#define WCN36XX_DXE_CH_DEST_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) +#define WCN36XX_DXE_CH_DEST_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) +#define WCN36XX_DXE_CH_DEST_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) +#define WCN36XX_DXE_CH_DEST_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) + +/* Interrupt status */ +#define WCN36XX_DXE_CH_STATUS_REG_ADDR 0x0004 +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) + + +/* DXE default control register */ +#define WCN36XX_DXE_REG_CTL_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET) +#define WCN36XX_DXE_REG_CTL_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET) +#define WCN36XX_DXE_REG_CTL_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET) +#define WCN36XX_DXE_REG_CTL_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET) + +#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400 +#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200 + + +/* Interrupt control channel mask */ +#define WCN36XX_INT_MASK_CHAN_TX_L 0x00000001 +#define WCN36XX_INT_MASK_CHAN_RX_L 0x00000002 +#define WCN36XX_INT_MASK_CHAN_RX_H 0x00000008 +#define WCN36XX_INT_MASK_CHAN_TX_H 0x00000010 + +#define WCN36XX_BD_CHUNK_SIZE 128 + +#define WCN36XX_PKT_SIZE 0xF20 +enum wcn36xx_dxe_ch_type { + WCN36XX_DXE_CH_TX_L, + WCN36XX_DXE_CH_TX_H, + WCN36XX_DXE_CH_RX_L, + WCN36XX_DXE_CH_RX_H +}; + +/* amount of descriptors per channel */ +enum wcn36xx_dxe_ch_desc_num { + WCN36XX_DXE_CH_DESC_NUMB_TX_L = 128, + WCN36XX_DXE_CH_DESC_NUMB_TX_H = 10, + WCN36XX_DXE_CH_DESC_NUMB_RX_L = 512, + WCN36XX_DXE_CH_DESC_NUMB_RX_H = 40 +}; + +/** + * struct wcn36xx_dxe_desc - describes descriptor of one DXE buffer + * + * @ctrl: is a union that consists of following bits: + * union { + * u32 valid :1; //0 = DMA stop, 1 = DMA continue with this + * //descriptor + * u32 transfer_type :2; //0 = Host to Host space + * u32 eop :1; //End of Packet + * u32 bd_handling :1; //if transferType = Host to BMU, then 0 + * // means first 128 bytes contain BD, and 1 + * // means create new empty BD + * u32 siq :1; // SIQ + * u32 diq :1; // DIQ + * u32 pdu_rel :1; //0 = don't release BD and PDUs when done, + * // 1 = release them + * u32 bthld_sel :4; //BMU Threshold Select + * u32 prio :3; //Specifies the priority level to use for + * // the transfer + * u32 stop_channel :1; //1 = DMA stops processing further, channel + * //requires re-enabling after this + * u32 intr :1; //Interrupt on Descriptor Done + * u32 rsvd :1; //reserved + * u32 size :14;//14 bits used - ignored for BMU transfers, + * //only used for host to host transfers? + * } ctrl; + */ +struct wcn36xx_dxe_desc { + u32 ctrl; + u32 fr_len; + + u32 src_addr_l; + u32 dst_addr_l; + u32 phy_next_l; + u32 src_addr_h; + u32 dst_addr_h; + u32 phy_next_h; +} __packed; + +/* DXE Control block */ +struct wcn36xx_dxe_ctl { + struct wcn36xx_dxe_ctl *next; + struct wcn36xx_dxe_desc *desc; + unsigned int desc_phy_addr; + int ctl_blk_order; + struct sk_buff *skb; + spinlock_t skb_lock; + void *bd_cpu_addr; + dma_addr_t bd_phy_addr; +}; + +struct wcn36xx_dxe_ch { + enum wcn36xx_dxe_ch_type ch_type; + void *cpu_addr; + dma_addr_t dma_addr; + enum wcn36xx_dxe_ch_desc_num desc_num; + /* DXE control block ring */ + struct wcn36xx_dxe_ctl *head_blk_ctl; + struct wcn36xx_dxe_ctl *tail_blk_ctl; + + /* DXE channel specific configs */ + u32 dxe_wq; + u32 ctrl_bd; + u32 ctrl_skb; + u32 reg_ctrl; + u32 def_ctrl; +}; + +/* Memory Pool for BD headers */ +struct wcn36xx_dxe_mem_pool { + int chunk_size; + void *virt_addr; + dma_addr_t phy_addr; +}; + +struct wcn36xx_vif; +int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn); +void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn); +void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn); +int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn); +void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn); +int wcn36xx_dxe_init(struct wcn36xx *wcn); +void wcn36xx_dxe_deinit(struct wcn36xx *wcn); +int wcn36xx_dxe_init_channels(struct wcn36xx *wcn); +int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, + struct wcn36xx_vif *vif_priv, + struct sk_buff *skb, + bool is_low); +void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status); +void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low); +#endif /* _DXE_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h new file mode 100644 index 00000000000..a1f1127d780 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/hal.h @@ -0,0 +1,4659 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _HAL_H_ +#define _HAL_H_ + +/*--------------------------------------------------------------------------- + API VERSIONING INFORMATION + + The RIVA API is versioned as MAJOR.MINOR.VERSION.REVISION + The MAJOR is incremented for major product/architecture changes + (and then MINOR/VERSION/REVISION are zeroed) + The MINOR is incremented for minor product/architecture changes + (and then VERSION/REVISION are zeroed) + The VERSION is incremented if a significant API change occurs + (and then REVISION is zeroed) + The REVISION is incremented if an insignificant API change occurs + or if a new API is added + All values are in the range 0..255 (ie they are 8-bit values) + ---------------------------------------------------------------------------*/ +#define WCN36XX_HAL_VER_MAJOR 1 +#define WCN36XX_HAL_VER_MINOR 4 +#define WCN36XX_HAL_VER_VERSION 1 +#define WCN36XX_HAL_VER_REVISION 2 + +/* This is to force compiler to use the maximum of an int ( 4 bytes ) */ +#define WCN36XX_HAL_MAX_ENUM_SIZE 0x7FFFFFFF +#define WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE 0x7FFF + +/* Max no. of transmit categories */ +#define STACFG_MAX_TC 8 + +/* The maximum value of access category */ +#define WCN36XX_HAL_MAX_AC 4 + +#define WCN36XX_HAL_IPV4_ADDR_LEN 4 + +#define WALN_HAL_STA_INVALID_IDX 0xFF +#define WCN36XX_HAL_BSS_INVALID_IDX 0xFF + +/* Default Beacon template size */ +#define BEACON_TEMPLATE_SIZE 0x180 + +/* Param Change Bitmap sent to HAL */ +#define PARAM_BCN_INTERVAL_CHANGED (1 << 0) +#define PARAM_SHORT_PREAMBLE_CHANGED (1 << 1) +#define PARAM_SHORT_SLOT_TIME_CHANGED (1 << 2) +#define PARAM_llACOEXIST_CHANGED (1 << 3) +#define PARAM_llBCOEXIST_CHANGED (1 << 4) +#define PARAM_llGCOEXIST_CHANGED (1 << 5) +#define PARAM_HT20MHZCOEXIST_CHANGED (1<<6) +#define PARAM_NON_GF_DEVICES_PRESENT_CHANGED (1<<7) +#define PARAM_RIFS_MODE_CHANGED (1<<8) +#define PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED (1<<9) +#define PARAM_OBSS_MODE_CHANGED (1<<10) +#define PARAM_BEACON_UPDATE_MASK \ + (PARAM_BCN_INTERVAL_CHANGED | \ + PARAM_SHORT_PREAMBLE_CHANGED | \ + PARAM_SHORT_SLOT_TIME_CHANGED | \ + PARAM_llACOEXIST_CHANGED | \ + PARAM_llBCOEXIST_CHANGED | \ + PARAM_llGCOEXIST_CHANGED | \ + PARAM_HT20MHZCOEXIST_CHANGED | \ + PARAM_NON_GF_DEVICES_PRESENT_CHANGED | \ + PARAM_RIFS_MODE_CHANGED | \ + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED | \ + PARAM_OBSS_MODE_CHANGED) + +/* dump command response Buffer size */ +#define DUMPCMD_RSP_BUFFER 100 + +/* version string max length (including NULL) */ +#define WCN36XX_HAL_VERSION_LENGTH 64 + +/* message types for messages exchanged between WDI and HAL */ +enum wcn36xx_hal_host_msg_type { + /* Init/De-Init */ + WCN36XX_HAL_START_REQ = 0, + WCN36XX_HAL_START_RSP = 1, + WCN36XX_HAL_STOP_REQ = 2, + WCN36XX_HAL_STOP_RSP = 3, + + /* Scan */ + WCN36XX_HAL_INIT_SCAN_REQ = 4, + WCN36XX_HAL_INIT_SCAN_RSP = 5, + WCN36XX_HAL_START_SCAN_REQ = 6, + WCN36XX_HAL_START_SCAN_RSP = 7, + WCN36XX_HAL_END_SCAN_REQ = 8, + WCN36XX_HAL_END_SCAN_RSP = 9, + WCN36XX_HAL_FINISH_SCAN_REQ = 10, + WCN36XX_HAL_FINISH_SCAN_RSP = 11, + + /* HW STA configuration/deconfiguration */ + WCN36XX_HAL_CONFIG_STA_REQ = 12, + WCN36XX_HAL_CONFIG_STA_RSP = 13, + WCN36XX_HAL_DELETE_STA_REQ = 14, + WCN36XX_HAL_DELETE_STA_RSP = 15, + WCN36XX_HAL_CONFIG_BSS_REQ = 16, + WCN36XX_HAL_CONFIG_BSS_RSP = 17, + WCN36XX_HAL_DELETE_BSS_REQ = 18, + WCN36XX_HAL_DELETE_BSS_RSP = 19, + + /* Infra STA asscoiation */ + WCN36XX_HAL_JOIN_REQ = 20, + WCN36XX_HAL_JOIN_RSP = 21, + WCN36XX_HAL_POST_ASSOC_REQ = 22, + WCN36XX_HAL_POST_ASSOC_RSP = 23, + + /* Security */ + WCN36XX_HAL_SET_BSSKEY_REQ = 24, + WCN36XX_HAL_SET_BSSKEY_RSP = 25, + WCN36XX_HAL_SET_STAKEY_REQ = 26, + WCN36XX_HAL_SET_STAKEY_RSP = 27, + WCN36XX_HAL_RMV_BSSKEY_REQ = 28, + WCN36XX_HAL_RMV_BSSKEY_RSP = 29, + WCN36XX_HAL_RMV_STAKEY_REQ = 30, + WCN36XX_HAL_RMV_STAKEY_RSP = 31, + + /* Qos Related */ + WCN36XX_HAL_ADD_TS_REQ = 32, + WCN36XX_HAL_ADD_TS_RSP = 33, + WCN36XX_HAL_DEL_TS_REQ = 34, + WCN36XX_HAL_DEL_TS_RSP = 35, + WCN36XX_HAL_UPD_EDCA_PARAMS_REQ = 36, + WCN36XX_HAL_UPD_EDCA_PARAMS_RSP = 37, + WCN36XX_HAL_ADD_BA_REQ = 38, + WCN36XX_HAL_ADD_BA_RSP = 39, + WCN36XX_HAL_DEL_BA_REQ = 40, + WCN36XX_HAL_DEL_BA_RSP = 41, + + WCN36XX_HAL_CH_SWITCH_REQ = 42, + WCN36XX_HAL_CH_SWITCH_RSP = 43, + WCN36XX_HAL_SET_LINK_ST_REQ = 44, + WCN36XX_HAL_SET_LINK_ST_RSP = 45, + WCN36XX_HAL_GET_STATS_REQ = 46, + WCN36XX_HAL_GET_STATS_RSP = 47, + WCN36XX_HAL_UPDATE_CFG_REQ = 48, + WCN36XX_HAL_UPDATE_CFG_RSP = 49, + + WCN36XX_HAL_MISSED_BEACON_IND = 50, + WCN36XX_HAL_UNKNOWN_ADDR2_FRAME_RX_IND = 51, + WCN36XX_HAL_MIC_FAILURE_IND = 52, + WCN36XX_HAL_FATAL_ERROR_IND = 53, + WCN36XX_HAL_SET_KEYDONE_MSG = 54, + + /* NV Interface */ + WCN36XX_HAL_DOWNLOAD_NV_REQ = 55, + WCN36XX_HAL_DOWNLOAD_NV_RSP = 56, + + WCN36XX_HAL_ADD_BA_SESSION_REQ = 57, + WCN36XX_HAL_ADD_BA_SESSION_RSP = 58, + WCN36XX_HAL_TRIGGER_BA_REQ = 59, + WCN36XX_HAL_TRIGGER_BA_RSP = 60, + WCN36XX_HAL_UPDATE_BEACON_REQ = 61, + WCN36XX_HAL_UPDATE_BEACON_RSP = 62, + WCN36XX_HAL_SEND_BEACON_REQ = 63, + WCN36XX_HAL_SEND_BEACON_RSP = 64, + + WCN36XX_HAL_SET_BCASTKEY_REQ = 65, + WCN36XX_HAL_SET_BCASTKEY_RSP = 66, + WCN36XX_HAL_DELETE_STA_CONTEXT_IND = 67, + WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_REQ = 68, + WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP = 69, + + /* PTT interface support */ + WCN36XX_HAL_PROCESS_PTT_REQ = 70, + WCN36XX_HAL_PROCESS_PTT_RSP = 71, + + /* BTAMP related events */ + WCN36XX_HAL_SIGNAL_BTAMP_EVENT_REQ = 72, + WCN36XX_HAL_SIGNAL_BTAMP_EVENT_RSP = 73, + WCN36XX_HAL_TL_HAL_FLUSH_AC_REQ = 74, + WCN36XX_HAL_TL_HAL_FLUSH_AC_RSP = 75, + + WCN36XX_HAL_ENTER_IMPS_REQ = 76, + WCN36XX_HAL_EXIT_IMPS_REQ = 77, + WCN36XX_HAL_ENTER_BMPS_REQ = 78, + WCN36XX_HAL_EXIT_BMPS_REQ = 79, + WCN36XX_HAL_ENTER_UAPSD_REQ = 80, + WCN36XX_HAL_EXIT_UAPSD_REQ = 81, + WCN36XX_HAL_UPDATE_UAPSD_PARAM_REQ = 82, + WCN36XX_HAL_CONFIGURE_RXP_FILTER_REQ = 83, + WCN36XX_HAL_ADD_BCN_FILTER_REQ = 84, + WCN36XX_HAL_REM_BCN_FILTER_REQ = 85, + WCN36XX_HAL_ADD_WOWL_BCAST_PTRN = 86, + WCN36XX_HAL_DEL_WOWL_BCAST_PTRN = 87, + WCN36XX_HAL_ENTER_WOWL_REQ = 88, + WCN36XX_HAL_EXIT_WOWL_REQ = 89, + WCN36XX_HAL_HOST_OFFLOAD_REQ = 90, + WCN36XX_HAL_SET_RSSI_THRESH_REQ = 91, + WCN36XX_HAL_GET_RSSI_REQ = 92, + WCN36XX_HAL_SET_UAPSD_AC_PARAMS_REQ = 93, + WCN36XX_HAL_CONFIGURE_APPS_CPU_WAKEUP_STATE_REQ = 94, + + WCN36XX_HAL_ENTER_IMPS_RSP = 95, + WCN36XX_HAL_EXIT_IMPS_RSP = 96, + WCN36XX_HAL_ENTER_BMPS_RSP = 97, + WCN36XX_HAL_EXIT_BMPS_RSP = 98, + WCN36XX_HAL_ENTER_UAPSD_RSP = 99, + WCN36XX_HAL_EXIT_UAPSD_RSP = 100, + WCN36XX_HAL_SET_UAPSD_AC_PARAMS_RSP = 101, + WCN36XX_HAL_UPDATE_UAPSD_PARAM_RSP = 102, + WCN36XX_HAL_CONFIGURE_RXP_FILTER_RSP = 103, + WCN36XX_HAL_ADD_BCN_FILTER_RSP = 104, + WCN36XX_HAL_REM_BCN_FILTER_RSP = 105, + WCN36XX_HAL_SET_RSSI_THRESH_RSP = 106, + WCN36XX_HAL_HOST_OFFLOAD_RSP = 107, + WCN36XX_HAL_ADD_WOWL_BCAST_PTRN_RSP = 108, + WCN36XX_HAL_DEL_WOWL_BCAST_PTRN_RSP = 109, + WCN36XX_HAL_ENTER_WOWL_RSP = 110, + WCN36XX_HAL_EXIT_WOWL_RSP = 111, + WCN36XX_HAL_RSSI_NOTIFICATION_IND = 112, + WCN36XX_HAL_GET_RSSI_RSP = 113, + WCN36XX_HAL_CONFIGURE_APPS_CPU_WAKEUP_STATE_RSP = 114, + + /* 11k related events */ + WCN36XX_HAL_SET_MAX_TX_POWER_REQ = 115, + WCN36XX_HAL_SET_MAX_TX_POWER_RSP = 116, + + /* 11R related msgs */ + WCN36XX_HAL_AGGR_ADD_TS_REQ = 117, + WCN36XX_HAL_AGGR_ADD_TS_RSP = 118, + + /* P2P WLAN_FEATURE_P2P */ + WCN36XX_HAL_SET_P2P_GONOA_REQ = 119, + WCN36XX_HAL_SET_P2P_GONOA_RSP = 120, + + /* WLAN Dump commands */ + WCN36XX_HAL_DUMP_COMMAND_REQ = 121, + WCN36XX_HAL_DUMP_COMMAND_RSP = 122, + + /* OEM_DATA FEATURE SUPPORT */ + WCN36XX_HAL_START_OEM_DATA_REQ = 123, + WCN36XX_HAL_START_OEM_DATA_RSP = 124, + + /* ADD SELF STA REQ and RSP */ + WCN36XX_HAL_ADD_STA_SELF_REQ = 125, + WCN36XX_HAL_ADD_STA_SELF_RSP = 126, + + /* DEL SELF STA SUPPORT */ + WCN36XX_HAL_DEL_STA_SELF_REQ = 127, + WCN36XX_HAL_DEL_STA_SELF_RSP = 128, + + /* Coex Indication */ + WCN36XX_HAL_COEX_IND = 129, + + /* Tx Complete Indication */ + WCN36XX_HAL_OTA_TX_COMPL_IND = 130, + + /* Host Suspend/resume messages */ + WCN36XX_HAL_HOST_SUSPEND_IND = 131, + WCN36XX_HAL_HOST_RESUME_REQ = 132, + WCN36XX_HAL_HOST_RESUME_RSP = 133, + + WCN36XX_HAL_SET_TX_POWER_REQ = 134, + WCN36XX_HAL_SET_TX_POWER_RSP = 135, + WCN36XX_HAL_GET_TX_POWER_REQ = 136, + WCN36XX_HAL_GET_TX_POWER_RSP = 137, + + WCN36XX_HAL_P2P_NOA_ATTR_IND = 138, + + WCN36XX_HAL_ENABLE_RADAR_DETECT_REQ = 139, + WCN36XX_HAL_ENABLE_RADAR_DETECT_RSP = 140, + WCN36XX_HAL_GET_TPC_REPORT_REQ = 141, + WCN36XX_HAL_GET_TPC_REPORT_RSP = 142, + WCN36XX_HAL_RADAR_DETECT_IND = 143, + WCN36XX_HAL_RADAR_DETECT_INTR_IND = 144, + WCN36XX_HAL_KEEP_ALIVE_REQ = 145, + WCN36XX_HAL_KEEP_ALIVE_RSP = 146, + + /* PNO messages */ + WCN36XX_HAL_SET_PREF_NETWORK_REQ = 147, + WCN36XX_HAL_SET_PREF_NETWORK_RSP = 148, + WCN36XX_HAL_SET_RSSI_FILTER_REQ = 149, + WCN36XX_HAL_SET_RSSI_FILTER_RSP = 150, + WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ = 151, + WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP = 152, + WCN36XX_HAL_PREF_NETW_FOUND_IND = 153, + + WCN36XX_HAL_SET_TX_PER_TRACKING_REQ = 154, + WCN36XX_HAL_SET_TX_PER_TRACKING_RSP = 155, + WCN36XX_HAL_TX_PER_HIT_IND = 156, + + WCN36XX_HAL_8023_MULTICAST_LIST_REQ = 157, + WCN36XX_HAL_8023_MULTICAST_LIST_RSP = 158, + + WCN36XX_HAL_SET_PACKET_FILTER_REQ = 159, + WCN36XX_HAL_SET_PACKET_FILTER_RSP = 160, + WCN36XX_HAL_PACKET_FILTER_MATCH_COUNT_REQ = 161, + WCN36XX_HAL_PACKET_FILTER_MATCH_COUNT_RSP = 162, + WCN36XX_HAL_CLEAR_PACKET_FILTER_REQ = 163, + WCN36XX_HAL_CLEAR_PACKET_FILTER_RSP = 164, + + /* + * This is temp fix. Should be removed once Host and Riva code is + * in sync. + */ + WCN36XX_HAL_INIT_SCAN_CON_REQ = 165, + + WCN36XX_HAL_SET_POWER_PARAMS_REQ = 166, + WCN36XX_HAL_SET_POWER_PARAMS_RSP = 167, + + WCN36XX_HAL_TSM_STATS_REQ = 168, + WCN36XX_HAL_TSM_STATS_RSP = 169, + + /* wake reason indication (WOW) */ + WCN36XX_HAL_WAKE_REASON_IND = 170, + + /* GTK offload support */ + WCN36XX_HAL_GTK_OFFLOAD_REQ = 171, + WCN36XX_HAL_GTK_OFFLOAD_RSP = 172, + WCN36XX_HAL_GTK_OFFLOAD_GETINFO_REQ = 173, + WCN36XX_HAL_GTK_OFFLOAD_GETINFO_RSP = 174, + + WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ = 175, + WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP = 176, + WCN36XX_HAL_EXCLUDE_UNENCRYPTED_IND = 177, + + WCN36XX_HAL_SET_THERMAL_MITIGATION_REQ = 178, + WCN36XX_HAL_SET_THERMAL_MITIGATION_RSP = 179, + + WCN36XX_HAL_UPDATE_VHT_OP_MODE_REQ = 182, + WCN36XX_HAL_UPDATE_VHT_OP_MODE_RSP = 183, + + WCN36XX_HAL_P2P_NOA_START_IND = 184, + + WCN36XX_HAL_GET_ROAM_RSSI_REQ = 185, + WCN36XX_HAL_GET_ROAM_RSSI_RSP = 186, + + WCN36XX_HAL_CLASS_B_STATS_IND = 187, + WCN36XX_HAL_DEL_BA_IND = 188, + WCN36XX_HAL_DHCP_START_IND = 189, + WCN36XX_HAL_DHCP_STOP_IND = 190, + + WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE +}; + +/* Enumeration for Version */ +enum wcn36xx_hal_host_msg_version { + WCN36XX_HAL_MSG_VERSION0 = 0, + WCN36XX_HAL_MSG_VERSION1 = 1, + /* define as 2 bytes data */ + WCN36XX_HAL_MSG_WCNSS_CTRL_VERSION = 0x7FFF, + WCN36XX_HAL_MSG_VERSION_MAX_FIELD = WCN36XX_HAL_MSG_WCNSS_CTRL_VERSION +}; + +enum driver_type { + DRIVER_TYPE_PRODUCTION = 0, + DRIVER_TYPE_MFG = 1, + DRIVER_TYPE_DVT = 2, + DRIVER_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_stop_type { + HAL_STOP_TYPE_SYS_RESET, + HAL_STOP_TYPE_SYS_DEEP_SLEEP, + HAL_STOP_TYPE_RF_KILL, + HAL_STOP_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_sys_mode { + HAL_SYS_MODE_NORMAL, + HAL_SYS_MODE_LEARN, + HAL_SYS_MODE_SCAN, + HAL_SYS_MODE_PROMISC, + HAL_SYS_MODE_SUSPEND_LINK, + HAL_SYS_MODE_ROAM_SCAN, + HAL_SYS_MODE_ROAM_SUSPEND_LINK, + HAL_SYS_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum phy_chan_bond_state { + /* 20MHz IF bandwidth centered on IF carrier */ + PHY_SINGLE_CHANNEL_CENTERED = 0, + + /* 40MHz IF bandwidth with lower 20MHz supporting the primary channel */ + PHY_DOUBLE_CHANNEL_LOW_PRIMARY = 1, + + /* 40MHz IF bandwidth centered on IF carrier */ + PHY_DOUBLE_CHANNEL_CENTERED = 2, + + /* 40MHz IF bandwidth with higher 20MHz supporting the primary ch */ + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY = 3, + + /* 20/40MHZ offset LOW 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED = 4, + + /* 20/40MHZ offset CENTERED 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED = 5, + + /* 20/40MHZ offset HIGH 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED = 6, + + /* 20/40MHZ offset LOW 40/80MHZ offset LOW */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW = 7, + + /* 20/40MHZ offset HIGH 40/80MHZ offset LOW */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW = 8, + + /* 20/40MHZ offset LOW 40/80MHZ offset HIGH */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH = 9, + + /* 20/40MHZ offset-HIGH 40/80MHZ offset HIGH */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH = 10, + + PHY_CHANNEL_BONDING_STATE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Spatial Multiplexing(SM) Power Save mode */ +enum wcn36xx_hal_ht_mimo_state { + /* Static SM Power Save mode */ + WCN36XX_HAL_HT_MIMO_PS_STATIC = 0, + + /* Dynamic SM Power Save mode */ + WCN36XX_HAL_HT_MIMO_PS_DYNAMIC = 1, + + /* reserved */ + WCN36XX_HAL_HT_MIMO_PS_NA = 2, + + /* SM Power Save disabled */ + WCN36XX_HAL_HT_MIMO_PS_NO_LIMIT = 3, + + WCN36XX_HAL_HT_MIMO_PS_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* each station added has a rate mode which specifies the sta attributes */ +enum sta_rate_mode { + STA_TAURUS = 0, + STA_TITAN, + STA_POLARIS, + STA_11b, + STA_11bg, + STA_11a, + STA_11n, + STA_11ac, + STA_INVALID_RATE_MODE = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* 1,2,5.5,11 */ +#define WCN36XX_HAL_NUM_DSSS_RATES 4 + +/* 6,9,12,18,24,36,48,54 */ +#define WCN36XX_HAL_NUM_OFDM_RATES 8 + +/* 72,96,108 */ +#define WCN36XX_HAL_NUM_POLARIS_RATES 3 + +#define WCN36XX_HAL_MAC_MAX_SUPPORTED_MCS_SET 16 + +enum wcn36xx_hal_bss_type { + WCN36XX_HAL_INFRASTRUCTURE_MODE, + + /* Added for softAP support */ + WCN36XX_HAL_INFRA_AP_MODE, + + WCN36XX_HAL_IBSS_MODE, + + /* Added for BT-AMP support */ + WCN36XX_HAL_BTAMP_STA_MODE, + + /* Added for BT-AMP support */ + WCN36XX_HAL_BTAMP_AP_MODE, + + WCN36XX_HAL_AUTO_MODE, + + WCN36XX_HAL_DONOT_USE_BSS_TYPE = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_nw_type { + WCN36XX_HAL_11A_NW_TYPE, + WCN36XX_HAL_11B_NW_TYPE, + WCN36XX_HAL_11G_NW_TYPE, + WCN36XX_HAL_11N_NW_TYPE, + WCN36XX_HAL_DONOT_USE_NW_TYPE = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +#define WCN36XX_HAL_MAC_RATESET_EID_MAX 12 + +enum wcn36xx_hal_ht_operating_mode { + /* No Protection */ + WCN36XX_HAL_HT_OP_MODE_PURE, + + /* Overlap Legacy device present, protection is optional */ + WCN36XX_HAL_HT_OP_MODE_OVERLAP_LEGACY, + + /* No legacy device, but 20 MHz HT present */ + WCN36XX_HAL_HT_OP_MODE_NO_LEGACY_20MHZ_HT, + + /* Protection is required */ + WCN36XX_HAL_HT_OP_MODE_MIXED, + + WCN36XX_HAL_HT_OP_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Encryption type enum used with peer */ +enum ani_ed_type { + WCN36XX_HAL_ED_NONE, + WCN36XX_HAL_ED_WEP40, + WCN36XX_HAL_ED_WEP104, + WCN36XX_HAL_ED_TKIP, + WCN36XX_HAL_ED_CCMP, + WCN36XX_HAL_ED_WPI, + WCN36XX_HAL_ED_AES_128_CMAC, + WCN36XX_HAL_ED_NOT_IMPLEMENTED = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +#define WLAN_MAX_KEY_RSC_LEN 16 +#define WLAN_WAPI_KEY_RSC_LEN 16 + +/* MAX key length when ULA is used */ +#define WCN36XX_HAL_MAC_MAX_KEY_LENGTH 32 +#define WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS 4 + +/* + * Enum to specify whether key is used for TX only, RX only or both. + */ +enum ani_key_direction { + WCN36XX_HAL_TX_ONLY, + WCN36XX_HAL_RX_ONLY, + WCN36XX_HAL_TX_RX, + WCN36XX_HAL_TX_DEFAULT, + WCN36XX_HAL_DONOT_USE_KEY_DIRECTION = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum ani_wep_type { + WCN36XX_HAL_WEP_STATIC, + WCN36XX_HAL_WEP_DYNAMIC, + WCN36XX_HAL_WEP_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_link_state { + + WCN36XX_HAL_LINK_IDLE_STATE = 0, + WCN36XX_HAL_LINK_PREASSOC_STATE = 1, + WCN36XX_HAL_LINK_POSTASSOC_STATE = 2, + WCN36XX_HAL_LINK_AP_STATE = 3, + WCN36XX_HAL_LINK_IBSS_STATE = 4, + + /* BT-AMP Case */ + WCN36XX_HAL_LINK_BTAMP_PREASSOC_STATE = 5, + WCN36XX_HAL_LINK_BTAMP_POSTASSOC_STATE = 6, + WCN36XX_HAL_LINK_BTAMP_AP_STATE = 7, + WCN36XX_HAL_LINK_BTAMP_STA_STATE = 8, + + /* Reserved for HAL Internal Use */ + WCN36XX_HAL_LINK_LEARN_STATE = 9, + WCN36XX_HAL_LINK_SCAN_STATE = 10, + WCN36XX_HAL_LINK_FINISH_SCAN_STATE = 11, + WCN36XX_HAL_LINK_INIT_CAL_STATE = 12, + WCN36XX_HAL_LINK_FINISH_CAL_STATE = 13, + WCN36XX_HAL_LINK_LISTEN_STATE = 14, + + WCN36XX_HAL_LINK_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_stats_mask { + HAL_SUMMARY_STATS_INFO = 0x00000001, + HAL_GLOBAL_CLASS_A_STATS_INFO = 0x00000002, + HAL_GLOBAL_CLASS_B_STATS_INFO = 0x00000004, + HAL_GLOBAL_CLASS_C_STATS_INFO = 0x00000008, + HAL_GLOBAL_CLASS_D_STATS_INFO = 0x00000010, + HAL_PER_STA_STATS_INFO = 0x00000020 +}; + +/* BT-AMP events type */ +enum bt_amp_event_type { + BTAMP_EVENT_CONNECTION_START, + BTAMP_EVENT_CONNECTION_STOP, + BTAMP_EVENT_CONNECTION_TERMINATED, + + /* This and beyond are invalid values */ + BTAMP_EVENT_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE, +}; + +/* PE Statistics */ +enum pe_stats_mask { + PE_SUMMARY_STATS_INFO = 0x00000001, + PE_GLOBAL_CLASS_A_STATS_INFO = 0x00000002, + PE_GLOBAL_CLASS_B_STATS_INFO = 0x00000004, + PE_GLOBAL_CLASS_C_STATS_INFO = 0x00000008, + PE_GLOBAL_CLASS_D_STATS_INFO = 0x00000010, + PE_PER_STA_STATS_INFO = 0x00000020, + + /* This and beyond are invalid values */ + PE_STATS_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* + * Configuration Parameter IDs + */ +#define WCN36XX_HAL_CFG_STA_ID 0 +#define WCN36XX_HAL_CFG_CURRENT_TX_ANTENNA 1 +#define WCN36XX_HAL_CFG_CURRENT_RX_ANTENNA 2 +#define WCN36XX_HAL_CFG_LOW_GAIN_OVERRIDE 3 +#define WCN36XX_HAL_CFG_POWER_STATE_PER_CHAIN 4 +#define WCN36XX_HAL_CFG_CAL_PERIOD 5 +#define WCN36XX_HAL_CFG_CAL_CONTROL 6 +#define WCN36XX_HAL_CFG_PROXIMITY 7 +#define WCN36XX_HAL_CFG_NETWORK_DENSITY 8 +#define WCN36XX_HAL_CFG_MAX_MEDIUM_TIME 9 +#define WCN36XX_HAL_CFG_MAX_MPDUS_IN_AMPDU 10 +#define WCN36XX_HAL_CFG_RTS_THRESHOLD 11 +#define WCN36XX_HAL_CFG_SHORT_RETRY_LIMIT 12 +#define WCN36XX_HAL_CFG_LONG_RETRY_LIMIT 13 +#define WCN36XX_HAL_CFG_FRAGMENTATION_THRESHOLD 14 +#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_ZERO 15 +#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_ONE 16 +#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_TWO 17 +#define WCN36XX_HAL_CFG_FIXED_RATE 18 +#define WCN36XX_HAL_CFG_RETRYRATE_POLICY 19 +#define WCN36XX_HAL_CFG_RETRYRATE_SECONDARY 20 +#define WCN36XX_HAL_CFG_RETRYRATE_TERTIARY 21 +#define WCN36XX_HAL_CFG_FORCE_POLICY_PROTECTION 22 +#define WCN36XX_HAL_CFG_FIXED_RATE_MULTICAST_24GHZ 23 +#define WCN36XX_HAL_CFG_FIXED_RATE_MULTICAST_5GHZ 24 +#define WCN36XX_HAL_CFG_DEFAULT_RATE_INDEX_24GHZ 25 +#define WCN36XX_HAL_CFG_DEFAULT_RATE_INDEX_5GHZ 26 +#define WCN36XX_HAL_CFG_MAX_BA_SESSIONS 27 +#define WCN36XX_HAL_CFG_PS_DATA_INACTIVITY_TIMEOUT 28 +#define WCN36XX_HAL_CFG_PS_ENABLE_BCN_FILTER 29 +#define WCN36XX_HAL_CFG_PS_ENABLE_RSSI_MONITOR 30 +#define WCN36XX_HAL_CFG_NUM_BEACON_PER_RSSI_AVERAGE 31 +#define WCN36XX_HAL_CFG_STATS_PERIOD 32 +#define WCN36XX_HAL_CFG_CFP_MAX_DURATION 33 +#define WCN36XX_HAL_CFG_FRAME_TRANS_ENABLED 34 +#define WCN36XX_HAL_CFG_DTIM_PERIOD 35 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACBK 36 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACBE 37 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACVO 38 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACVI 39 +#define WCN36XX_HAL_CFG_BA_THRESHOLD_HIGH 40 +#define WCN36XX_HAL_CFG_MAX_BA_BUFFERS 41 +#define WCN36XX_HAL_CFG_RPE_POLLING_THRESHOLD 42 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC0_REG 43 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC1_REG 44 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC2_REG 45 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC3_REG 46 +#define WCN36XX_HAL_CFG_NO_OF_ONCHIP_REORDER_SESSIONS 47 +#define WCN36XX_HAL_CFG_PS_LISTEN_INTERVAL 48 +#define WCN36XX_HAL_CFG_PS_HEART_BEAT_THRESHOLD 49 +#define WCN36XX_HAL_CFG_PS_NTH_BEACON_FILTER 50 +#define WCN36XX_HAL_CFG_PS_MAX_PS_POLL 51 +#define WCN36XX_HAL_CFG_PS_MIN_RSSI_THRESHOLD 52 +#define WCN36XX_HAL_CFG_PS_RSSI_FILTER_PERIOD 53 +#define WCN36XX_HAL_CFG_PS_BROADCAST_FRAME_FILTER_ENABLE 54 +#define WCN36XX_HAL_CFG_PS_IGNORE_DTIM 55 +#define WCN36XX_HAL_CFG_PS_ENABLE_BCN_EARLY_TERM 56 +#define WCN36XX_HAL_CFG_DYNAMIC_PS_POLL_VALUE 57 +#define WCN36XX_HAL_CFG_PS_NULLDATA_AP_RESP_TIMEOUT 58 +#define WCN36XX_HAL_CFG_TELE_BCN_WAKEUP_EN 59 +#define WCN36XX_HAL_CFG_TELE_BCN_TRANS_LI 60 +#define WCN36XX_HAL_CFG_TELE_BCN_TRANS_LI_IDLE_BCNS 61 +#define WCN36XX_HAL_CFG_TELE_BCN_MAX_LI 62 +#define WCN36XX_HAL_CFG_TELE_BCN_MAX_LI_IDLE_BCNS 63 +#define WCN36XX_HAL_CFG_TX_PWR_CTRL_ENABLE 64 +#define WCN36XX_HAL_CFG_VALID_RADAR_CHANNEL_LIST 65 +#define WCN36XX_HAL_CFG_TX_POWER_24_20 66 +#define WCN36XX_HAL_CFG_TX_POWER_24_40 67 +#define WCN36XX_HAL_CFG_TX_POWER_50_20 68 +#define WCN36XX_HAL_CFG_TX_POWER_50_40 69 +#define WCN36XX_HAL_CFG_MCAST_BCAST_FILTER_SETTING 70 +#define WCN36XX_HAL_CFG_BCN_EARLY_TERM_WAKEUP_INTERVAL 71 +#define WCN36XX_HAL_CFG_MAX_TX_POWER_2_4 72 +#define WCN36XX_HAL_CFG_MAX_TX_POWER_5 73 +#define WCN36XX_HAL_CFG_INFRA_STA_KEEP_ALIVE_PERIOD 74 +#define WCN36XX_HAL_CFG_ENABLE_CLOSE_LOOP 75 +#define WCN36XX_HAL_CFG_BTC_EXECUTION_MODE 76 +#define WCN36XX_HAL_CFG_BTC_DHCP_BT_SLOTS_TO_BLOCK 77 +#define WCN36XX_HAL_CFG_BTC_A2DP_DHCP_BT_SUB_INTERVALS 78 +#define WCN36XX_HAL_CFG_PS_TX_INACTIVITY_TIMEOUT 79 +#define WCN36XX_HAL_CFG_WCNSS_API_VERSION 80 +#define WCN36XX_HAL_CFG_AP_KEEPALIVE_TIMEOUT 81 +#define WCN36XX_HAL_CFG_GO_KEEPALIVE_TIMEOUT 82 +#define WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST 83 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_INQ_BT 84 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_PAGE_BT 85 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_CONN_BT 86 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_LE_BT 87 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_INQ_WLAN 88 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_PAGE_WLAN 89 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_CONN_WLAN 90 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_LE_WLAN 91 +#define WCN36XX_HAL_CFG_BTC_DYN_MAX_LEN_BT 92 +#define WCN36XX_HAL_CFG_BTC_DYN_MAX_LEN_WLAN 93 +#define WCN36XX_HAL_CFG_BTC_MAX_SCO_BLOCK_PERC 94 +#define WCN36XX_HAL_CFG_BTC_DHCP_PROT_ON_A2DP 95 +#define WCN36XX_HAL_CFG_BTC_DHCP_PROT_ON_SCO 96 +#define WCN36XX_HAL_CFG_ENABLE_UNICAST_FILTER 97 +#define WCN36XX_HAL_CFG_MAX_ASSOC_LIMIT 98 +#define WCN36XX_HAL_CFG_ENABLE_LPWR_IMG_TRANSITION 99 +#define WCN36XX_HAL_CFG_ENABLE_MCC_ADAPTIVE_SCHEDULER 100 +#define WCN36XX_HAL_CFG_ENABLE_DETECT_PS_SUPPORT 101 +#define WCN36XX_HAL_CFG_AP_LINK_MONITOR_TIMEOUT 102 +#define WCN36XX_HAL_CFG_BTC_DWELL_TIME_MULTIPLIER 103 +#define WCN36XX_HAL_CFG_ENABLE_TDLS_OXYGEN_MODE 104 +#define WCN36XX_HAL_CFG_MAX_PARAMS 105 + +/* Message definitons - All the messages below need to be packed */ + +/* Definition for HAL API Version. */ +struct wcnss_wlan_version { + u8 revision; + u8 version; + u8 minor; + u8 major; +} __packed; + +/* Definition for Encryption Keys */ +struct wcn36xx_hal_keys { + u8 id; + + /* 0 for multicast */ + u8 unicast; + + enum ani_key_direction direction; + + /* Usage is unknown */ + u8 rsc[WLAN_MAX_KEY_RSC_LEN]; + + /* =1 for authenticator,=0 for supplicant */ + u8 pae_role; + + u16 length; + u8 key[WCN36XX_HAL_MAC_MAX_KEY_LENGTH]; +} __packed; + +/* + * set_sta_key_params Moving here since it is shared by + * configbss/setstakey msgs + */ +struct wcn36xx_hal_set_sta_key_params { + /* STA Index */ + u16 sta_index; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* STATIC/DYNAMIC - valid only for WEP */ + enum ani_wep_type wep_type; + + /* Default WEP key, valid only for static WEP, must between 0 and 3. */ + u8 def_wep_idx; + + /* valid only for non-static WEP encyrptions */ + struct wcn36xx_hal_keys key[WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS]; + + /* + * Control for Replay Count, 1= Single TID based replay count on Tx + * 0 = Per TID based replay count on TX + */ + u8 single_tid_rc; + +} __packed; + +/* 4-byte control message header used by HAL*/ +struct wcn36xx_hal_msg_header { + enum wcn36xx_hal_host_msg_type msg_type:16; + enum wcn36xx_hal_host_msg_version msg_version:16; + u32 len; +} __packed; + +/* Config format required by HAL for each CFG item*/ +struct wcn36xx_hal_cfg { + /* Cfg Id. The Id required by HAL is exported by HAL + * in shared header file between UMAC and HAL.*/ + u16 id; + + /* Length of the Cfg. This parameter is used to go to next cfg + * in the TLV format.*/ + u16 len; + + /* Padding bytes for unaligned address's */ + u16 pad_bytes; + + /* Reserve bytes for making cfgVal to align address */ + u16 reserve; + + /* Following the uCfgLen field there should be a 'uCfgLen' bytes + * containing the uCfgValue ; u8 uCfgValue[uCfgLen] */ +} __packed; + +struct wcn36xx_hal_mac_start_parameters { + /* Drive Type - Production or FTM etc */ + enum driver_type type; + + /* Length of the config buffer */ + u32 len; + + /* Following this there is a TLV formatted buffer of length + * "len" bytes containing all config values. + * The TLV is expected to be formatted like this: + * 0 15 31 31+CFG_LEN-1 length-1 + * | CFG_ID | CFG_LEN | CFG_BODY | CFG_ID |......| + */ +} __packed; + +struct wcn36xx_hal_mac_start_req_msg { + /* config buffer must start in TLV format just here */ + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_mac_start_parameters params; +} __packed; + +struct wcn36xx_hal_mac_start_rsp_params { + /* success or failure */ + u16 status; + + /* Max number of STA supported by the device */ + u8 stations; + + /* Max number of BSS supported by the device */ + u8 bssids; + + /* API Version */ + struct wcnss_wlan_version version; + + /* CRM build information */ + u8 crm_version[WCN36XX_HAL_VERSION_LENGTH]; + + /* hardware/chipset/misc version information */ + u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH]; + +} __packed; + +struct wcn36xx_hal_mac_start_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_mac_start_rsp_params start_rsp_params; +} __packed; + +struct wcn36xx_hal_mac_stop_req_params { + /* The reason for which the device is being stopped */ + enum wcn36xx_hal_stop_type reason; + +} __packed; + +struct wcn36xx_hal_mac_stop_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_mac_stop_req_params stop_req_params; +} __packed; + +struct wcn36xx_hal_mac_stop_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct wcn36xx_hal_update_cfg_req_msg { + /* + * Note: The length specified in tHalUpdateCfgReqMsg messages should be + * header.msgLen = sizeof(tHalUpdateCfgReqMsg) + uConfigBufferLen + */ + struct wcn36xx_hal_msg_header header; + + /* Length of the config buffer. Allows UMAC to update multiple CFGs */ + u32 len; + + /* + * Following this there is a TLV formatted buffer of length + * "uConfigBufferLen" bytes containing all config values. + * The TLV is expected to be formatted like this: + * 0 15 31 31+CFG_LEN-1 length-1 + * | CFG_ID | CFG_LEN | CFG_BODY | CFG_ID |......| + */ + +} __packed; + +struct wcn36xx_hal_update_cfg_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +} __packed; + +/* Frame control field format (2 bytes) */ +struct wcn36xx_hal_mac_frame_ctl { + +#ifndef ANI_LITTLE_BIT_ENDIAN + + u8 subType:4; + u8 type:2; + u8 protVer:2; + + u8 order:1; + u8 wep:1; + u8 moreData:1; + u8 powerMgmt:1; + u8 retry:1; + u8 moreFrag:1; + u8 fromDS:1; + u8 toDS:1; + +#else + + u8 protVer:2; + u8 type:2; + u8 subType:4; + + u8 toDS:1; + u8 fromDS:1; + u8 moreFrag:1; + u8 retry:1; + u8 powerMgmt:1; + u8 moreData:1; + u8 wep:1; + u8 order:1; + +#endif + +}; + +/* Sequence control field */ +struct wcn36xx_hal_mac_seq_ctl { + u8 fragNum:4; + u8 seqNumLo:4; + u8 seqNumHi:8; +}; + +/* Management header format */ +struct wcn36xx_hal_mac_mgmt_hdr { + struct wcn36xx_hal_mac_frame_ctl fc; + u8 durationLo; + u8 durationHi; + u8 da[6]; + u8 sa[6]; + u8 bssId[6]; + struct wcn36xx_hal_mac_seq_ctl seqControl; +}; + +/* FIXME: pronto v1 apparently has 4 */ +#define WCN36XX_HAL_NUM_BSSID 2 + +/* Scan Entry to hold active BSS idx's */ +struct wcn36xx_hal_scan_entry { + u8 bss_index[WCN36XX_HAL_NUM_BSSID]; + u8 active_bss_count; +}; + +struct wcn36xx_hal_init_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* LEARN - AP Role + SCAN - STA Role */ + enum wcn36xx_hal_sys_mode mode; + + /* BSSID of the BSS */ + u8 bssid[ETH_ALEN]; + + /* Whether BSS needs to be notified */ + u8 notify; + + /* Kind of frame to be used for notifying the BSS (Data Null, QoS + * Null, or CTS to Self). Must always be a valid frame type. */ + u8 frame_type; + + /* UMAC has the option of passing the MAC frame to be used for + * notifying the BSS. If non-zero, HAL will use the MAC frame + * buffer pointed to by macMgmtHdr. If zero, HAL will generate the + * appropriate MAC frame based on frameType. */ + u8 frame_len; + + /* Following the framelength there is a MAC frame buffer if + * frameLength is non-zero. */ + struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr; + + /* Entry to hold number of active BSS idx's */ + struct wcn36xx_hal_scan_entry scan_entry; +}; + +struct wcn36xx_hal_init_scan_con_req_msg { + struct wcn36xx_hal_msg_header header; + + /* LEARN - AP Role + SCAN - STA Role */ + enum wcn36xx_hal_sys_mode mode; + + /* BSSID of the BSS */ + u8 bssid[ETH_ALEN]; + + /* Whether BSS needs to be notified */ + u8 notify; + + /* Kind of frame to be used for notifying the BSS (Data Null, QoS + * Null, or CTS to Self). Must always be a valid frame type. */ + u8 frame_type; + + /* UMAC has the option of passing the MAC frame to be used for + * notifying the BSS. If non-zero, HAL will use the MAC frame + * buffer pointed to by macMgmtHdr. If zero, HAL will generate the + * appropriate MAC frame based on frameType. */ + u8 frame_length; + + /* Following the framelength there is a MAC frame buffer if + * frameLength is non-zero. */ + struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr; + + /* Entry to hold number of active BSS idx's */ + struct wcn36xx_hal_scan_entry scan_entry; + + /* Single NoA usage in Scanning */ + u8 use_noa; + + /* Indicates the scan duration (in ms) */ + u16 scan_duration; + +}; + +struct wcn36xx_hal_init_scan_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +} __packed; + +struct wcn36xx_hal_start_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Indicates the channel to scan */ + u8 scan_channel; +} __packed; + +struct wcn36xx_hal_start_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u32 start_tsf[2]; + u8 tx_mgmt_power; + +} __packed; + +struct wcn36xx_hal_end_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Indicates the channel to stop scanning. Not used really. But + * retained for symmetry with "start Scan" message. It can also + * help in error check if needed. */ + u8 scan_channel; +} __packed; + +struct wcn36xx_hal_end_scan_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct wcn36xx_hal_finish_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Identifies the operational state of the AP/STA + * LEARN - AP Role SCAN - STA Role */ + enum wcn36xx_hal_sys_mode mode; + + /* Operating channel to tune to. */ + u8 oper_channel; + + /* Channel Bonding state If 20/40 MHz is operational, this will + * indicate the 40 MHz extension channel in combination with the + * control channel */ + enum phy_chan_bond_state cb_state; + + /* BSSID of the BSS */ + u8 bssid[ETH_ALEN]; + + /* Whether BSS needs to be notified */ + u8 notify; + + /* Kind of frame to be used for notifying the BSS (Data Null, QoS + * Null, or CTS to Self). Must always be a valid frame type. */ + u8 frame_type; + + /* UMAC has the option of passing the MAC frame to be used for + * notifying the BSS. If non-zero, HAL will use the MAC frame + * buffer pointed to by macMgmtHdr. If zero, HAL will generate the + * appropriate MAC frame based on frameType. */ + u8 frame_length; + + /* Following the framelength there is a MAC frame buffer if + * frameLength is non-zero. */ + struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr; + + /* Entry to hold number of active BSS idx's */ + struct wcn36xx_hal_scan_entry scan_entry; + +} __packed; + +struct wcn36xx_hal_finish_scan_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +} __packed; + +enum wcn36xx_hal_rate_index { + HW_RATE_INDEX_1MBPS = 0x82, + HW_RATE_INDEX_2MBPS = 0x84, + HW_RATE_INDEX_5_5MBPS = 0x8B, + HW_RATE_INDEX_6MBPS = 0x0C, + HW_RATE_INDEX_9MBPS = 0x12, + HW_RATE_INDEX_11MBPS = 0x96, + HW_RATE_INDEX_12MBPS = 0x18, + HW_RATE_INDEX_18MBPS = 0x24, + HW_RATE_INDEX_24MBPS = 0x30, + HW_RATE_INDEX_36MBPS = 0x48, + HW_RATE_INDEX_48MBPS = 0x60, + HW_RATE_INDEX_54MBPS = 0x6C +}; + +struct wcn36xx_hal_supported_rates { + /* + * For Self STA Entry: this represents Self Mode. + * For Peer Stations, this represents the mode of the peer. + * On Station: + * + * --this mode is updated when PE adds the Self Entry. + * + * -- OR when PE sends 'ADD_BSS' message and station context in BSS + * is used to indicate the mode of the AP. + * + * ON AP: + * + * -- this mode is updated when PE sends 'ADD_BSS' and Sta entry + * for that BSS is used to indicate the self mode of the AP. + * + * -- OR when a station is associated, PE sends 'ADD_STA' message + * with this mode updated. + */ + + enum sta_rate_mode op_rate_mode; + + /* 11b, 11a and aniLegacyRates are IE rates which gives rate in + * unit of 500Kbps */ + u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES]; + u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES]; + u16 legacy_rates[WCN36XX_HAL_NUM_POLARIS_RATES]; + u16 reserved; + + /* Taurus only supports 26 Titan Rates(no ESF/concat Rates will be + * supported) First 26 bits are reserved for those Titan rates and + * the last 4 bits(bit28-31) for Taurus, 2(bit26-27) bits are + * reserved. */ + /* Titan and Taurus Rates */ + u32 enhanced_rate_bitmap; + + /* + * 0-76 bits used, remaining reserved + * bits 0-15 and 32 should be set. + */ + u8 supported_mcs_set[WCN36XX_HAL_MAC_MAX_SUPPORTED_MCS_SET]; + + /* + * RX Highest Supported Data Rate defines the highest data + * rate that the STA is able to receive, in unites of 1Mbps. + * This value is derived from "Supported MCS Set field" inside + * the HT capability element. + */ + u16 rx_highest_data_rate; + +} __packed; + +struct wcn36xx_hal_config_sta_params { + /* BSSID of STA */ + u8 bssid[ETH_ALEN]; + + /* ASSOC ID, as assigned by UMAC */ + u16 aid; + + /* STA entry Type: 0 - Self, 1 - Other/Peer, 2 - BSSID, 3 - BCAST */ + u8 type; + + /* Short Preamble Supported. */ + u8 short_preamble_supported; + + /* MAC Address of STA */ + u8 mac[ETH_ALEN]; + + /* Listen interval of the STA */ + u16 listen_interval; + + /* Support for 11e/WMM */ + u8 wmm_enabled; + + /* 11n HT capable STA */ + u8 ht_capable; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* RIFS mode 0 - NA, 1 - Allowed */ + u8 rifs_mode; + + /* L-SIG TXOP Protection mechanism + 0 - No Support, 1 - Supported + SG - there is global field */ + u8 lsig_txop_protection; + + /* Max Ampdu Size supported by STA. TPE programming. + 0 : 8k , 1 : 16k, 2 : 32k, 3 : 64k */ + u8 max_ampdu_size; + + /* Max Ampdu density. Used by RA. 3 : 0~7 : 2^(11nAMPDUdensity -4) */ + u8 max_ampdu_density; + + /* Max AMSDU size 1 : 3839 bytes, 0 : 7935 bytes */ + u8 max_amsdu_size; + + /* Short GI support for 40Mhz packets */ + u8 sgi_40mhz; + + /* Short GI support for 20Mhz packets */ + u8 sgi_20Mhz; + + /* TODO move this parameter to the end for 3680 */ + /* These rates are the intersection of peer and self capabilities. */ + struct wcn36xx_hal_supported_rates supported_rates; + + /* Robust Management Frame (RMF) enabled/disabled */ + u8 rmf; + + /* The unicast encryption type in the association */ + u32 encrypt_type; + + /* HAL should update the existing STA entry, if this flag is set. UMAC + will set this flag in case of RE-ASSOC, where we want to reuse the + old STA ID. 0 = Add, 1 = Update */ + u8 action; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* Max SP Length */ + u8 max_sp_len; + + /* 11n Green Field preamble support + 0 - Not supported, 1 - Supported */ + u8 green_field_capable; + + /* MIMO Power Save mode */ + enum wcn36xx_hal_ht_mimo_state mimo_ps; + + /* Delayed BA Support */ + u8 delayed_ba_support; + + /* Max AMPDU duration in 32us */ + u8 max_ampdu_duration; + + /* HT STA should set it to 1 if it is enabled in BSS. HT STA should + * set it to 0 if AP does not support it. This indication is sent + * to HAL and HAL uses this flag to pickup up appropriate 40Mhz + * rates. */ + u8 dsss_cck_mode_40mhz; + + /* Valid STA Idx when action=Update. Set to 0xFF when invalid! + * Retained for backward compalibity with existing HAL code */ + u8 sta_index; + + /* BSSID of BSS to which station is associated. Set to 0xFF when + * invalid. Retained for backward compalibity with existing HAL + * code */ + u8 bssid_index; + + u8 p2p; + + /* TODO add this parameter for 3680. */ + /* Reserved to align next field on a dword boundary */ + /* u8 reserved; */ +} __packed; + +struct wcn36xx_hal_config_sta_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_sta_params sta_params; +} __packed; + +struct wcn36xx_hal_config_sta_params_v1 { + /* BSSID of STA */ + u8 bssid[ETH_ALEN]; + + /* ASSOC ID, as assigned by UMAC */ + u16 aid; + + /* STA entry Type: 0 - Self, 1 - Other/Peer, 2 - BSSID, 3 - BCAST */ + u8 type; + + /* Short Preamble Supported. */ + u8 short_preamble_supported; + + /* MAC Address of STA */ + u8 mac[ETH_ALEN]; + + /* Listen interval of the STA */ + u16 listen_interval; + + /* Support for 11e/WMM */ + u8 wmm_enabled; + + /* 11n HT capable STA */ + u8 ht_capable; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* RIFS mode 0 - NA, 1 - Allowed */ + u8 rifs_mode; + + /* L-SIG TXOP Protection mechanism + 0 - No Support, 1 - Supported + SG - there is global field */ + u8 lsig_txop_protection; + + /* Max Ampdu Size supported by STA. TPE programming. + 0 : 8k , 1 : 16k, 2 : 32k, 3 : 64k */ + u8 max_ampdu_size; + + /* Max Ampdu density. Used by RA. 3 : 0~7 : 2^(11nAMPDUdensity -4) */ + u8 max_ampdu_density; + + /* Max AMSDU size 1 : 3839 bytes, 0 : 7935 bytes */ + u8 max_amsdu_size; + + /* Short GI support for 40Mhz packets */ + u8 sgi_40mhz; + + /* Short GI support for 20Mhz packets */ + u8 sgi_20Mhz; + + /* Robust Management Frame (RMF) enabled/disabled */ + u8 rmf; + + /* The unicast encryption type in the association */ + u32 encrypt_type; + + /* HAL should update the existing STA entry, if this flag is set. UMAC + will set this flag in case of RE-ASSOC, where we want to reuse the + old STA ID. 0 = Add, 1 = Update */ + u8 action; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* Max SP Length */ + u8 max_sp_len; + + /* 11n Green Field preamble support + 0 - Not supported, 1 - Supported */ + u8 green_field_capable; + + /* MIMO Power Save mode */ + enum wcn36xx_hal_ht_mimo_state mimo_ps; + + /* Delayed BA Support */ + u8 delayed_ba_support; + + /* Max AMPDU duration in 32us */ + u8 max_ampdu_duration; + + /* HT STA should set it to 1 if it is enabled in BSS. HT STA should + * set it to 0 if AP does not support it. This indication is sent + * to HAL and HAL uses this flag to pickup up appropriate 40Mhz + * rates. */ + u8 dsss_cck_mode_40mhz; + + /* Valid STA Idx when action=Update. Set to 0xFF when invalid! + * Retained for backward compalibity with existing HAL code */ + u8 sta_index; + + /* BSSID of BSS to which station is associated. Set to 0xFF when + * invalid. Retained for backward compalibity with existing HAL + * code */ + u8 bssid_index; + + u8 p2p; + + /* Reserved to align next field on a dword boundary */ + u8 reserved; + + /* These rates are the intersection of peer and self capabilities. */ + struct wcn36xx_hal_supported_rates supported_rates; +} __packed; + +struct wcn36xx_hal_config_sta_req_msg_v1 { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_sta_params_v1 sta_params; +} __packed; + +struct config_sta_rsp_params { + /* success or failure */ + u32 status; + + /* Station index; valid only when 'status' field value SUCCESS */ + u8 sta_index; + + /* BSSID Index of BSS to which the station is associated */ + u8 bssid_index; + + /* DPU Index for PTK */ + u8 dpu_index; + + /* DPU Index for GTK */ + u8 bcast_dpu_index; + + /* DPU Index for IGTK */ + u8 bcast_mgmt_dpu_idx; + + /* PTK DPU signature */ + u8 uc_ucast_sig; + + /* GTK DPU isignature */ + u8 uc_bcast_sig; + + /* IGTK DPU signature */ + u8 uc_mgmt_sig; + + u8 p2p; + +} __packed; + +struct wcn36xx_hal_config_sta_rsp_msg { + struct wcn36xx_hal_msg_header header; + + struct config_sta_rsp_params params; +} __packed; + +/* Delete STA Request message */ +struct wcn36xx_hal_delete_sta_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Index of STA to delete */ + u8 sta_index; + +} __packed; + +/* Delete STA Response message */ +struct wcn36xx_hal_delete_sta_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Index of STA deleted */ + u8 sta_id; +} __packed; + +/* 12 Bytes long because this structure can be used to represent rate and + * extended rate set IEs. The parser assume this to be at least 12 */ +struct wcn36xx_hal_rate_set { + u8 num_rates; + u8 rate[WCN36XX_HAL_MAC_RATESET_EID_MAX]; +} __packed; + +/* access category record */ +struct wcn36xx_hal_aci_aifsn { +#ifndef ANI_LITTLE_BIT_ENDIAN + u8 rsvd:1; + u8 aci:2; + u8 acm:1; + u8 aifsn:4; +#else + u8 aifsn:4; + u8 acm:1; + u8 aci:2; + u8 rsvd:1; +#endif +} __packed; + +/* contention window size */ +struct wcn36xx_hal_mac_cw { +#ifndef ANI_LITTLE_BIT_ENDIAN + u8 max:4; + u8 min:4; +#else + u8 min:4; + u8 max:4; +#endif +} __packed; + +struct wcn36xx_hal_edca_param_record { + struct wcn36xx_hal_aci_aifsn aci; + struct wcn36xx_hal_mac_cw cw; + u16 txop_limit; +} __packed; + +struct wcn36xx_hal_mac_ssid { + u8 length; + u8 ssid[32]; +} __packed; + +/* Concurrency role. These are generic IDs that identify the various roles + * in the software system. */ +enum wcn36xx_hal_con_mode { + WCN36XX_HAL_STA_MODE = 0, + + /* to support softAp mode . This is misleading. + It means AP MODE only. */ + WCN36XX_HAL_STA_SAP_MODE = 1, + + WCN36XX_HAL_P2P_CLIENT_MODE, + WCN36XX_HAL_P2P_GO_MODE, + WCN36XX_HAL_MONITOR_MODE, +}; + +/* This is a bit pattern to be set for each mode + * bit 0 - sta mode + * bit 1 - ap mode + * bit 2 - p2p client mode + * bit 3 - p2p go mode */ +enum wcn36xx_hal_concurrency_mode { + HAL_STA = 1, + HAL_SAP = 2, + + /* to support sta, softAp mode . This means STA+AP mode */ + HAL_STA_SAP = 3, + + HAL_P2P_CLIENT = 4, + HAL_P2P_GO = 8, + HAL_MAX_CONCURRENCY_PERSONA = 4 +}; + +struct wcn36xx_hal_config_bss_params { + /* BSSID */ + u8 bssid[ETH_ALEN]; + + /* Self Mac Address */ + u8 self_mac_addr[ETH_ALEN]; + + /* BSS type */ + enum wcn36xx_hal_bss_type bss_type; + + /* Operational Mode: AP =0, STA = 1 */ + u8 oper_mode; + + /* Network Type */ + enum wcn36xx_hal_nw_type nw_type; + + /* Used to classify PURE_11G/11G_MIXED to program MTU */ + u8 short_slot_time_supported; + + /* Co-exist with 11a STA */ + u8 lla_coexist; + + /* Co-exist with 11b STA */ + u8 llb_coexist; + + /* Co-exist with 11g STA */ + u8 llg_coexist; + + /* Coexistence with 11n STA */ + u8 ht20_coexist; + + /* Non GF coexist flag */ + u8 lln_non_gf_coexist; + + /* TXOP protection support */ + u8 lsig_tx_op_protection_full_support; + + /* RIFS mode */ + u8 rifs_mode; + + /* Beacon Interval in TU */ + u16 beacon_interval; + + /* DTIM period */ + u8 dtim_period; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* Operating channel */ + u8 oper_channel; + + /* Extension channel for channel bonding */ + u8 ext_channel; + + /* Reserved to align next field on a dword boundary */ + u8 reserved; + + /* TODO move sta to the end for 3680 */ + /* Context of the station being added in HW + * Add a STA entry for "itself" - + * + * On AP - Add the AP itself in an "STA context" + * + * On STA - Add the AP to which this STA is joining in an + * "STA context" + */ + struct wcn36xx_hal_config_sta_params sta; + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* HAL should update the existing BSS entry, if this flag is set. + * UMAC will set this flag in case of reassoc, where we want to + * resue the the old BSSID and still return success 0 = Add, 1 = + * Update */ + u8 action; + + /* MAC Rate Set */ + struct wcn36xx_hal_rate_set rateset; + + /* Enable/Disable HT capabilities of the BSS */ + u8 ht; + + /* Enable/Disable OBSS protection */ + u8 obss_prot_enabled; + + /* RMF enabled/disabled */ + u8 rmf; + + /* HT Operating Mode operating mode of the 802.11n STA */ + enum wcn36xx_hal_ht_operating_mode ht_oper_mode; + + /* Dual CTS Protection: 0 - Unused, 1 - Used */ + u8 dual_cts_protection; + + /* Probe Response Max retries */ + u8 max_probe_resp_retry_limit; + + /* To Enable Hidden ssid */ + u8 hidden_ssid; + + /* To Enable Disable FW Proxy Probe Resp */ + u8 proxy_probe_resp; + + /* Boolean to indicate if EDCA params are valid. UMAC might not + * have valid EDCA params or might not desire to apply EDCA params + * during config BSS. 0 implies Not Valid ; Non-Zero implies + * valid */ + u8 edca_params_valid; + + /* EDCA Parameters for Best Effort Access Category */ + struct wcn36xx_hal_edca_param_record acbe; + + /* EDCA Parameters forBackground Access Category */ + struct wcn36xx_hal_edca_param_record acbk; + + /* EDCA Parameters for Video Access Category */ + struct wcn36xx_hal_edca_param_record acvi; + + /* EDCA Parameters for Voice Access Category */ + struct wcn36xx_hal_edca_param_record acvo; + + /* Ext Bss Config Msg if set */ + u8 ext_set_sta_key_param_valid; + + /* SetStaKeyParams for ext bss msg */ + struct wcn36xx_hal_set_sta_key_params ext_set_sta_key_param; + + /* Persona for the BSS can be STA,AP,GO,CLIENT value same as enum + * wcn36xx_hal_con_mode */ + u8 wcn36xx_hal_persona; + + u8 spectrum_mgt_enable; + + /* HAL fills in the tx power used for mgmt frames in txMgmtPower */ + s8 tx_mgmt_power; + + /* maxTxPower has max power to be used after applying the power + * constraint if any */ + s8 max_tx_power; +} __packed; + +struct wcn36xx_hal_config_bss_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_bss_params bss_params; +} __packed; + +struct wcn36xx_hal_config_bss_params_v1 { + /* BSSID */ + u8 bssid[ETH_ALEN]; + + /* Self Mac Address */ + u8 self_mac_addr[ETH_ALEN]; + + /* BSS type */ + enum wcn36xx_hal_bss_type bss_type; + + /* Operational Mode: AP =0, STA = 1 */ + u8 oper_mode; + + /* Network Type */ + enum wcn36xx_hal_nw_type nw_type; + + /* Used to classify PURE_11G/11G_MIXED to program MTU */ + u8 short_slot_time_supported; + + /* Co-exist with 11a STA */ + u8 lla_coexist; + + /* Co-exist with 11b STA */ + u8 llb_coexist; + + /* Co-exist with 11g STA */ + u8 llg_coexist; + + /* Coexistence with 11n STA */ + u8 ht20_coexist; + + /* Non GF coexist flag */ + u8 lln_non_gf_coexist; + + /* TXOP protection support */ + u8 lsig_tx_op_protection_full_support; + + /* RIFS mode */ + u8 rifs_mode; + + /* Beacon Interval in TU */ + u16 beacon_interval; + + /* DTIM period */ + u8 dtim_period; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* Operating channel */ + u8 oper_channel; + + /* Extension channel for channel bonding */ + u8 ext_channel; + + /* Reserved to align next field on a dword boundary */ + u8 reserved; + + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* HAL should update the existing BSS entry, if this flag is set. + * UMAC will set this flag in case of reassoc, where we want to + * resue the the old BSSID and still return success 0 = Add, 1 = + * Update */ + u8 action; + + /* MAC Rate Set */ + struct wcn36xx_hal_rate_set rateset; + + /* Enable/Disable HT capabilities of the BSS */ + u8 ht; + + /* Enable/Disable OBSS protection */ + u8 obss_prot_enabled; + + /* RMF enabled/disabled */ + u8 rmf; + + /* HT Operating Mode operating mode of the 802.11n STA */ + enum wcn36xx_hal_ht_operating_mode ht_oper_mode; + + /* Dual CTS Protection: 0 - Unused, 1 - Used */ + u8 dual_cts_protection; + + /* Probe Response Max retries */ + u8 max_probe_resp_retry_limit; + + /* To Enable Hidden ssid */ + u8 hidden_ssid; + + /* To Enable Disable FW Proxy Probe Resp */ + u8 proxy_probe_resp; + + /* Boolean to indicate if EDCA params are valid. UMAC might not + * have valid EDCA params or might not desire to apply EDCA params + * during config BSS. 0 implies Not Valid ; Non-Zero implies + * valid */ + u8 edca_params_valid; + + /* EDCA Parameters for Best Effort Access Category */ + struct wcn36xx_hal_edca_param_record acbe; + + /* EDCA Parameters forBackground Access Category */ + struct wcn36xx_hal_edca_param_record acbk; + + /* EDCA Parameters for Video Access Category */ + struct wcn36xx_hal_edca_param_record acvi; + + /* EDCA Parameters for Voice Access Category */ + struct wcn36xx_hal_edca_param_record acvo; + + /* Ext Bss Config Msg if set */ + u8 ext_set_sta_key_param_valid; + + /* SetStaKeyParams for ext bss msg */ + struct wcn36xx_hal_set_sta_key_params ext_set_sta_key_param; + + /* Persona for the BSS can be STA,AP,GO,CLIENT value same as enum + * wcn36xx_hal_con_mode */ + u8 wcn36xx_hal_persona; + + u8 spectrum_mgt_enable; + + /* HAL fills in the tx power used for mgmt frames in txMgmtPower */ + s8 tx_mgmt_power; + + /* maxTxPower has max power to be used after applying the power + * constraint if any */ + s8 max_tx_power; + + /* Context of the station being added in HW + * Add a STA entry for "itself" - + * + * On AP - Add the AP itself in an "STA context" + * + * On STA - Add the AP to which this STA is joining in an + * "STA context" + */ + struct wcn36xx_hal_config_sta_params_v1 sta; +} __packed; + +struct wcn36xx_hal_config_bss_req_msg_v1 { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_bss_params_v1 bss_params; +} __packed; + +struct wcn36xx_hal_config_bss_rsp_params { + /* Success or Failure */ + u32 status; + + /* BSS index allocated by HAL */ + u8 bss_index; + + /* DPU descriptor index for PTK */ + u8 dpu_desc_index; + + /* PTK DPU signature */ + u8 ucast_dpu_signature; + + /* DPU descriptor index for GTK */ + u8 bcast_dpu_desc_indx; + + /* GTK DPU signature */ + u8 bcast_dpu_signature; + + /* DPU descriptor for IGTK */ + u8 mgmt_dpu_desc_index; + + /* IGTK DPU signature */ + u8 mgmt_dpu_signature; + + /* Station Index for BSS entry */ + u8 bss_sta_index; + + /* Self station index for this BSS */ + u8 bss_self_sta_index; + + /* Bcast station for buffering bcast frames in AP role */ + u8 bss_bcast_sta_idx; + + /* MAC Address of STA(PEER/SELF) in staContext of configBSSReq */ + u8 mac[ETH_ALEN]; + + /* HAL fills in the tx power used for mgmt frames in this field. */ + s8 tx_mgmt_power; + +} __packed; + +struct wcn36xx_hal_config_bss_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_bss_rsp_params bss_rsp_params; +} __packed; + +struct wcn36xx_hal_delete_bss_req_msg { + struct wcn36xx_hal_msg_header header; + + /* BSS index to be deleted */ + u8 bss_index; + +} __packed; + +struct wcn36xx_hal_delete_bss_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure */ + u32 status; + + /* BSS index that has been deleted */ + u8 bss_index; + +} __packed; + +struct wcn36xx_hal_join_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Indicates the BSSID to which STA is going to associate */ + u8 bssid[ETH_ALEN]; + + /* Indicates the channel to switch to. */ + u8 channel; + + /* Self STA MAC */ + u8 self_sta_mac_addr[ETH_ALEN]; + + /* Local power constraint */ + u8 local_power_constraint; + + /* Secondary channel offset */ + enum phy_chan_bond_state secondary_channel_offset; + + /* link State */ + enum wcn36xx_hal_link_state link_state; + + /* Max TX power */ + s8 max_tx_power; +} __packed; + +struct wcn36xx_hal_join_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* HAL fills in the tx power used for mgmt frames in this field */ + u8 tx_mgmt_power; +} __packed; + +struct post_assoc_req_msg { + struct wcn36xx_hal_msg_header header; + + struct wcn36xx_hal_config_sta_params sta_params; + struct wcn36xx_hal_config_bss_params bss_params; +}; + +struct post_assoc_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct config_sta_rsp_params sta_rsp_params; + struct wcn36xx_hal_config_bss_rsp_params bss_rsp_params; +}; + +/* This is used to create a set of WEP keys for a given BSS. */ +struct wcn36xx_hal_set_bss_key_req_msg { + struct wcn36xx_hal_msg_header header; + + /* BSS Index of the BSS */ + u8 bss_idx; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* Number of keys */ + u8 num_keys; + + /* Array of keys. */ + struct wcn36xx_hal_keys keys[WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS]; + + /* Control for Replay Count, 1= Single TID based replay count on Tx + * 0 = Per TID based replay count on TX */ + u8 single_tid_rc; +} __packed; + +/* tagged version of set bss key */ +struct wcn36xx_hal_set_bss_key_req_msg_tagged { + struct wcn36xx_hal_set_bss_key_req_msg Msg; + u32 tag; +} __packed; + +struct wcn36xx_hal_set_bss_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +/* + * This is used configure the key information on a given station. + * When the sec_type is WEP40 or WEP104, the def_wep_idx is used to locate + * a preconfigured key from a BSS the station assoicated with; otherwise + * a new key descriptor is created based on the key field. + */ +struct wcn36xx_hal_set_sta_key_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_set_sta_key_params set_sta_key_params; +} __packed; + +struct wcn36xx_hal_set_sta_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct wcn36xx_hal_remove_bss_key_req_msg { + struct wcn36xx_hal_msg_header header; + + /* BSS Index of the BSS */ + u8 bss_idx; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* Key Id */ + u8 key_id; + + /* STATIC/DYNAMIC. Used in Nullifying in Key Descriptors for + * Static/Dynamic keys */ + enum ani_wep_type wep_type; +} __packed; + +struct wcn36xx_hal_remove_bss_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +/* + * This is used by PE to Remove the key information on a given station. + */ +struct wcn36xx_hal_remove_sta_key_req_msg { + struct wcn36xx_hal_msg_header header; + + /* STA Index */ + u16 sta_idx; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* Key Id */ + u8 key_id; + + /* Whether to invalidate the Broadcast key or Unicast key. In case + * of WEP, the same key is used for both broadcast and unicast. */ + u8 unicast; + +} __packed; + +struct wcn36xx_hal_remove_sta_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /*success or failure */ + u32 status; + +} __packed; + +#ifdef FEATURE_OEM_DATA_SUPPORT + +#ifndef OEM_DATA_REQ_SIZE +#define OEM_DATA_REQ_SIZE 134 +#endif + +#ifndef OEM_DATA_RSP_SIZE +#define OEM_DATA_RSP_SIZE 1968 +#endif + +struct start_oem_data_req_msg { + struct wcn36xx_hal_msg_header header; + + u32 status; + tSirMacAddr self_mac_addr; + u8 oem_data_req[OEM_DATA_REQ_SIZE]; + +}; + +struct start_oem_data_rsp_msg { + struct wcn36xx_hal_msg_header header; + + u8 oem_data_rsp[OEM_DATA_RSP_SIZE]; +}; + +#endif + +struct wcn36xx_hal_switch_channel_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Channel number */ + u8 channel_number; + + /* Local power constraint */ + u8 local_power_constraint; + + /* Secondary channel offset */ + enum phy_chan_bond_state secondary_channel_offset; + + /* HAL fills in the tx power used for mgmt frames in this field. */ + u8 tx_mgmt_power; + + /* Max TX power */ + u8 max_tx_power; + + /* Self STA MAC */ + u8 self_sta_mac_addr[ETH_ALEN]; + + /* VO WIFI comment: BSSID needed to identify session. As the + * request has power constraints, this should be applied only to + * that session Since MTU timing and EDCA are sessionized, this + * struct needs to be sessionized and bssid needs to be out of the + * VOWifi feature flag V IMP: Keep bssId field at the end of this + * msg. It is used to mantain backward compatbility by way of + * ignoring if using new host/old FW or old host/new FW since it is + * at the end of this struct + */ + u8 bssid[ETH_ALEN]; +} __packed; + +struct wcn36xx_hal_switch_channel_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Status */ + u32 status; + + /* Channel number - same as in request */ + u8 channel_number; + + /* HAL fills in the tx power used for mgmt frames in this field */ + u8 tx_mgmt_power; + + /* BSSID needed to identify session - same as in request */ + u8 bssid[ETH_ALEN]; + +} __packed; + +struct update_edca_params_req_msg { + struct wcn36xx_hal_msg_header header; + + /*BSS Index */ + u16 bss_index; + + /* Best Effort */ + struct wcn36xx_hal_edca_param_record acbe; + + /* Background */ + struct wcn36xx_hal_edca_param_record acbk; + + /* Video */ + struct wcn36xx_hal_edca_param_record acvi; + + /* Voice */ + struct wcn36xx_hal_edca_param_record acvo; +}; + +struct update_edca_params_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct dpu_stats_params { + /* Index of STA to which the statistics */ + u16 sta_index; + + /* Encryption mode */ + u8 enc_mode; + + /* status */ + u32 status; + + /* Statistics */ + u32 send_blocks; + u32 recv_blocks; + u32 replays; + u8 mic_error_cnt; + u32 prot_excl_cnt; + u16 format_err_cnt; + u16 un_decryptable_cnt; + u32 decrypt_err_cnt; + u32 decrypt_ok_cnt; +}; + +struct wcn36xx_hal_stats_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Valid STA Idx for per STA stats request */ + u32 sta_id; + + /* Categories of stats requested as specified in eHalStatsMask */ + u32 stats_mask; +}; + +struct ani_summary_stats_info { + /* Total number of packets(per AC) that were successfully + * transmitted with retries */ + u32 retry_cnt[4]; + + /* The number of MSDU packets and MMPDU frames per AC that the + * 802.11 station successfully transmitted after more than one + * retransmission attempt */ + u32 multiple_retry_cnt[4]; + + /* Total number of packets(per AC) that were successfully + * transmitted (with and without retries, including multi-cast, + * broadcast) */ + u32 tx_frm_cnt[4]; + + /* Total number of packets that were successfully received (after + * appropriate filter rules including multi-cast, broadcast) */ + u32 rx_frm_cnt; + + /* Total number of duplicate frames received successfully */ + u32 frm_dup_cnt; + + /* Total number packets(per AC) failed to transmit */ + u32 fail_cnt[4]; + + /* Total number of RTS/CTS sequence failures for transmission of a + * packet */ + u32 rts_fail_cnt; + + /* Total number packets failed transmit because of no ACK from the + * remote entity */ + u32 ack_fail_cnt; + + /* Total number of RTS/CTS sequence success for transmission of a + * packet */ + u32 rts_succ_cnt; + + /* The sum of the receive error count and dropped-receive-buffer + * error count. HAL will provide this as a sum of (FCS error) + + * (Fail get BD/PDU in HW) */ + u32 rx_discard_cnt; + + /* + * The receive error count. HAL will provide the RxP FCS error + * global counter. */ + u32 rx_error_cnt; + + /* The sum of the transmit-directed byte count, transmit-multicast + * byte count and transmit-broadcast byte count. HAL will sum TPE + * UC/MC/BCAST global counters to provide this. */ + u32 tx_byte_cnt; +}; + +/* defines tx_rate_flags */ +enum tx_rate_info { + /* Legacy rates */ + HAL_TX_RATE_LEGACY = 0x1, + + /* HT20 rates */ + HAL_TX_RATE_HT20 = 0x2, + + /* HT40 rates */ + HAL_TX_RATE_HT40 = 0x4, + + /* Rate with Short guard interval */ + HAL_TX_RATE_SGI = 0x8, + + /* Rate with Long guard interval */ + HAL_TX_RATE_LGI = 0x10 +}; + +struct ani_global_class_a_stats_info { + /* The number of MPDU frames received by the 802.11 station for + * MSDU packets or MMPDU frames */ + u32 rx_frag_cnt; + + /* The number of MPDU frames received by the 802.11 station for + * MSDU packets or MMPDU frames when a promiscuous packet filter + * was enabled */ + u32 promiscuous_rx_frag_cnt; + + /* The receiver input sensitivity referenced to a FER of 8% at an + * MPDU length of 1024 bytes at the antenna connector. Each element + * of the array shall correspond to a supported rate and the order + * shall be the same as the supporteRates parameter. */ + u32 rx_input_sensitivity; + + /* The maximum transmit power in dBm upto one decimal. for eg: if + * it is 10.5dBm, the value would be 105 */ + u32 max_pwr; + + /* Number of times the receiver failed to synchronize with the + * incoming signal after detecting the sync in the preamble of the + * transmitted PLCP protocol data unit. */ + u32 sync_fail_cnt; + + /* Legacy transmit rate, in units of 500 kbit/sec, for the most + * recently transmitted frame */ + u32 tx_rate; + + /* mcs index for HT20 and HT40 rates */ + u32 mcs_index; + + /* to differentiate between HT20 and HT40 rates; short and long + * guard interval */ + u32 tx_rate_flags; +}; + +struct ani_global_security_stats { + /* The number of unencrypted received MPDU frames that the MAC + * layer discarded when the IEEE 802.11 dot11ExcludeUnencrypted + * management information base (MIB) object is enabled */ + u32 rx_wep_unencrypted_frm_cnt; + + /* The number of received MSDU packets that that the 802.11 station + * discarded because of MIC failures */ + u32 rx_mic_fail_cnt; + + /* The number of encrypted MPDU frames that the 802.11 station + * failed to decrypt because of a TKIP ICV error */ + u32 tkip_icv_err; + + /* The number of received MPDU frames that the 802.11 discarded + * because of an invalid AES-CCMP format */ + u32 aes_ccmp_format_err; + + /* The number of received MPDU frames that the 802.11 station + * discarded because of the AES-CCMP replay protection procedure */ + u32 aes_ccmp_replay_cnt; + + /* The number of received MPDU frames that the 802.11 station + * discarded because of errors detected by the AES-CCMP decryption + * algorithm */ + u32 aes_ccmp_decrpt_err; + + /* The number of encrypted MPDU frames received for which a WEP + * decryption key was not available on the 802.11 station */ + u32 wep_undecryptable_cnt; + + /* The number of encrypted MPDU frames that the 802.11 station + * failed to decrypt because of a WEP ICV error */ + u32 wep_icv_err; + + /* The number of received encrypted packets that the 802.11 station + * successfully decrypted */ + u32 rx_decrypt_succ_cnt; + + /* The number of encrypted packets that the 802.11 station failed + * to decrypt */ + u32 rx_decrypt_fail_cnt; +}; + +struct ani_global_class_b_stats_info { + struct ani_global_security_stats uc_stats; + struct ani_global_security_stats mc_bc_stats; +}; + +struct ani_global_class_c_stats_info { + /* This counter shall be incremented for a received A-MSDU frame + * with the stations MAC address in the address 1 field or an + * A-MSDU frame with a group address in the address 1 field */ + u32 rx_amsdu_cnt; + + /* This counter shall be incremented when the MAC receives an AMPDU + * from the PHY */ + u32 rx_ampdu_cnt; + + /* This counter shall be incremented when a Frame is transmitted + * only on the primary channel */ + u32 tx_20_frm_cnt; + + /* This counter shall be incremented when a Frame is received only + * on the primary channel */ + u32 rx_20_frm_cnt; + + /* This counter shall be incremented by the number of MPDUs + * received in the A-MPDU when an A-MPDU is received */ + u32 rx_mpdu_in_ampdu_cnt; + + /* This counter shall be incremented when an MPDU delimiter has a + * CRC error when this is the first CRC error in the received AMPDU + * or when the previous delimiter has been decoded correctly */ + u32 ampdu_delimiter_crc_err; +}; + +struct ani_per_sta_stats_info { + /* The number of MPDU frames that the 802.11 station transmitted + * and acknowledged through a received 802.11 ACK frame */ + u32 tx_frag_cnt[4]; + + /* This counter shall be incremented when an A-MPDU is transmitted */ + u32 tx_ampdu_cnt; + + /* This counter shall increment by the number of MPDUs in the AMPDU + * when an A-MPDU is transmitted */ + u32 tx_mpdu_in_ampdu_cnt; +}; + +struct wcn36xx_hal_stats_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure */ + u32 status; + + /* STA Idx */ + u32 sta_index; + + /* Categories of STATS being returned as per eHalStatsMask */ + u32 stats_mask; + + /* message type is same as the request type */ + u16 msg_type; + + /* length of the entire request, includes the pStatsBuf length too */ + u16 msg_len; +}; + +struct wcn36xx_hal_set_link_state_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bssid[ETH_ALEN]; + enum wcn36xx_hal_link_state state; + u8 self_mac_addr[ETH_ALEN]; + +} __packed; + +struct set_link_state_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +/* TSPEC Params */ +struct wcn36xx_hal_ts_info_tfc { +#ifndef ANI_LITTLE_BIT_ENDIAN + u16 ackPolicy:2; + u16 userPrio:3; + u16 psb:1; + u16 aggregation:1; + u16 accessPolicy:2; + u16 direction:2; + u16 tsid:4; + u16 trafficType:1; +#else + u16 trafficType:1; + u16 tsid:4; + u16 direction:2; + u16 accessPolicy:2; + u16 aggregation:1; + u16 psb:1; + u16 userPrio:3; + u16 ackPolicy:2; +#endif +}; + +/* Flag to schedule the traffic type */ +struct wcn36xx_hal_ts_info_sch { +#ifndef ANI_LITTLE_BIT_ENDIAN + u8 rsvd:7; + u8 schedule:1; +#else + u8 schedule:1; + u8 rsvd:7; +#endif +}; + +/* Traffic and scheduling info */ +struct wcn36xx_hal_ts_info { + struct wcn36xx_hal_ts_info_tfc traffic; + struct wcn36xx_hal_ts_info_sch schedule; +}; + +/* Information elements */ +struct wcn36xx_hal_tspec_ie { + u8 type; + u8 length; + struct wcn36xx_hal_ts_info ts_info; + u16 nom_msdu_size; + u16 max_msdu_size; + u32 min_svc_interval; + u32 max_svc_interval; + u32 inact_interval; + u32 suspend_interval; + u32 svc_start_time; + u32 min_data_rate; + u32 mean_data_rate; + u32 peak_data_rate; + u32 max_burst_sz; + u32 delay_bound; + u32 min_phy_rate; + u16 surplus_bw; + u16 medium_time; +}; + +struct add_ts_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* TSPEC handler uniquely identifying a TSPEC for a STA in a BSS */ + u16 tspec_index; + + /* To program TPE with required parameters */ + struct wcn36xx_hal_tspec_ie tspec; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* These parameters are for all the access categories */ + + /* Service Interval */ + u32 service_interval[WCN36XX_HAL_MAX_AC]; + + /* Suspend Interval */ + u32 suspend_interval[WCN36XX_HAL_MAX_AC]; + + /* Delay Interval */ + u32 delay_interval[WCN36XX_HAL_MAX_AC]; +}; + +struct add_rs_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct del_ts_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* TSPEC identifier uniquely identifying a TSPEC for a STA in a BSS */ + u16 tspec_index; + + /* To lookup station id using the mac address */ + u8 bssid[ETH_ALEN]; +}; + +struct del_ts_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +/* End of TSpec Parameters */ + +/* Start of BLOCK ACK related Parameters */ + +struct wcn36xx_hal_add_ba_session_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* Peer MAC Address */ + u8 mac_addr[ETH_ALEN]; + + /* ADDBA Action Frame dialog token + HAL will not interpret this object */ + u8 dialog_token; + + /* TID for which the BA is being setup + This identifies the TC or TS of interest */ + u8 tid; + + /* 0 - Delayed BA (Not supported) + 1 - Immediate BA */ + u8 policy; + + /* Indicates the number of buffers for this TID (baTID) + NOTE - This is the requested buffer size. When this + is processed by HAL and subsequently by HDD, it is + possible that HDD may change this buffer size. Any + change in the buffer size should be noted by PE and + advertized appropriately in the ADDBA response */ + u16 buffer_size; + + /* BA timeout in TU's 0 means no timeout will occur */ + u16 timeout; + + /* b0..b3 - Fragment Number - Always set to 0 + b4..b15 - Starting Sequence Number of first MSDU + for which this BA is setup */ + u16 ssn; + + /* ADDBA direction + 1 - Originator + 0 - Recipient */ + u8 direction; +} __packed; + +struct wcn36xx_hal_add_ba_session_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Dialog token */ + u8 dialog_token; + + /* TID for which the BA session has been setup */ + u8 ba_tid; + + /* BA Buffer Size allocated for the current BA session */ + u8 ba_buffer_size; + + u8 ba_session_id; + + /* Reordering Window buffer */ + u8 win_size; + + /* Station Index to id the sta */ + u8 sta_index; + + /* Starting Sequence Number */ + u16 ssn; +} __packed; + +struct wcn36xx_hal_add_ba_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Session Id */ + u8 session_id; + + /* Reorder Window Size */ + u8 win_size; +/* Old FW 1.2.2.4 does not support this*/ +#ifdef FEATURE_ON_CHIP_REORDERING + u8 reordering_done_on_chip; +#endif +} __packed; + +struct wcn36xx_hal_add_ba_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Dialog token */ + u8 dialog_token; +} __packed; + +struct add_ba_info { + u16 ba_enable:1; + u16 starting_seq_num:12; + u16 reserved:3; +}; + +struct wcn36xx_hal_trigger_ba_rsp_candidate { + u8 sta_addr[ETH_ALEN]; + struct add_ba_info ba_info[STACFG_MAX_TC]; +} __packed; + +struct wcn36xx_hal_trigger_ba_req_candidate { + u8 sta_index; + u8 tid_bitmap; +} __packed; + +struct wcn36xx_hal_trigger_ba_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Session Id */ + u8 session_id; + + /* baCandidateCnt is followed by trigger BA + * Candidate List(tTriggerBaCandidate) + */ + u16 candidate_cnt; + +} __packed; + +struct wcn36xx_hal_trigger_ba_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* TO SUPPORT BT-AMP */ + u8 bssid[ETH_ALEN]; + + /* success or failure */ + u32 status; + + /* baCandidateCnt is followed by trigger BA + * Rsp Candidate List(tTriggerRspBaCandidate) + */ + u16 candidate_cnt; +} __packed; + +struct wcn36xx_hal_del_ba_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* TID for which the BA session is being deleted */ + u8 tid; + + /* DELBA direction + 1 - Originator + 0 - Recipient */ + u8 direction; +} __packed; + +struct wcn36xx_hal_del_ba_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct tsm_stats_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Traffic Id */ + u8 tid; + + u8 bssid[ETH_ALEN]; +}; + +struct tsm_stats_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /*success or failure */ + u32 status; + + /* Uplink Packet Queue delay */ + u16 uplink_pkt_queue_delay; + + /* Uplink Packet Queue delay histogram */ + u16 uplink_pkt_queue_delay_hist[4]; + + /* Uplink Packet Transmit delay */ + u32 uplink_pkt_tx_delay; + + /* Uplink Packet loss */ + u16 uplink_pkt_loss; + + /* Uplink Packet count */ + u16 uplink_pkt_count; + + /* Roaming count */ + u8 roaming_count; + + /* Roaming Delay */ + u16 roaming_delay; +}; + +struct set_key_done_msg { + struct wcn36xx_hal_msg_header header; + + /*bssid of the keys */ + u8 bssidx; + u8 enc_type; +}; + +struct wcn36xx_hal_nv_img_download_req_msg { + /* Note: The length specified in wcn36xx_hal_nv_img_download_req_msg + * messages should be + * header.len = sizeof(wcn36xx_hal_nv_img_download_req_msg) + + * nv_img_buffer_size */ + struct wcn36xx_hal_msg_header header; + + /* Fragment sequence number of the NV Image. Note that NV Image + * might not fit into one message due to size limitation of the SMD + * channel FIFO. UMAC can hence choose to chop the NV blob into + * multiple fragments starting with seqeunce number 0, 1, 2 etc. + * The last fragment MUST be indicated by marking the + * isLastFragment field to 1. Note that all the NV blobs would be + * concatenated together by HAL without any padding bytes in + * between.*/ + u16 frag_number; + + /* Is this the last fragment? When set to 1 it indicates that no + * more fragments will be sent by UMAC and HAL can concatenate all + * the NV blobs rcvd & proceed with the parsing. HAL would generate + * a WCN36XX_HAL_DOWNLOAD_NV_RSP to the WCN36XX_HAL_DOWNLOAD_NV_REQ + * after it receives each fragment */ + u16 last_fragment; + + /* NV Image size (number of bytes) */ + u32 nv_img_buffer_size; + + /* Following the 'nv_img_buffer_size', there should be + * nv_img_buffer_size bytes of NV Image i.e. + * u8[nv_img_buffer_size] */ +} __packed; + +struct wcn36xx_hal_nv_img_download_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure. HAL would generate a + * WCN36XX_HAL_DOWNLOAD_NV_RSP after each fragment */ + u32 status; +} __packed; + +struct wcn36xx_hal_nv_store_ind { + /* Note: The length specified in tHalNvStoreInd messages should be + * header.msgLen = sizeof(tHalNvStoreInd) + nvBlobSize */ + struct wcn36xx_hal_msg_header header; + + /* NV Item */ + u32 table_id; + + /* Size of NV Blob */ + u32 nv_blob_size; + + /* Following the 'nvBlobSize', there should be nvBlobSize bytes of + * NV blob i.e. u8[nvBlobSize] */ +}; + +/* End of Block Ack Related Parameters */ + +#define WCN36XX_HAL_CIPHER_SEQ_CTR_SIZE 6 + +/* Definition for MIC failure indication MAC reports this each time a MIC + * failure occures on Rx TKIP packet + */ +struct mic_failure_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 bssid[ETH_ALEN]; + + /* address used to compute MIC */ + u8 src_addr[ETH_ALEN]; + + /* transmitter address */ + u8 ta_addr[ETH_ALEN]; + + u8 dst_addr[ETH_ALEN]; + + u8 multicast; + + /* first byte of IV */ + u8 iv1; + + /* second byte of IV */ + u8 key_id; + + /* sequence number */ + u8 tsc[WCN36XX_HAL_CIPHER_SEQ_CTR_SIZE]; + + /* receive address */ + u8 rx_addr[ETH_ALEN]; +}; + +struct update_vht_op_mode_req_msg { + struct wcn36xx_hal_msg_header header; + + u16 op_mode; + u16 sta_id; +}; + +struct update_vht_op_mode_params_rsp_msg { + struct wcn36xx_hal_msg_header header; + + u32 status; +}; + +struct update_beacon_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; + + /* shortPreamble mode. HAL should update all the STA rates when it + * receives this message */ + u8 short_preamble; + + /* short Slot time. */ + u8 short_slot_time; + + /* Beacon Interval */ + u16 beacon_interval; + + /* Protection related */ + u8 lla_coexist; + u8 llb_coexist; + u8 llg_coexist; + u8 ht20_coexist; + u8 lln_non_gf_coexist; + u8 lsig_tx_op_protection_full_support; + u8 rifs_mode; + + u16 param_change_bitmap; +}; + +struct update_beacon_rsp_msg { + struct wcn36xx_hal_msg_header header; + u32 status; +}; + +struct wcn36xx_hal_send_beacon_req_msg { + struct wcn36xx_hal_msg_header header; + + /* length of the template. */ + u32 beacon_length; + + /* Beacon data. */ + u8 beacon[BEACON_TEMPLATE_SIZE]; + + u8 bssid[ETH_ALEN]; + + /* TIM IE offset from the beginning of the template. */ + u32 tim_ie_offset; + + /* P2P IE offset from the begining of the template */ + u16 p2p_ie_offset; +} __packed; + +struct send_beacon_rsp_msg { + struct wcn36xx_hal_msg_header header; + u32 status; +} __packed; + +struct enable_radar_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bssid[ETH_ALEN]; + u8 channel; +}; + +struct enable_radar_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Link Parameters */ + u8 bssid[ETH_ALEN]; + + /* success or failure */ + u32 status; +}; + +struct radar_detect_intr_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 radar_det_channel; +}; + +struct radar_detect_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* channel number in which the RADAR detected */ + u8 channel_number; + + /* RADAR pulse width in usecond */ + u16 radar_pulse_width; + + /* Number of RADAR pulses */ + u16 num_radar_pulse; +}; + +struct wcn36xx_hal_get_tpc_report_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 sta[ETH_ALEN]; + u8 dialog_token; + u8 txpower; +}; + +struct wcn36xx_hal_get_tpc_report_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_send_probe_resp_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 probe_resp_template[BEACON_TEMPLATE_SIZE]; + u32 probe_resp_template_len; + u32 proxy_probe_req_valid_ie_bmap[8]; + u8 bssid[ETH_ALEN]; +}; + +struct send_probe_resp_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct send_unknown_frame_rx_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_delete_sta_context_ind_msg { + struct wcn36xx_hal_msg_header header; + + u16 aid; + u16 sta_id; + + /* TO SUPPORT BT-AMP */ + u8 bssid[ETH_ALEN]; + + /* HAL copies bssid from the sta table. */ + u8 addr2[ETH_ALEN]; + + /* To unify the keepalive / unknown A2 / tim-based disa */ + u16 reason_code; +} __packed; + +struct indicate_del_sta { + struct wcn36xx_hal_msg_header header; + u8 aid; + u8 sta_index; + u8 bss_index; + u8 reason_code; + u32 status; +}; + +struct bt_amp_event_msg { + struct wcn36xx_hal_msg_header header; + + enum bt_amp_event_type btAmpEventType; +}; + +struct bt_amp_event_rsp { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct tl_hal_flush_ac_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index. originates from HAL */ + u8 sta_id; + + /* TID for which the transmit queue is being flushed */ + u8 tid; +}; + +struct tl_hal_flush_ac_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index. originates from HAL */ + u8 sta_id; + + /* TID for which the transmit queue is being flushed */ + u8 tid; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_enter_imps_req_msg { + struct wcn36xx_hal_msg_header header; +}; + +struct wcn36xx_hal_exit_imps_req { + struct wcn36xx_hal_msg_header header; +}; + +struct wcn36xx_hal_enter_bmps_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; + + /* TBTT value derived from the last beacon */ +#ifndef BUILD_QWPTTSTATIC + u64 tbtt; +#endif + u8 dtim_count; + + /* DTIM period given to HAL during association may not be valid, if + * association is based on ProbeRsp instead of beacon. */ + u8 dtim_period; + + /* For CCX and 11R Roaming */ + u32 rssi_filter_period; + + u32 num_beacon_per_rssi_average; + u8 rssi_filter_enable; +} __packed; + +struct wcn36xx_hal_exit_bmps_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 send_data_null; + u8 bss_index; +} __packed; + +struct wcn36xx_hal_missed_beacon_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; +} __packed; + +/* Beacon Filtering data structures */ + +/* The above structure would be followed by multiple of below mentioned + * structure + */ +struct beacon_filter_ie { + u8 element_id; + u8 check_ie_presence; + u8 offset; + u8 value; + u8 bitmask; + u8 ref; +}; + +struct wcn36xx_hal_add_bcn_filter_req_msg { + struct wcn36xx_hal_msg_header header; + + u16 capability_info; + u16 capability_mask; + u16 beacon_interval; + u16 ie_num; + u8 bss_index; + u8 reserved; +}; + +struct wcn36xx_hal_rem_bcn_filter_req { + struct wcn36xx_hal_msg_header header; + + u8 ie_Count; + u8 rem_ie_id[1]; +}; + +#define WCN36XX_HAL_IPV4_ARP_REPLY_OFFLOAD 0 +#define WCN36XX_HAL_IPV6_NEIGHBOR_DISCOVERY_OFFLOAD 1 +#define WCN36XX_HAL_IPV6_NS_OFFLOAD 2 +#define WCN36XX_HAL_IPV6_ADDR_LEN 16 +#define WCN36XX_HAL_OFFLOAD_DISABLE 0 +#define WCN36XX_HAL_OFFLOAD_ENABLE 1 +#define WCN36XX_HAL_OFFLOAD_BCAST_FILTER_ENABLE 0x2 +#define WCN36XX_HAL_OFFLOAD_ARP_AND_BCAST_FILTER_ENABLE \ + (HAL_OFFLOAD_ENABLE|HAL_OFFLOAD_BCAST_FILTER_ENABLE) + +struct wcn36xx_hal_ns_offload_params { + u8 src_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN]; + u8 self_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN]; + + /* Only support 2 possible Network Advertisement IPv6 address */ + u8 target_ipv6_addr1[WCN36XX_HAL_IPV6_ADDR_LEN]; + u8 target_ipv6_addr2[WCN36XX_HAL_IPV6_ADDR_LEN]; + + u8 self_addr[ETH_ALEN]; + u8 src_ipv6_addr_valid:1; + u8 target_ipv6_addr1_valid:1; + u8 target_ipv6_addr2_valid:1; + u8 reserved1:5; + + /* make it DWORD aligned */ + u8 reserved2; + + /* slot index for this offload */ + u32 slot_index; + u8 bss_index; +}; + +struct wcn36xx_hal_host_offload_req { + u8 offload_Type; + + /* enable or disable */ + u8 enable; + + union { + u8 host_ipv4_addr[4]; + u8 host_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN]; + } u; +}; + +struct wcn36xx_hal_host_offload_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_host_offload_req host_offload_params; + struct wcn36xx_hal_ns_offload_params ns_offload_params; +}; + +/* Packet Types. */ +#define WCN36XX_HAL_KEEP_ALIVE_NULL_PKT 1 +#define WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP 2 + +/* Enable or disable keep alive */ +#define WCN36XX_HAL_KEEP_ALIVE_DISABLE 0 +#define WCN36XX_HAL_KEEP_ALIVE_ENABLE 1 +#define WCN36XX_KEEP_ALIVE_TIME_PERIOD 30 /* unit: s */ + +/* Keep Alive request. */ +struct wcn36xx_hal_keep_alive_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 packet_type; + u32 time_period; + u8 host_ipv4_addr[WCN36XX_HAL_IPV4_ADDR_LEN]; + u8 dest_ipv4_addr[WCN36XX_HAL_IPV4_ADDR_LEN]; + u8 dest_addr[ETH_ALEN]; + u8 bss_index; +} __packed; + +struct wcn36xx_hal_rssi_threshold_req_msg { + struct wcn36xx_hal_msg_header header; + + s8 threshold1:8; + s8 threshold2:8; + s8 threshold3:8; + u8 thres1_pos_notify:1; + u8 thres1_neg_notify:1; + u8 thres2_pos_notify:1; + u8 thres2_neg_notify:1; + u8 thres3_pos_notify:1; + u8 thres3_neg_notify:1; + u8 reserved10:2; +}; + +struct wcn36xx_hal_enter_uapsd_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bk_delivery:1; + u8 be_delivery:1; + u8 vi_delivery:1; + u8 vo_delivery:1; + u8 bk_trigger:1; + u8 be_trigger:1; + u8 vi_trigger:1; + u8 vo_trigger:1; + u8 bss_index; +}; + +struct wcn36xx_hal_exit_uapsd_req_msg { + struct wcn36xx_hal_msg_header header; + u8 bss_index; +}; + +#define WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE 128 +#define WCN36XX_HAL_WOWL_BCAST_MAX_NUM_PATTERNS 16 + +struct wcn36xx_hal_wowl_add_bcast_ptrn_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Pattern ID */ + u8 id; + + /* Pattern byte offset from beginning of the 802.11 packet to start + * of the wake-up pattern */ + u8 byte_Offset; + + /* Non-Zero Pattern size */ + u8 size; + + /* Pattern */ + u8 pattern[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + /* Non-zero pattern mask size */ + u8 mask_size; + + /* Pattern mask */ + u8 mask[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + /* Extra pattern */ + u8 extra[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + /* Extra pattern mask */ + u8 mask_extra[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + u8 bss_index; +}; + +struct wcn36xx_hal_wow_del_bcast_ptrn_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Pattern ID of the wakeup pattern to be deleted */ + u8 id; + u8 bss_index; +}; + +struct wcn36xx_hal_wowl_enter_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Enables/disables magic packet filtering */ + u8 magic_packet_enable; + + /* Magic pattern */ + u8 magic_pattern[ETH_ALEN]; + + /* Enables/disables packet pattern filtering in firmware. Enabling + * this flag enables broadcast pattern matching in Firmware. If + * unicast pattern matching is also desired, + * ucUcastPatternFilteringEnable flag must be set tot true as well + */ + u8 pattern_filtering_enable; + + /* Enables/disables unicast packet pattern filtering. This flag + * specifies whether we want to do pattern match on unicast packets + * as well and not just broadcast packets. This flag has no effect + * if the ucPatternFilteringEnable (main controlling flag) is set + * to false + */ + u8 ucast_pattern_filtering_enable; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it receives the Channel Switch + * Action Frame. + */ + u8 wow_channel_switch_receive; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it receives the + * Deauthentication Frame. + */ + u8 wow_deauth_receive; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it receives the Disassociation + * Frame. + */ + u8 wow_disassoc_receive; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it has missed consecutive + * beacons. This is a hardware register configuration (NOT a + * firmware configuration). + */ + u8 wow_max_missed_beacons; + + /* This configuration is valid only when magicPktEnable=1. This is + * a timeout value in units of microsec. It requests hardware to + * unconditionally wake up after it has stayed in WoWLAN mode for + * some time. Set 0 to disable this feature. + */ + u8 wow_max_sleep; + + /* This configuration directs the WoW packet filtering to look for + * EAP-ID requests embedded in EAPOL frames and use this as a wake + * source. + */ + u8 wow_eap_id_request_enable; + + /* This configuration directs the WoW packet filtering to look for + * EAPOL-4WAY requests and use this as a wake source. + */ + u8 wow_eapol_4way_enable; + + /* This configuration allows a host wakeup on an network scan + * offload match. + */ + u8 wow_net_scan_offload_match; + + /* This configuration allows a host wakeup on any GTK rekeying + * error. + */ + u8 wow_gtk_rekey_error; + + /* This configuration allows a host wakeup on BSS connection loss. + */ + u8 wow_bss_connection_loss; + + u8 bss_index; +}; + +struct wcn36xx_hal_wowl_exit_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; +}; + +struct wcn36xx_hal_get_rssi_req_msg { + struct wcn36xx_hal_msg_header header; +}; + +struct wcn36xx_hal_get_roam_rssi_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Valid STA Idx for per STA stats request */ + u32 sta_id; +}; + +struct wcn36xx_hal_set_uapsd_ac_params_req_msg { + struct wcn36xx_hal_msg_header header; + + /* STA index */ + u8 sta_idx; + + /* Access Category */ + u8 ac; + + /* User Priority */ + u8 up; + + /* Service Interval */ + u32 service_interval; + + /* Suspend Interval */ + u32 suspend_interval; + + /* Delay Interval */ + u32 delay_interval; +}; + +struct wcn36xx_hal_configure_rxp_filter_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 set_mcst_bcst_filter_setting; + u8 set_mcst_bcst_filter; +}; + +struct wcn36xx_hal_enter_imps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_exit_imps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_enter_bmps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +} __packed; + +struct wcn36xx_hal_exit_bmps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +} __packed; + +struct wcn36xx_hal_enter_uapsd_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_exit_uapsd_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_rssi_notification_ind_msg { + struct wcn36xx_hal_msg_header header; + + u32 rssi_thres1_pos_cross:1; + u32 rssi_thres1_neg_cross:1; + u32 rssi_thres2_pos_cross:1; + u32 rssi_thres2_neg_cross:1; + u32 rssi_thres3_pos_cross:1; + u32 rssi_thres3_neg_cross:1; + u32 avg_rssi:8; + u32 reserved:18; + +}; + +struct wcn36xx_hal_get_rssio_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + s8 rssi; + +}; + +struct wcn36xx_hal_get_roam_rssi_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 sta_id; + s8 rssi; +}; + +struct wcn36xx_hal_wowl_enter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_wowl_exit_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_add_bcn_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_rem_bcn_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_add_wowl_bcast_ptrn_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_del_wowl_bcast_ptrn_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_host_offload_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_keep_alive_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_set_rssi_thresh_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_set_uapsd_ac_params_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_configure_rxp_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct set_max_tx_pwr_req { + struct wcn36xx_hal_msg_header header; + + /* BSSID is needed to identify which session issued this request. + * As the request has power constraints, this should be applied + * only to that session */ + u8 bssid[ETH_ALEN]; + + u8 self_addr[ETH_ALEN]; + + /* In request, power == MaxTx power to be used. */ + u8 power; +}; + +struct set_max_tx_pwr_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* power == tx power used for management frames */ + u8 power; + + /* success or failure */ + u32 status; +}; + +struct set_tx_pwr_req_msg { + struct wcn36xx_hal_msg_header header; + + /* TX Power in milli watts */ + u32 tx_power; + + u8 bss_index; +}; + +struct set_tx_pwr_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct get_tx_pwr_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 sta_id; +}; + +struct get_tx_pwr_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* TX Power in milli watts */ + u32 tx_power; +}; + +struct set_p2p_gonoa_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 opp_ps; + u32 ct_window; + u8 count; + u32 duration; + u32 interval; + u32 single_noa_duration; + u8 ps_selection; +}; + +struct set_p2p_gonoa_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_add_sta_self_req { + struct wcn36xx_hal_msg_header header; + + u8 self_addr[ETH_ALEN]; + u32 status; +} __packed; + +struct wcn36xx_hal_add_sta_self_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Self STA Index */ + u8 self_sta_index; + + /* DPU Index (IGTK, PTK, GTK all same) */ + u8 dpu_index; + + /* DPU Signature */ + u8 dpu_signature; +} __packed; + +struct wcn36xx_hal_del_sta_self_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 self_addr[ETH_ALEN]; +} __packed; + +struct wcn36xx_hal_del_sta_self_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /*success or failure */ + u32 status; + + u8 self_addr[ETH_ALEN]; +} __packed; + +struct aggr_add_ts_req { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_idx; + + /* TSPEC handler uniquely identifying a TSPEC for a STA in a BSS. + * This will carry the bitmap with the bit positions representing + * different AC.s */ + u16 tspec_index; + + /* Tspec info per AC To program TPE with required parameters */ + struct wcn36xx_hal_tspec_ie tspec[WCN36XX_HAL_MAX_AC]; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* These parameters are for all the access categories */ + + /* Service Interval */ + u32 service_interval[WCN36XX_HAL_MAX_AC]; + + /* Suspend Interval */ + u32 suspend_interval[WCN36XX_HAL_MAX_AC]; + + /* Delay Interval */ + u32 delay_interval[WCN36XX_HAL_MAX_AC]; +}; + +struct aggr_add_ts_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status0; + + /* FIXME PRIMA for future use for 11R */ + u32 status1; +}; + +struct wcn36xx_hal_configure_apps_cpu_wakeup_state_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 is_apps_cpu_awake; +}; + +struct wcn36xx_hal_configure_apps_cpu_wakeup_state_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_dump_cmd_req_msg { + struct wcn36xx_hal_msg_header header; + + u32 arg1; + u32 arg2; + u32 arg3; + u32 arg4; + u32 arg5; +} __packed; + +struct wcn36xx_hal_dump_cmd_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Length of the responce message */ + u32 rsp_length; + + /* FIXME: Currently considering the the responce will be less than + * 100bytes */ + u8 rsp_buffer[DUMPCMD_RSP_BUFFER]; +} __packed; + +#define WLAN_COEX_IND_DATA_SIZE (4) +#define WLAN_COEX_IND_TYPE_DISABLE_HB_MONITOR (0) +#define WLAN_COEX_IND_TYPE_ENABLE_HB_MONITOR (1) + +struct coex_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* Coex Indication Type */ + u32 type; + + /* Coex Indication Data */ + u32 data[WLAN_COEX_IND_DATA_SIZE]; +}; + +struct wcn36xx_hal_tx_compl_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* Tx Complete Indication Success or Failure */ + u32 status; +}; + +struct wcn36xx_hal_wlan_host_suspend_ind_msg { + struct wcn36xx_hal_msg_header header; + + u32 configured_mcst_bcst_filter_setting; + u32 active_session_count; +}; + +struct wcn36xx_hal_wlan_exclude_unencrpted_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 dot11_exclude_unencrypted; + u8 bssid[ETH_ALEN]; +}; + +struct noa_attr_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 index; + u8 opp_ps_flag; + u16 ctwin; + + u16 noa1_interval_count; + u16 bss_index; + u32 noa1_duration; + u32 noa1_interval; + u32 noa1_starttime; + + u16 noa2_interval_count; + u16 reserved2; + u32 noa2_duration; + u32 noa2_interval; + u32 noa2_start_time; + + u32 status; +}; + +struct noa_start_ind_msg { + struct wcn36xx_hal_msg_header header; + + u32 status; + u32 bss_index; +}; + +struct wcn36xx_hal_wlan_host_resume_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 configured_mcst_bcst_filter_setting; +}; + +struct wcn36xx_hal_host_resume_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_del_ba_ind_msg { + struct wcn36xx_hal_msg_header header; + + u16 sta_idx; + + /* Peer MAC Address, whose BA session has timed out */ + u8 peer_addr[ETH_ALEN]; + + /* TID for which a BA session timeout is being triggered */ + u8 ba_tid; + + /* DELBA direction + * 1 - Originator + * 0 - Recipient + */ + u8 direction; + + u32 reason_code; + + /* TO SUPPORT BT-AMP */ + u8 bssid[ETH_ALEN]; +}; + +/* PNO Messages */ + +/* Max number of channels that a network can be found on */ +#define WCN36XX_HAL_PNO_MAX_NETW_CHANNELS 26 + +/* Max number of channels that a network can be found on */ +#define WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX 60 + +/* Maximum numbers of networks supported by PNO */ +#define WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS 16 + +/* The number of scan time intervals that can be programmed into PNO */ +#define WCN36XX_HAL_PNO_MAX_SCAN_TIMERS 10 + +/* Maximum size of the probe template */ +#define WCN36XX_HAL_PNO_MAX_PROBE_SIZE 450 + +/* Type of PNO enabling: + * + * Immediate - scanning will start immediately and PNO procedure will be + * repeated based on timer + * + * Suspend - scanning will start at suspend + * + * Resume - scanning will start on system resume + */ +enum pno_mode { + PNO_MODE_IMMEDIATE, + PNO_MODE_ON_SUSPEND, + PNO_MODE_ON_RESUME, + PNO_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Authentication type */ +enum auth_type { + AUTH_TYPE_ANY = 0, + AUTH_TYPE_OPEN_SYSTEM = 1, + + /* Upper layer authentication types */ + AUTH_TYPE_WPA = 2, + AUTH_TYPE_WPA_PSK = 3, + + AUTH_TYPE_RSN = 4, + AUTH_TYPE_RSN_PSK = 5, + AUTH_TYPE_FT_RSN = 6, + AUTH_TYPE_FT_RSN_PSK = 7, + AUTH_TYPE_WAPI_WAI_CERTIFICATE = 8, + AUTH_TYPE_WAPI_WAI_PSK = 9, + + AUTH_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Encryption type */ +enum ed_type { + ED_ANY = 0, + ED_NONE = 1, + ED_WEP = 2, + ED_TKIP = 3, + ED_CCMP = 4, + ED_WPI = 5, + + ED_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* SSID broadcast type */ +enum ssid_bcast_type { + BCAST_UNKNOWN = 0, + BCAST_NORMAL = 1, + BCAST_HIDDEN = 2, + + BCAST_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* The network description for which PNO will have to look for */ +struct network_type { + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* Authentication type for the network */ + enum auth_type authentication; + + /* Encryption type for the network */ + enum ed_type encryption; + + /* Indicate the channel on which the Network can be found 0 - if + * all channels */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS]; + + /* Indicates the RSSI threshold for the network to be considered */ + u8 rssi_threshold; +}; + +struct scan_timer { + /* How much it should wait */ + u32 value; + + /* How many times it should repeat that wait value 0 - keep using + * this timer until PNO is disabled */ + u32 repeat; + + /* e.g: 2 3 4 0 - it will wait 2s between consecutive scans for 3 + * times - after that it will wait 4s between consecutive scans + * until disabled */ +}; + +/* The network parameters to be sent to the PNO algorithm */ +struct scan_timers_type { + /* set to 0 if you wish for PNO to use its default telescopic timer */ + u8 count; + + /* A set value represents the amount of time that PNO will wait + * between two consecutive scan procedures If the desired is for a + * uniform timer that fires always at the exact same interval - one + * single value is to be set If there is a desire for a more + * complex - telescopic like timer multiple values can be set - + * once PNO reaches the end of the array it will continue scanning + * at intervals presented by the last value */ + struct scan_timer values[WCN36XX_HAL_PNO_MAX_SCAN_TIMERS]; +}; + +/* Preferred network list request */ +struct set_pref_netw_list_req { + struct wcn36xx_hal_msg_header header; + + /* Enable PNO */ + u32 enable; + + /* Immediate, On Suspend, On Resume */ + enum pno_mode mode; + + /* Number of networks sent for PNO */ + u32 networks_count; + + /* The networks that PNO needs to look for */ + struct network_type networks[WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS]; + + /* The scan timers required for PNO */ + struct scan_timers_type scan_timers; + + /* Probe template for 2.4GHz band */ + u16 band_24g_probe_size; + u8 band_24g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; + + /* Probe template for 5GHz band */ + u16 band_5g_probe_size; + u8 band_5g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; +}; + +/* The network description for which PNO will have to look for */ +struct network_type_new { + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* Authentication type for the network */ + enum auth_type authentication; + + /* Encryption type for the network */ + enum ed_type encryption; + + /* SSID broadcast type, normal, hidden or unknown */ + enum ssid_bcast_type bcast_network_type; + + /* Indicate the channel on which the Network can be found 0 - if + * all channels */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS]; + + /* Indicates the RSSI threshold for the network to be considered */ + u8 rssi_threshold; +}; + +/* Preferred network list request new */ +struct set_pref_netw_list_req_new { + struct wcn36xx_hal_msg_header header; + + /* Enable PNO */ + u32 enable; + + /* Immediate, On Suspend, On Resume */ + enum pno_mode mode; + + /* Number of networks sent for PNO */ + u32 networks_count; + + /* The networks that PNO needs to look for */ + struct network_type_new networks[WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS]; + + /* The scan timers required for PNO */ + struct scan_timers_type scan_timers; + + /* Probe template for 2.4GHz band */ + u16 band_24g_probe_size; + u8 band_24g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; + + /* Probe template for 5GHz band */ + u16 band_5g_probe_size; + u8 band_5g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; +}; + +/* Preferred network list response */ +struct set_pref_netw_list_resp { + struct wcn36xx_hal_msg_header header; + + /* status of the request - just to indicate that PNO has + * acknowledged the request and will start scanning */ + u32 status; +}; + +/* Preferred network found indication */ +struct pref_netw_found_ind { + + struct wcn36xx_hal_msg_header header; + + /* Network that was found with the highest RSSI */ + struct wcn36xx_hal_mac_ssid ssid; + + /* Indicates the RSSI */ + u8 rssi; +}; + +/* RSSI Filter request */ +struct set_rssi_filter_req { + struct wcn36xx_hal_msg_header header; + + /* RSSI Threshold */ + u8 rssi_threshold; +}; + +/* Set RSSI filter resp */ +struct set_rssi_filter_resp { + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +}; + +/* Update scan params - sent from host to PNO to be used during PNO + * scanningx */ +struct wcn36xx_hal_update_scan_params_req { + + struct wcn36xx_hal_msg_header header; + + /* Host setting for 11d */ + u8 dot11d_enabled; + + /* Lets PNO know that host has determined the regulatory domain */ + u8 dot11d_resolved; + + /* Channels on which PNO is allowed to scan */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS]; + + /* Minimum channel time */ + u16 active_min_ch_time; + + /* Maximum channel time */ + u16 active_max_ch_time; + + /* Minimum channel time */ + u16 passive_min_ch_time; + + /* Maximum channel time */ + u16 passive_max_ch_time; + + /* Cb State */ + enum phy_chan_bond_state state; +} __packed; + +/* Update scan params - sent from host to PNO to be used during PNO + * scanningx */ +struct update_scan_params_req_ex { + + struct wcn36xx_hal_msg_header header; + + /* Host setting for 11d */ + u8 dot11d_enabled; + + /* Lets PNO know that host has determined the regulatory domain */ + u8 dot11d_resolved; + + /* Channels on which PNO is allowed to scan */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; + + /* Minimum channel time */ + u16 active_min_ch_time; + + /* Maximum channel time */ + u16 active_max_ch_time; + + /* Minimum channel time */ + u16 passive_min_ch_time; + + /* Maximum channel time */ + u16 passive_max_ch_time; + + /* Cb State */ + enum phy_chan_bond_state state; +}; + +/* Update scan params - sent from host to PNO to be used during PNO + * scanningx */ +struct wcn36xx_hal_update_scan_params_resp { + + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +} __packed; + +struct wcn36xx_hal_set_tx_per_tracking_req_msg { + struct wcn36xx_hal_msg_header header; + + /* 0: disable, 1:enable */ + u8 tx_per_tracking_enable; + + /* Check period, unit is sec. */ + u8 tx_per_tracking_period; + + /* (Fail TX packet)/(Total TX packet) ratio, the unit is 10%. */ + u8 tx_per_tracking_ratio; + + /* A watermark of check number, once the tx packet exceed this + * number, we do the check, default is 5 */ + u32 tx_per_tracking_watermark; +}; + +struct wcn36xx_hal_set_tx_per_tracking_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +}; + +struct tx_per_hit_ind_msg { + struct wcn36xx_hal_msg_header header; +}; + +/* Packet Filtering Definitions Begin */ +#define WCN36XX_HAL_PROTOCOL_DATA_LEN 8 +#define WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS 240 +#define WCN36XX_HAL_MAX_NUM_FILTERS 20 +#define WCN36XX_HAL_MAX_CMP_PER_FILTER 10 + +enum wcn36xx_hal_receive_packet_filter_type { + HAL_RCV_FILTER_TYPE_INVALID, + HAL_RCV_FILTER_TYPE_FILTER_PKT, + HAL_RCV_FILTER_TYPE_BUFFER_PKT, + HAL_RCV_FILTER_TYPE_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_rcv_pkt_flt_protocol_type { + HAL_FILTER_PROTO_TYPE_INVALID, + HAL_FILTER_PROTO_TYPE_MAC, + HAL_FILTER_PROTO_TYPE_ARP, + HAL_FILTER_PROTO_TYPE_IPV4, + HAL_FILTER_PROTO_TYPE_IPV6, + HAL_FILTER_PROTO_TYPE_UDP, + HAL_FILTER_PROTO_TYPE_MAX +}; + +enum wcn36xx_hal_rcv_pkt_flt_cmp_flag_type { + HAL_FILTER_CMP_TYPE_INVALID, + HAL_FILTER_CMP_TYPE_EQUAL, + HAL_FILTER_CMP_TYPE_MASK_EQUAL, + HAL_FILTER_CMP_TYPE_NOT_EQUAL, + HAL_FILTER_CMP_TYPE_MAX +}; + +struct wcn36xx_hal_rcv_pkt_filter_params { + u8 protocol_layer; + u8 cmp_flag; + + /* Length of the data to compare */ + u16 data_length; + + /* from start of the respective frame header */ + u8 data_offset; + + /* Reserved field */ + u8 reserved; + + /* Data to compare */ + u8 compare_data[WCN36XX_HAL_PROTOCOL_DATA_LEN]; + + /* Mask to be applied on the received packet data before compare */ + u8 data_mask[WCN36XX_HAL_PROTOCOL_DATA_LEN]; +}; + +struct wcn36xx_hal_sessionized_rcv_pkt_filter_cfg_type { + u8 id; + u8 type; + u8 params_count; + u32 coleasce_time; + u8 bss_index; + struct wcn36xx_hal_rcv_pkt_filter_params params[1]; +}; + +struct wcn36xx_hal_set_rcv_pkt_filter_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 id; + u8 type; + u8 params_count; + u32 coalesce_time; + struct wcn36xx_hal_rcv_pkt_filter_params params[1]; +}; + +struct wcn36xx_hal_rcv_flt_mc_addr_list_type { + /* from start of the respective frame header */ + u8 data_offset; + + u32 mc_addr_count; + u8 mc_addr[ETH_ALEN][WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS]; + u8 bss_index; +}; + +struct wcn36xx_hal_set_pkt_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_match_cnt_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_match_cnt { + u8 id; + u32 match_cnt; +}; + +struct wcn36xx_hal_rcv_flt_pkt_match_cnt_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure */ + u32 status; + + u32 match_count; + struct wcn36xx_hal_rcv_flt_pkt_match_cnt + matches[WCN36XX_HAL_MAX_NUM_FILTERS]; + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_clear_param { + /* only valid for response message */ + u32 status; + u8 id; + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_clear_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_rcv_flt_pkt_clear_param param; +}; + +struct wcn36xx_hal_rcv_flt_pkt_clear_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_rcv_flt_pkt_clear_param param; +}; + +struct wcn36xx_hal_rcv_flt_pkt_set_mc_list_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_rcv_flt_mc_addr_list_type mc_addr_list; +}; + +struct wcn36xx_hal_rcv_flt_pkt_set_mc_list_rsp_msg { + struct wcn36xx_hal_msg_header header; + u32 status; + u8 bss_index; +}; + +/* Packet Filtering Definitions End */ + +struct wcn36xx_hal_set_power_params_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Ignore DTIM */ + u32 ignore_dtim; + + /* DTIM Period */ + u32 dtim_period; + + /* Listen Interval */ + u32 listen_interval; + + /* Broadcast Multicast Filter */ + u32 bcast_mcast_filter; + + /* Beacon Early Termination */ + u32 enable_bet; + + /* Beacon Early Termination Interval */ + u32 bet_interval; +} __packed; + +struct wcn36xx_hal_set_power_params_resp { + + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +} __packed; + +/* Capability bitmap exchange definitions and macros starts */ + +enum place_holder_in_cap_bitmap { + MCC = 0, + P2P = 1, + DOT11AC = 2, + SLM_SESSIONIZATION = 3, + DOT11AC_OPMODE = 4, + SAP32STA = 5, + TDLS = 6, + P2P_GO_NOA_DECOUPLE_INIT_SCAN = 7, + WLANACTIVE_OFFLOAD = 8, + BEACON_OFFLOAD = 9, + SCAN_OFFLOAD = 10, + ROAM_OFFLOAD = 11, + BCN_MISS_OFFLOAD = 12, + STA_POWERSAVE = 13, + STA_ADVANCED_PWRSAVE = 14, + AP_UAPSD = 15, + AP_DFS = 16, + BLOCKACK = 17, + PHY_ERR = 18, + BCN_FILTER = 19, + RTT = 20, + RATECTRL = 21, + WOW = 22, + MAX_FEATURE_SUPPORTED = 128, +}; + +#define WCN36XX_HAL_CAPS_SIZE 4 + +struct wcn36xx_hal_feat_caps_msg { + + struct wcn36xx_hal_msg_header header; + + u32 feat_caps[WCN36XX_HAL_CAPS_SIZE]; +} __packed; + +/* status codes to help debug rekey failures */ +enum gtk_rekey_status { + WCN36XX_HAL_GTK_REKEY_STATUS_SUCCESS = 0, + + /* rekey detected, but not handled */ + WCN36XX_HAL_GTK_REKEY_STATUS_NOT_HANDLED = 1, + + /* MIC check error on M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_MIC_ERROR = 2, + + /* decryption error on M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_DECRYPT_ERROR = 3, + + /* M1 replay detected */ + WCN36XX_HAL_GTK_REKEY_STATUS_REPLAY_ERROR = 4, + + /* missing GTK key descriptor in M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_MISSING_KDE = 5, + + /* missing iGTK key descriptor in M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_MISSING_IGTK_KDE = 6, + + /* key installation error */ + WCN36XX_HAL_GTK_REKEY_STATUS_INSTALL_ERROR = 7, + + /* iGTK key installation error */ + WCN36XX_HAL_GTK_REKEY_STATUS_IGTK_INSTALL_ERROR = 8, + + /* GTK rekey M2 response TX error */ + WCN36XX_HAL_GTK_REKEY_STATUS_RESP_TX_ERROR = 9, + + /* non-specific general error */ + WCN36XX_HAL_GTK_REKEY_STATUS_GEN_ERROR = 255 +}; + +/* wake reason types */ +enum wake_reason_type { + WCN36XX_HAL_WAKE_REASON_NONE = 0, + + /* magic packet match */ + WCN36XX_HAL_WAKE_REASON_MAGIC_PACKET = 1, + + /* host defined pattern match */ + WCN36XX_HAL_WAKE_REASON_PATTERN_MATCH = 2, + + /* EAP-ID frame detected */ + WCN36XX_HAL_WAKE_REASON_EAPID_PACKET = 3, + + /* start of EAPOL 4-way handshake detected */ + WCN36XX_HAL_WAKE_REASON_EAPOL4WAY_PACKET = 4, + + /* network scan offload match */ + WCN36XX_HAL_WAKE_REASON_NETSCAN_OFFL_MATCH = 5, + + /* GTK rekey status wakeup (see status) */ + WCN36XX_HAL_WAKE_REASON_GTK_REKEY_STATUS = 6, + + /* BSS connection lost */ + WCN36XX_HAL_WAKE_REASON_BSS_CONN_LOST = 7, +}; + +/* + Wake Packet which is saved at tWakeReasonParams.DataStart + This data is sent for any wake reasons that involve a packet-based wakeup : + + WCN36XX_HAL_WAKE_REASON_TYPE_MAGIC_PACKET + WCN36XX_HAL_WAKE_REASON_TYPE_PATTERN_MATCH + WCN36XX_HAL_WAKE_REASON_TYPE_EAPID_PACKET + WCN36XX_HAL_WAKE_REASON_TYPE_EAPOL4WAY_PACKET + WCN36XX_HAL_WAKE_REASON_TYPE_GTK_REKEY_STATUS + + The information is provided to the host for auditing and debug purposes + +*/ + +/* Wake reason indication */ +struct wcn36xx_hal_wake_reason_ind { + struct wcn36xx_hal_msg_header header; + + /* see tWakeReasonType */ + u32 reason; + + /* argument specific to the reason type */ + u32 reason_arg; + + /* length of optional data stored in this message, in case HAL + * truncates the data (i.e. data packets) this length will be less + * than the actual length */ + u32 stored_data_len; + + /* actual length of data */ + u32 actual_data_len; + + /* variable length start of data (length == storedDataLen) see + * specific wake type */ + u8 data_start[1]; + + u32 bss_index:8; + u32 reserved:24; +}; + +#define WCN36XX_HAL_GTK_KEK_BYTES 16 +#define WCN36XX_HAL_GTK_KCK_BYTES 16 + +#define WCN36XX_HAL_GTK_OFFLOAD_FLAGS_DISABLE (1 << 0) + +#define GTK_SET_BSS_KEY_TAG 0x1234AA55 + +struct wcn36xx_hal_gtk_offload_req_msg { + struct wcn36xx_hal_msg_header header; + + /* optional flags */ + u32 flags; + + /* Key confirmation key */ + u8 kck[WCN36XX_HAL_GTK_KCK_BYTES]; + + /* key encryption key */ + u8 kek[WCN36XX_HAL_GTK_KEK_BYTES]; + + /* replay counter */ + u64 key_replay_counter; + + u8 bss_index; +}; + +struct wcn36xx_hal_gtk_offload_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_gtk_offload_get_info_req_msg { + struct wcn36xx_hal_msg_header header; + u8 bss_index; +}; + +struct wcn36xx_hal_gtk_offload_get_info_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* last rekey status when the rekey was offloaded */ + u32 last_rekey_status; + + /* current replay counter value */ + u64 key_replay_counter; + + /* total rekey attempts */ + u32 total_rekey_count; + + /* successful GTK rekeys */ + u32 gtk_rekey_count; + + /* successful iGTK rekeys */ + u32 igtk_rekey_count; + + u8 bss_index; +}; + +struct dhcp_info { + /* Indicates the device mode which indicates about the DHCP activity */ + u8 device_mode; + + u8 addr[ETH_ALEN]; +}; + +struct dhcp_ind_status { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +/* + * Thermal Mitigation mode of operation. + * + * WCN36XX_HAL_THERMAL_MITIGATION_MODE_0 - Based on AMPDU disabling aggregation + * + * WCN36XX_HAL_THERMAL_MITIGATION_MODE_1 - Based on AMPDU disabling aggregation + * and reducing transmit power + * + * WCN36XX_HAL_THERMAL_MITIGATION_MODE_2 - Not supported */ +enum wcn36xx_hal_thermal_mitigation_mode_type { + HAL_THERMAL_MITIGATION_MODE_INVALID = -1, + HAL_THERMAL_MITIGATION_MODE_0, + HAL_THERMAL_MITIGATION_MODE_1, + HAL_THERMAL_MITIGATION_MODE_2, + HAL_THERMAL_MITIGATION_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE, +}; + + +/* + * Thermal Mitigation level. + * Note the levels are incremental i.e WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_2 = + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_0 + + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_1 + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_0 - lowest level of thermal mitigation. + * This level indicates normal mode of operation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_1 - 1st level of thermal mitigation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_2 - 2nd level of thermal mitigation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_3 - 3rd level of thermal mitigation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_4 - 4th level of thermal mitigation + */ +enum wcn36xx_hal_thermal_mitigation_level_type { + HAL_THERMAL_MITIGATION_LEVEL_INVALID = -1, + HAL_THERMAL_MITIGATION_LEVEL_0, + HAL_THERMAL_MITIGATION_LEVEL_1, + HAL_THERMAL_MITIGATION_LEVEL_2, + HAL_THERMAL_MITIGATION_LEVEL_3, + HAL_THERMAL_MITIGATION_LEVEL_4, + HAL_THERMAL_MITIGATION_LEVEL_MAX = WCN36XX_HAL_MAX_ENUM_SIZE, +}; + + +/* WCN36XX_HAL_SET_THERMAL_MITIGATION_REQ */ +struct set_thermal_mitigation_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Thermal Mitigation Operation Mode */ + enum wcn36xx_hal_thermal_mitigation_mode_type mode; + + /* Thermal Mitigation Level */ + enum wcn36xx_hal_thermal_mitigation_level_type level; +}; + +struct set_thermal_mitigation_resp { + + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +}; + +/* Per STA Class B Statistics. Class B statistics are STA TX/RX stats + * provided to FW from Host via periodic messages */ +struct stats_class_b_ind { + struct wcn36xx_hal_msg_header header; + + /* Duration over which this stats was collected */ + u32 duration; + + /* Per STA Stats */ + + /* TX stats */ + u32 tx_bytes_pushed; + u32 tx_packets_pushed; + + /* RX stats */ + u32 rx_bytes_rcvd; + u32 rx_packets_rcvd; + u32 rx_time_total; +}; + +#endif /* _HAL_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c new file mode 100644 index 00000000000..4ab5370ab7a --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -0,0 +1,1099 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 <linux/platform_device.h> +#include "wcn36xx.h" + +unsigned int wcn36xx_dbg_mask; +module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); + +#define CHAN2G(_freq, _idx) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 25, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 25, \ +} + +/* The wcn firmware expects channel values to matching + * their mnemonic values. So use these for .hw_value. */ +static struct ieee80211_channel wcn_2ghz_channels[] = { + CHAN2G(2412, 1), /* Channel 1 */ + CHAN2G(2417, 2), /* Channel 2 */ + CHAN2G(2422, 3), /* Channel 3 */ + CHAN2G(2427, 4), /* Channel 4 */ + CHAN2G(2432, 5), /* Channel 5 */ + CHAN2G(2437, 6), /* Channel 6 */ + CHAN2G(2442, 7), /* Channel 7 */ + CHAN2G(2447, 8), /* Channel 8 */ + CHAN2G(2452, 9), /* Channel 9 */ + CHAN2G(2457, 10), /* Channel 10 */ + CHAN2G(2462, 11), /* Channel 11 */ + CHAN2G(2467, 12), /* Channel 12 */ + CHAN2G(2472, 13), /* Channel 13 */ + CHAN2G(2484, 14) /* Channel 14 */ + +}; + +static struct ieee80211_channel wcn_5ghz_channels[] = { + CHAN5G(5180, 36), + CHAN5G(5200, 40), + CHAN5G(5220, 44), + CHAN5G(5240, 48), + CHAN5G(5260, 52), + CHAN5G(5280, 56), + CHAN5G(5300, 60), + CHAN5G(5320, 64), + CHAN5G(5500, 100), + CHAN5G(5520, 104), + CHAN5G(5540, 108), + CHAN5G(5560, 112), + CHAN5G(5580, 116), + CHAN5G(5600, 120), + CHAN5G(5620, 124), + CHAN5G(5640, 128), + CHAN5G(5660, 132), + CHAN5G(5700, 140), + CHAN5G(5745, 149), + CHAN5G(5765, 153), + CHAN5G(5785, 157), + CHAN5G(5805, 161), + CHAN5G(5825, 165) +}; + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (_hw_rate) \ +} + +static struct ieee80211_rate wcn_2ghz_rates[] = { + RATE(10, HW_RATE_INDEX_1MBPS, 0), + RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, HW_RATE_INDEX_6MBPS, 0), + RATE(90, HW_RATE_INDEX_9MBPS, 0), + RATE(120, HW_RATE_INDEX_12MBPS, 0), + RATE(180, HW_RATE_INDEX_18MBPS, 0), + RATE(240, HW_RATE_INDEX_24MBPS, 0), + RATE(360, HW_RATE_INDEX_36MBPS, 0), + RATE(480, HW_RATE_INDEX_48MBPS, 0), + RATE(540, HW_RATE_INDEX_54MBPS, 0) +}; + +static struct ieee80211_rate wcn_5ghz_rates[] = { + RATE(60, HW_RATE_INDEX_6MBPS, 0), + RATE(90, HW_RATE_INDEX_9MBPS, 0), + RATE(120, HW_RATE_INDEX_12MBPS, 0), + RATE(180, HW_RATE_INDEX_18MBPS, 0), + RATE(240, HW_RATE_INDEX_24MBPS, 0), + RATE(360, HW_RATE_INDEX_36MBPS, 0), + RATE(480, HW_RATE_INDEX_48MBPS, 0), + RATE(540, HW_RATE_INDEX_54MBPS, 0) +}; + +static struct ieee80211_supported_band wcn_band_2ghz = { + .channels = wcn_2ghz_channels, + .n_channels = ARRAY_SIZE(wcn_2ghz_channels), + .bitrates = wcn_2ghz_rates, + .n_bitrates = ARRAY_SIZE(wcn_2ghz_rates), + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_LSIG_TXOP_PROT, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + } + } +}; + +static struct ieee80211_supported_band wcn_band_5ghz = { + .channels = wcn_5ghz_channels, + .n_channels = ARRAY_SIZE(wcn_5ghz_channels), + .bitrates = wcn_5ghz_rates, + .n_bitrates = ARRAY_SIZE(wcn_5ghz_rates), + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_LSIG_TXOP_PROT | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_SUP_WIDTH_20_40, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + } + } +}; + +#ifdef CONFIG_PM + +static const struct wiphy_wowlan_support wowlan_support = { + .flags = WIPHY_WOWLAN_ANY +}; + +#endif + +static inline u8 get_sta_index(struct ieee80211_vif *vif, + struct wcn36xx_sta *sta_priv) +{ + return NL80211_IFTYPE_STATION == vif->type ? + sta_priv->bss_sta_index : + sta_priv->sta_index; +} + +static const char * const wcn36xx_caps_names[] = { + "MCC", /* 0 */ + "P2P", /* 1 */ + "DOT11AC", /* 2 */ + "SLM_SESSIONIZATION", /* 3 */ + "DOT11AC_OPMODE", /* 4 */ + "SAP32STA", /* 5 */ + "TDLS", /* 6 */ + "P2P_GO_NOA_DECOUPLE_INIT_SCAN",/* 7 */ + "WLANACTIVE_OFFLOAD", /* 8 */ + "BEACON_OFFLOAD", /* 9 */ + "SCAN_OFFLOAD", /* 10 */ + "ROAM_OFFLOAD", /* 11 */ + "BCN_MISS_OFFLOAD", /* 12 */ + "STA_POWERSAVE", /* 13 */ + "STA_ADVANCED_PWRSAVE", /* 14 */ + "AP_UAPSD", /* 15 */ + "AP_DFS", /* 16 */ + "BLOCKACK", /* 17 */ + "PHY_ERR", /* 18 */ + "BCN_FILTER", /* 19 */ + "RTT", /* 20 */ + "RATECTRL", /* 21 */ + "WOW" /* 22 */ +}; + +static const char *wcn36xx_get_cap_name(enum place_holder_in_cap_bitmap x) +{ + if (x >= ARRAY_SIZE(wcn36xx_caps_names)) + return "UNKNOWN"; + return wcn36xx_caps_names[x]; +} + +static void wcn36xx_feat_caps_info(struct wcn36xx *wcn) +{ + int i; + + for (i = 0; i < MAX_FEATURE_SUPPORTED; i++) { + if (get_feat_caps(wcn->fw_feat_caps, i)) + wcn36xx_info("FW Cap %s\n", wcn36xx_get_cap_name(i)); + } +} + +static void wcn36xx_detect_chip_version(struct wcn36xx *wcn) +{ + if (get_feat_caps(wcn->fw_feat_caps, DOT11AC)) { + wcn36xx_info("Chip is 3680\n"); + wcn->chip_version = WCN36XX_CHIP_3680; + } else { + wcn36xx_info("Chip is 3660\n"); + wcn->chip_version = WCN36XX_CHIP_3660; + } +} + +static int wcn36xx_start(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + int ret; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n"); + + /* SMD initialization */ + ret = wcn36xx_smd_open(wcn); + if (ret) { + wcn36xx_err("Failed to open smd channel: %d\n", ret); + goto out_err; + } + + /* Allocate memory pools for Mgmt BD headers and Data BD headers */ + ret = wcn36xx_dxe_allocate_mem_pools(wcn); + if (ret) { + wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret); + goto out_smd_close; + } + + ret = wcn36xx_dxe_alloc_ctl_blks(wcn); + if (ret) { + wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret); + goto out_free_dxe_pool; + } + + wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL); + if (!wcn->hal_buf) { + wcn36xx_err("Failed to allocate smd buf\n"); + ret = -ENOMEM; + goto out_free_dxe_ctl; + } + + ret = wcn36xx_smd_load_nv(wcn); + if (ret) { + wcn36xx_err("Failed to push NV to chip\n"); + goto out_free_smd_buf; + } + + ret = wcn36xx_smd_start(wcn); + if (ret) { + wcn36xx_err("Failed to start chip\n"); + goto out_free_smd_buf; + } + + if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + ret = wcn36xx_smd_feature_caps_exchange(wcn); + if (ret) + wcn36xx_warn("Exchange feature caps failed\n"); + else + wcn36xx_feat_caps_info(wcn); + } + + wcn36xx_detect_chip_version(wcn); + + /* DMA channel initialization */ + ret = wcn36xx_dxe_init(wcn); + if (ret) { + wcn36xx_err("DXE init failed\n"); + goto out_smd_stop; + } + + wcn36xx_debugfs_init(wcn); + + INIT_LIST_HEAD(&wcn->vif_list); + return 0; + +out_smd_stop: + wcn36xx_smd_stop(wcn); +out_free_smd_buf: + kfree(wcn->hal_buf); +out_free_dxe_pool: + wcn36xx_dxe_free_mem_pools(wcn); +out_free_dxe_ctl: + wcn36xx_dxe_free_ctl_blks(wcn); +out_smd_close: + wcn36xx_smd_close(wcn); +out_err: + return ret; +} + +static void wcn36xx_stop(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); + + wcn36xx_debugfs_exit(wcn); + wcn36xx_smd_stop(wcn); + wcn36xx_dxe_deinit(wcn); + wcn36xx_smd_close(wcn); + + wcn36xx_dxe_free_mem_pools(wcn); + wcn36xx_dxe_free_ctl_blks(wcn); + + kfree(wcn->hal_buf); +} + +static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) +{ + struct wcn36xx *wcn = hw->priv; + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *tmp; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + int ch = WCN36XX_HW_CHANNEL(wcn); + wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", + ch); + list_for_each_entry(tmp, &wcn->vif_list, list) { + vif = container_of((void *)tmp, + struct ieee80211_vif, + drv_priv); + wcn36xx_smd_switch_channel(wcn, vif, ch); + } + } + + return 0; +} + +#define WCN36XX_SUPPORTED_FILTERS (0) + +static void wcn36xx_configure_filter(struct ieee80211_hw *hw, + unsigned int changed, + unsigned int *total, u64 multicast) +{ + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); + + *total &= WCN36XX_SUPPORTED_FILTERS; +} + +static void wcn36xx_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_sta *sta_priv = NULL; + + if (control->sta) + sta_priv = (struct wcn36xx_sta *)control->sta->drv_priv; + + if (wcn36xx_start_tx(wcn, sta_priv, skb)) + ieee80211_free_txskb(wcn->hw, skb); +} + +static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *sta_priv = vif_priv->sta; + int ret = 0; + u8 key[WLAN_MAX_KEY_LEN]; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n"); + wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n", + cmd, key_conf->cipher, key_conf->keyidx, + key_conf->keylen, key_conf->flags); + wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ", + key_conf->key, + key_conf->keylen); + + switch (key_conf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; + break; + case WLAN_CIPHER_SUITE_CCMP: + vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; + break; + case WLAN_CIPHER_SUITE_TKIP: + vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; + break; + default: + wcn36xx_err("Unsupported key type 0x%x\n", + key_conf->cipher); + ret = -EOPNOTSUPP; + goto out; + } + + switch (cmd) { + case SET_KEY: + if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { + /* + * Supplicant is sending key in the wrong order: + * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) + * but HW expects it to be in the order as described in + * IEEE 802.11 spec (see chapter 11.7) like this: + * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) + */ + memcpy(key, key_conf->key, 16); + memcpy(key + 16, key_conf->key + 24, 8); + memcpy(key + 24, key_conf->key + 16, 8); + } else { + memcpy(key, key_conf->key, key_conf->keylen); + } + + if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { + sta_priv->is_data_encrypted = true; + /* Reconfigure bss with encrypt_type */ + if (NL80211_IFTYPE_STATION == vif->type) + wcn36xx_smd_config_bss(wcn, + vif, + sta, + sta->addr, + true); + + wcn36xx_smd_set_stakey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + key_conf->keylen, + key, + get_sta_index(vif, sta_priv)); + } else { + wcn36xx_smd_set_bsskey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + key_conf->keylen, + key); + if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) || + (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) { + sta_priv->is_data_encrypted = true; + wcn36xx_smd_set_stakey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + key_conf->keylen, + key, + get_sta_index(vif, sta_priv)); + } + } + break; + case DISABLE_KEY: + if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { + wcn36xx_smd_remove_bsskey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx); + } else { + sta_priv->is_data_encrypted = false; + /* do not remove key if disassociated */ + if (sta_priv->aid) + wcn36xx_smd_remove_stakey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + get_sta_index(vif, sta_priv)); + } + break; + default: + wcn36xx_err("Unsupported key cmd 0x%x\n", cmd); + ret = -EOPNOTSUPP; + goto out; + break; + } + +out: + return ret; +} + +static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); + wcn36xx_smd_start_scan(wcn); +} + +static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_smd_end_scan(wcn); + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); +} + +static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, + enum ieee80211_band band) +{ + int i, size; + u16 *rates_table; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + u32 rates = sta->supp_rates[band]; + + memset(&sta_priv->supported_rates, 0, + sizeof(sta_priv->supported_rates)); + sta_priv->supported_rates.op_rate_mode = STA_11n; + + size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); + rates_table = sta_priv->supported_rates.dsss_rates; + if (band == IEEE80211_BAND_2GHZ) { + for (i = 0; i < size; i++) { + if (rates & 0x01) { + rates_table[i] = wcn_2ghz_rates[i].hw_value; + rates = rates >> 1; + } + } + } + + size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates); + rates_table = sta_priv->supported_rates.ofdm_rates; + for (i = 0; i < size; i++) { + if (rates & 0x01) { + rates_table[i] = wcn_5ghz_rates[i].hw_value; + rates = rates >> 1; + } + } + + if (sta->ht_cap.ht_supported) { + BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) > + sizeof(sta_priv->supported_rates.supported_mcs_set)); + memcpy(sta_priv->supported_rates.supported_mcs_set, + sta->ht_cap.mcs.rx_mask, + sizeof(sta->ht_cap.mcs.rx_mask)); + } +} +void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) +{ + u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { + HW_RATE_INDEX_6MBPS, + HW_RATE_INDEX_9MBPS, + HW_RATE_INDEX_12MBPS, + HW_RATE_INDEX_18MBPS, + HW_RATE_INDEX_24MBPS, + HW_RATE_INDEX_36MBPS, + HW_RATE_INDEX_48MBPS, + HW_RATE_INDEX_54MBPS + }; + u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = { + HW_RATE_INDEX_1MBPS, + HW_RATE_INDEX_2MBPS, + HW_RATE_INDEX_5_5MBPS, + HW_RATE_INDEX_11MBPS + }; + + rates->op_rate_mode = STA_11n; + memcpy(rates->dsss_rates, dsss_rates, + sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES); + memcpy(rates->ofdm_rates, ofdm_rates, + sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); + rates->supported_mcs_set[0] = 0xFF; +} +static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct wcn36xx *wcn = hw->priv; + struct sk_buff *skb = NULL; + u16 tim_off, tim_len; + enum wcn36xx_hal_link_state link_state; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n", + vif, changed); + + if (changed & BSS_CHANGED_BEACON_INFO) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss changed dtim period %d\n", + bss_conf->dtim_period); + + vif_priv->dtim_period = bss_conf->dtim_period; + } + + if (changed & BSS_CHANGED_PS) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss PS set %d\n", + bss_conf->ps); + if (bss_conf->ps) { + wcn36xx_pmc_enter_bmps_state(wcn, vif); + } else { + wcn36xx_pmc_exit_bmps_state(wcn, vif); + } + } + + if (changed & BSS_CHANGED_BSSID) { + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n", + bss_conf->bssid); + + if (!is_zero_ether_addr(bss_conf->bssid)) { + vif_priv->is_joining = true; + vif_priv->bss_index = 0xff; + wcn36xx_smd_join(wcn, bss_conf->bssid, + vif->addr, WCN36XX_HW_CHANNEL(wcn)); + wcn36xx_smd_config_bss(wcn, vif, NULL, + bss_conf->bssid, false); + } else { + vif_priv->is_joining = false; + wcn36xx_smd_delete_bss(wcn, vif); + } + } + + if (changed & BSS_CHANGED_SSID) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss changed ssid\n"); + wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ", + bss_conf->ssid, bss_conf->ssid_len); + + vif_priv->ssid.length = bss_conf->ssid_len; + memcpy(&vif_priv->ssid.ssid, + bss_conf->ssid, + bss_conf->ssid_len); + } + + if (changed & BSS_CHANGED_ASSOC) { + vif_priv->is_joining = false; + if (bss_conf->assoc) { + struct ieee80211_sta *sta; + struct wcn36xx_sta *sta_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac assoc bss %pM vif %pM AID=%d\n", + bss_conf->bssid, + vif->addr, + bss_conf->aid); + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!sta) { + wcn36xx_err("sta %pM is not found\n", + bss_conf->bssid); + rcu_read_unlock(); + goto out; + } + sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); + + wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, + vif->addr, + WCN36XX_HAL_LINK_POSTASSOC_STATE); + wcn36xx_smd_config_bss(wcn, vif, sta, + bss_conf->bssid, + true); + sta_priv->aid = bss_conf->aid; + /* + * config_sta must be called from because this is the + * place where AID is available. + */ + wcn36xx_smd_config_sta(wcn, vif, sta); + rcu_read_unlock(); + } else { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "disassociated bss %pM vif %pM AID=%d\n", + bss_conf->bssid, + vif->addr, + bss_conf->aid); + wcn36xx_smd_set_link_st(wcn, + bss_conf->bssid, + vif->addr, + WCN36XX_HAL_LINK_IDLE_STATE); + } + } + + if (changed & BSS_CHANGED_AP_PROBE_RESP) { + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n"); + skb = ieee80211_proberesp_get(hw, vif); + if (!skb) { + wcn36xx_err("failed to alloc probereq skb\n"); + goto out; + } + + wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb); + dev_kfree_skb(skb); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED || + changed & BSS_CHANGED_BEACON) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss changed beacon enabled %d\n", + bss_conf->enable_beacon); + + if (bss_conf->enable_beacon) { + vif_priv->dtim_period = bss_conf->dtim_period; + vif_priv->bss_index = 0xff; + wcn36xx_smd_config_bss(wcn, vif, NULL, + vif->addr, false); + skb = ieee80211_beacon_get_tim(hw, vif, &tim_off, + &tim_len); + if (!skb) { + wcn36xx_err("failed to alloc beacon skb\n"); + goto out; + } + wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0); + dev_kfree_skb(skb); + + if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) + link_state = WCN36XX_HAL_LINK_IBSS_STATE; + else + link_state = WCN36XX_HAL_LINK_AP_STATE; + + wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, + link_state); + } else { + wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, + WCN36XX_HAL_LINK_IDLE_STATE); + wcn36xx_smd_delete_bss(wcn, vif); + } + } +out: + return; +} + +/* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ +static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct wcn36xx *wcn = hw->priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value); + + wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); + return 0; +} + +static void wcn36xx_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif); + + list_del(&vif_priv->list); + wcn36xx_smd_delete_sta_self(wcn, vif->addr); +} + +static int wcn36xx_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n", + vif, vif->type); + + if (!(NL80211_IFTYPE_STATION == vif->type || + NL80211_IFTYPE_AP == vif->type || + NL80211_IFTYPE_ADHOC == vif->type || + NL80211_IFTYPE_MESH_POINT == vif->type)) { + wcn36xx_warn("Unsupported interface type requested: %d\n", + vif->type); + return -EOPNOTSUPP; + } + + list_add(&vif_priv->list, &wcn->vif_list); + wcn36xx_smd_add_sta_self(wcn, vif); + + return 0; +} + +static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", + vif, sta->addr); + + vif_priv->sta = sta_priv; + sta_priv->vif = vif_priv; + /* + * For STA mode HW will be configured on BSS_CHANGED_ASSOC because + * at this stage AID is not available yet. + */ + if (NL80211_IFTYPE_STATION != vif->type) { + wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); + sta_priv->aid = sta->aid; + wcn36xx_smd_config_sta(wcn, vif, sta); + } + return 0; +} + +static int wcn36xx_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n", + vif, sta->addr, sta_priv->sta_index); + + wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index); + vif_priv->sta = NULL; + sta_priv->vif = NULL; + return 0; +} + +#ifdef CONFIG_PM + +static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n"); + + flush_workqueue(wcn->hal_ind_wq); + wcn36xx_smd_set_power_params(wcn, true); + return 0; +} + +static int wcn36xx_resume(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n"); + + flush_workqueue(wcn->hal_ind_wq); + wcn36xx_smd_set_power_params(wcn, false); + return 0; +} + +#endif + +static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_sta *sta_priv = NULL; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", + action, tid); + + sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + sta_priv->tid = tid; + wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, + get_sta_index(vif, sta_priv)); + wcn36xx_smd_add_ba(wcn); + wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); + ieee80211_start_tx_ba_session(sta, tid, 0); + break; + case IEEE80211_AMPDU_RX_STOP: + wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); + break; + case IEEE80211_AMPDU_TX_START: + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, + get_sta_index(vif, sta_priv)); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP_CONT: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + default: + wcn36xx_err("Unknown AMPDU action\n"); + } + + return 0; +} + +static const struct ieee80211_ops wcn36xx_ops = { + .start = wcn36xx_start, + .stop = wcn36xx_stop, + .add_interface = wcn36xx_add_interface, + .remove_interface = wcn36xx_remove_interface, +#ifdef CONFIG_PM + .suspend = wcn36xx_suspend, + .resume = wcn36xx_resume, +#endif + .config = wcn36xx_config, + .configure_filter = wcn36xx_configure_filter, + .tx = wcn36xx_tx, + .set_key = wcn36xx_set_key, + .sw_scan_start = wcn36xx_sw_scan_start, + .sw_scan_complete = wcn36xx_sw_scan_complete, + .bss_info_changed = wcn36xx_bss_info_changed, + .set_rts_threshold = wcn36xx_set_rts_threshold, + .sta_add = wcn36xx_sta_add, + .sta_remove = wcn36xx_sta_remove, + .ampdu_action = wcn36xx_ampdu_action, +}; + +static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) +{ + int ret = 0; + + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + }; + + wcn->hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_HAS_RATE_CONTROL | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_TIMING_BEACON_ONLY; + + wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz; + wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz; + + wcn->hw->wiphy->cipher_suites = cipher_suites; + wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + +#ifdef CONFIG_PM + wcn->hw->wiphy->wowlan = &wowlan_support; +#endif + + wcn->hw->max_listen_interval = 200; + + wcn->hw->queues = 4; + + SET_IEEE80211_DEV(wcn->hw, wcn->dev); + + wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); + wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); + + return ret; +} + +static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, + struct platform_device *pdev) +{ + struct resource *res; + /* Set TX IRQ */ + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "wcnss_wlantx_irq"); + if (!res) { + wcn36xx_err("failed to get tx_irq\n"); + return -ENOENT; + } + wcn->tx_irq = res->start; + + /* Set RX IRQ */ + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "wcnss_wlanrx_irq"); + if (!res) { + wcn36xx_err("failed to get rx_irq\n"); + return -ENOENT; + } + wcn->rx_irq = res->start; + + /* Map the memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "wcnss_mmio"); + if (!res) { + wcn36xx_err("failed to get mmio\n"); + return -ENOENT; + } + wcn->mmio = ioremap(res->start, resource_size(res)); + if (!wcn->mmio) { + wcn36xx_err("failed to map io memory\n"); + return -ENOMEM; + } + return 0; +} + +static int wcn36xx_probe(struct platform_device *pdev) +{ + struct ieee80211_hw *hw; + struct wcn36xx *wcn; + int ret; + u8 addr[ETH_ALEN]; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); + + hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); + if (!hw) { + wcn36xx_err("failed to alloc hw\n"); + ret = -ENOMEM; + goto out_err; + } + platform_set_drvdata(pdev, hw); + wcn = hw->priv; + wcn->hw = hw; + wcn->dev = &pdev->dev; + wcn->ctrl_ops = pdev->dev.platform_data; + + mutex_init(&wcn->hal_mutex); + + if (!wcn->ctrl_ops->get_hw_mac(addr)) { + wcn36xx_info("mac address: %pM\n", addr); + SET_IEEE80211_PERM_ADDR(wcn->hw, addr); + } + + ret = wcn36xx_platform_get_resources(wcn, pdev); + if (ret) + goto out_wq; + + wcn36xx_init_ieee80211(wcn); + ret = ieee80211_register_hw(wcn->hw); + if (ret) + goto out_unmap; + + return 0; + +out_unmap: + iounmap(wcn->mmio); +out_wq: + ieee80211_free_hw(hw); +out_err: + return ret; +} +static int wcn36xx_remove(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct wcn36xx *wcn = hw->priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n"); + + release_firmware(wcn->nv); + mutex_destroy(&wcn->hal_mutex); + + ieee80211_unregister_hw(hw); + iounmap(wcn->mmio); + ieee80211_free_hw(hw); + + return 0; +} +static const struct platform_device_id wcn36xx_platform_id_table[] = { + { + .name = "wcn36xx", + .driver_data = 0 + }, + {} +}; +MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table); + +static struct platform_driver wcn36xx_driver = { + .probe = wcn36xx_probe, + .remove = wcn36xx_remove, + .driver = { + .name = "wcn36xx", + .owner = THIS_MODULE, + }, + .id_table = wcn36xx_platform_id_table, +}; + +static int __init wcn36xx_init(void) +{ + platform_driver_register(&wcn36xx_driver); + return 0; +} +module_init(wcn36xx_init); + +static void __exit wcn36xx_exit(void) +{ + platform_driver_unregister(&wcn36xx_driver); +} +module_exit(wcn36xx_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); +MODULE_FIRMWARE(WLAN_NV_FILE); diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.c b/drivers/net/wireless/ath/wcn36xx/pmc.c new file mode 100644 index 00000000000..28b515c81b0 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/pmc.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 "wcn36xx.h" + +int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif) +{ + int ret = 0; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + /* TODO: Make sure the TX chain clean */ + ret = wcn36xx_smd_enter_bmps(wcn, vif); + if (!ret) { + wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n"); + vif_priv->pw_state = WCN36XX_BMPS; + } else { + /* + * One of the reasons why HW will not enter BMPS is because + * driver is trying to enter bmps before first beacon was + * received just after auth complete + */ + wcn36xx_err("Can not enter BMPS!\n"); + } + return ret; +} + +int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif) +{ + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + + if (WCN36XX_BMPS != vif_priv->pw_state) { + wcn36xx_err("Not in BMPS mode, no need to exit from BMPS mode!\n"); + return -EINVAL; + } + wcn36xx_smd_exit_bmps(wcn, vif); + vif_priv->pw_state = WCN36XX_FULL_POWER; + return 0; +} + +int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn, + struct ieee80211_vif *vif) +{ + wcn36xx_dbg(WCN36XX_DBG_PMC, "%s\n", __func__); + return wcn36xx_smd_keep_alive_req(wcn, vif, + WCN36XX_HAL_KEEP_ALIVE_NULL_PKT); +} diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.h b/drivers/net/wireless/ath/wcn36xx/pmc.h new file mode 100644 index 00000000000..f72ed68b5a0 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/pmc.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _WCN36XX_PMC_H_ +#define _WCN36XX_PMC_H_ + +struct wcn36xx; + +enum wcn36xx_power_state { + WCN36XX_FULL_POWER, + WCN36XX_BMPS +}; + +int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif); +int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif); +int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn, + struct ieee80211_vif *vif); +#endif /* _WCN36XX_PMC_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c new file mode 100644 index 00000000000..63986931829 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -0,0 +1,2166 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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/etherdevice.h> +#include <linux/firmware.h> +#include <linux/bitops.h> +#include "smd.h" + +static int put_cfg_tlv_u32(struct wcn36xx *wcn, size_t *len, u32 id, u32 value) +{ + struct wcn36xx_hal_cfg *entry; + u32 *val; + + if (*len + sizeof(*entry) + sizeof(u32) >= WCN36XX_HAL_BUF_SIZE) { + wcn36xx_err("Not enough room for TLV entry\n"); + return -ENOMEM; + } + + entry = (struct wcn36xx_hal_cfg *) (wcn->hal_buf + *len); + entry->id = id; + entry->len = sizeof(u32); + entry->pad_bytes = 0; + entry->reserve = 0; + + val = (u32 *) (entry + 1); + *val = value; + + *len += sizeof(*entry) + sizeof(u32); + + return 0; +} + +static void wcn36xx_smd_set_bss_nw_type(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + struct wcn36xx_hal_config_bss_params *bss_params) +{ + if (IEEE80211_BAND_5GHZ == WCN36XX_BAND(wcn)) + bss_params->nw_type = WCN36XX_HAL_11A_NW_TYPE; + else if (sta && sta->ht_cap.ht_supported) + bss_params->nw_type = WCN36XX_HAL_11N_NW_TYPE; + else if (sta && (sta->supp_rates[IEEE80211_BAND_2GHZ] & 0x7f)) + bss_params->nw_type = WCN36XX_HAL_11G_NW_TYPE; + else + bss_params->nw_type = WCN36XX_HAL_11B_NW_TYPE; +} + +static inline u8 is_cap_supported(unsigned long caps, unsigned long flag) +{ + return caps & flag ? 1 : 0; +} +static void wcn36xx_smd_set_bss_ht_params(struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct wcn36xx_hal_config_bss_params *bss_params) +{ + if (sta && sta->ht_cap.ht_supported) { + unsigned long caps = sta->ht_cap.cap; + bss_params->ht = sta->ht_cap.ht_supported; + bss_params->tx_channel_width_set = is_cap_supported(caps, + IEEE80211_HT_CAP_SUP_WIDTH_20_40); + bss_params->lsig_tx_op_protection_full_support = + is_cap_supported(caps, + IEEE80211_HT_CAP_LSIG_TXOP_PROT); + + bss_params->ht_oper_mode = vif->bss_conf.ht_operation_mode; + bss_params->lln_non_gf_coexist = + !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + /* IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT */ + bss_params->dual_cts_protection = 0; + /* IEEE80211_HT_OP_MODE_PROTECTION_20MHZ */ + bss_params->ht20_coexist = 0; + } +} + +static void wcn36xx_smd_set_sta_ht_params(struct ieee80211_sta *sta, + struct wcn36xx_hal_config_sta_params *sta_params) +{ + if (sta->ht_cap.ht_supported) { + unsigned long caps = sta->ht_cap.cap; + sta_params->ht_capable = sta->ht_cap.ht_supported; + sta_params->tx_channel_width_set = is_cap_supported(caps, + IEEE80211_HT_CAP_SUP_WIDTH_20_40); + sta_params->lsig_txop_protection = is_cap_supported(caps, + IEEE80211_HT_CAP_LSIG_TXOP_PROT); + + sta_params->max_ampdu_size = sta->ht_cap.ampdu_factor; + sta_params->max_ampdu_density = sta->ht_cap.ampdu_density; + sta_params->max_amsdu_size = is_cap_supported(caps, + IEEE80211_HT_CAP_MAX_AMSDU); + sta_params->sgi_20Mhz = is_cap_supported(caps, + IEEE80211_HT_CAP_SGI_20); + sta_params->sgi_40mhz = is_cap_supported(caps, + IEEE80211_HT_CAP_SGI_40); + sta_params->green_field_capable = is_cap_supported(caps, + IEEE80211_HT_CAP_GRN_FLD); + sta_params->delayed_ba_support = is_cap_supported(caps, + IEEE80211_HT_CAP_DELAY_BA); + sta_params->dsss_cck_mode_40mhz = is_cap_supported(caps, + IEEE80211_HT_CAP_DSSSCCK40); + } +} + +static void wcn36xx_smd_set_sta_default_ht_params( + struct wcn36xx_hal_config_sta_params *sta_params) +{ + sta_params->ht_capable = 1; + sta_params->tx_channel_width_set = 1; + sta_params->lsig_txop_protection = 1; + sta_params->max_ampdu_size = 3; + sta_params->max_ampdu_density = 5; + sta_params->max_amsdu_size = 0; + sta_params->sgi_20Mhz = 1; + sta_params->sgi_40mhz = 1; + sta_params->green_field_capable = 1; + sta_params->delayed_ba_support = 0; + sta_params->dsss_cck_mode_40mhz = 1; +} + +static void wcn36xx_smd_set_sta_params(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct wcn36xx_hal_config_sta_params *sta_params) +{ + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *priv_sta = NULL; + if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT) { + sta_params->type = 1; + sta_params->sta_index = 0xFF; + } else { + sta_params->type = 0; + sta_params->sta_index = 1; + } + + sta_params->listen_interval = WCN36XX_LISTEN_INTERVAL(wcn); + + /* + * In STA mode ieee80211_sta contains bssid and ieee80211_vif + * contains our mac address. In AP mode we are bssid so vif + * contains bssid and ieee80211_sta contains mac. + */ + if (NL80211_IFTYPE_STATION == vif->type) + memcpy(&sta_params->mac, vif->addr, ETH_ALEN); + else + memcpy(&sta_params->bssid, vif->addr, ETH_ALEN); + + sta_params->encrypt_type = priv_vif->encrypt_type; + sta_params->short_preamble_supported = + !(WCN36XX_FLAGS(wcn) & + IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE); + + sta_params->rifs_mode = 0; + sta_params->rmf = 0; + sta_params->action = 0; + sta_params->uapsd = 0; + sta_params->mimo_ps = WCN36XX_HAL_HT_MIMO_PS_STATIC; + sta_params->max_ampdu_duration = 0; + sta_params->bssid_index = priv_vif->bss_index; + sta_params->p2p = 0; + + if (sta) { + priv_sta = (struct wcn36xx_sta *)sta->drv_priv; + if (NL80211_IFTYPE_STATION == vif->type) + memcpy(&sta_params->bssid, sta->addr, ETH_ALEN); + else + memcpy(&sta_params->mac, sta->addr, ETH_ALEN); + sta_params->wmm_enabled = sta->wme; + sta_params->max_sp_len = sta->max_sp; + sta_params->aid = priv_sta->aid; + wcn36xx_smd_set_sta_ht_params(sta, sta_params); + memcpy(&sta_params->supported_rates, &priv_sta->supported_rates, + sizeof(priv_sta->supported_rates)); + } else { + wcn36xx_set_default_rates(&sta_params->supported_rates); + wcn36xx_smd_set_sta_default_ht_params(sta_params); + } +} + +static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len) +{ + int ret = 0; + unsigned long start; + wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len); + + init_completion(&wcn->hal_rsp_compl); + start = jiffies; + ret = wcn->ctrl_ops->tx(wcn->hal_buf, len); + if (ret) { + wcn36xx_err("HAL TX failed\n"); + goto out; + } + if (wait_for_completion_timeout(&wcn->hal_rsp_compl, + msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) { + wcn36xx_err("Timeout! No SMD response in %dms\n", + HAL_MSG_TIMEOUT); + ret = -ETIME; + goto out; + } + wcn36xx_dbg(WCN36XX_DBG_SMD, "SMD command completed in %dms", + jiffies_to_msecs(jiffies - start)); +out: + return ret; +} + +#define INIT_HAL_MSG(msg_body, type) \ + do { \ + memset(&msg_body, 0, sizeof(msg_body)); \ + msg_body.header.msg_type = type; \ + msg_body.header.msg_version = WCN36XX_HAL_MSG_VERSION0; \ + msg_body.header.len = sizeof(msg_body); \ + } while (0) \ + +#define PREPARE_HAL_BUF(send_buf, msg_body) \ + do { \ + memset(send_buf, 0, msg_body.header.len); \ + memcpy(send_buf, &msg_body, sizeof(msg_body)); \ + } while (0) \ + +static int wcn36xx_smd_rsp_status_check(void *buf, size_t len) +{ + struct wcn36xx_fw_msg_status_rsp *rsp; + + if (len < sizeof(struct wcn36xx_hal_msg_header) + + sizeof(struct wcn36xx_fw_msg_status_rsp)) + return -EIO; + + rsp = (struct wcn36xx_fw_msg_status_rsp *) + (buf + sizeof(struct wcn36xx_hal_msg_header)); + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) + return rsp->status; + + return 0; +} + +int wcn36xx_smd_load_nv(struct wcn36xx *wcn) +{ + struct nv_data *nv_d; + struct wcn36xx_hal_nv_img_download_req_msg msg_body; + int fw_bytes_left; + int ret; + u16 fm_offset = 0; + + if (!wcn->nv) { + ret = request_firmware(&wcn->nv, WLAN_NV_FILE, wcn->dev); + if (ret) { + wcn36xx_err("Failed to load nv file %s: %d\n", + WLAN_NV_FILE, ret); + goto out; + } + } + + nv_d = (struct nv_data *)wcn->nv->data; + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DOWNLOAD_NV_REQ); + + msg_body.header.len += WCN36XX_NV_FRAGMENT_SIZE; + + msg_body.frag_number = 0; + /* hal_buf must be protected with mutex */ + mutex_lock(&wcn->hal_mutex); + + do { + fw_bytes_left = wcn->nv->size - fm_offset - 4; + if (fw_bytes_left > WCN36XX_NV_FRAGMENT_SIZE) { + msg_body.last_fragment = 0; + msg_body.nv_img_buffer_size = WCN36XX_NV_FRAGMENT_SIZE; + } else { + msg_body.last_fragment = 1; + msg_body.nv_img_buffer_size = fw_bytes_left; + + /* Do not forget update general message len */ + msg_body.header.len = sizeof(msg_body) + fw_bytes_left; + + } + + /* Add load NV request message header */ + memcpy(wcn->hal_buf, &msg_body, sizeof(msg_body)); + + /* Add NV body itself */ + memcpy(wcn->hal_buf + sizeof(msg_body), + &nv_d->table + fm_offset, + msg_body.nv_img_buffer_size); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) + goto out_unlock; + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_load_nv response failed err=%d\n", + ret); + goto out_unlock; + } + msg_body.frag_number++; + fm_offset += WCN36XX_NV_FRAGMENT_SIZE; + + } while (msg_body.last_fragment != 1); + +out_unlock: + mutex_unlock(&wcn->hal_mutex); +out: return ret; +} + +static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len) +{ + struct wcn36xx_hal_mac_start_rsp_msg *rsp; + + if (len < sizeof(*rsp)) + return -EIO; + + rsp = (struct wcn36xx_hal_mac_start_rsp_msg *)buf; + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->start_rsp_params.status) + return -EIO; + + memcpy(wcn->crm_version, rsp->start_rsp_params.crm_version, + WCN36XX_HAL_VERSION_LENGTH); + memcpy(wcn->wlan_version, rsp->start_rsp_params.wlan_version, + WCN36XX_HAL_VERSION_LENGTH); + + /* null terminate the strings, just in case */ + wcn->crm_version[WCN36XX_HAL_VERSION_LENGTH] = '\0'; + wcn->wlan_version[WCN36XX_HAL_VERSION_LENGTH] = '\0'; + + wcn->fw_revision = rsp->start_rsp_params.version.revision; + wcn->fw_version = rsp->start_rsp_params.version.version; + wcn->fw_minor = rsp->start_rsp_params.version.minor; + wcn->fw_major = rsp->start_rsp_params.version.major; + + wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n", + wcn->wlan_version, wcn->crm_version); + + wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n", + wcn->fw_major, wcn->fw_minor, + wcn->fw_version, wcn->fw_revision, + rsp->start_rsp_params.stations, + rsp->start_rsp_params.bssids); + + return 0; +} + +int wcn36xx_smd_start(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_mac_start_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_REQ); + + msg_body.params.type = DRIVER_TYPE_PRODUCTION; + msg_body.params.len = 0; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start type %d\n", + msg_body.params.type); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_start failed\n"); + goto out; + } + + ret = wcn36xx_smd_start_rsp(wcn, wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_start response failed err=%d\n", ret); + goto out; + } + +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_stop(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_mac_stop_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_STOP_REQ); + + msg_body.stop_req_params.reason = HAL_STOP_TYPE_RF_KILL; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_stop failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_stop response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode) +{ + struct wcn36xx_hal_init_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_INIT_SCAN_REQ); + + msg_body.mode = mode; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal init scan mode %d\n", msg_body.mode); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_init_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_init_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_start_scan(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_start_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_REQ); + + msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start scan channel %d\n", + msg_body.scan_channel); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_start_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_start_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_end_scan(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_end_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_END_SCAN_REQ); + + msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal end scan channel %d\n", + msg_body.scan_channel); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_end_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_end_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, + enum wcn36xx_hal_sys_mode mode) +{ + struct wcn36xx_hal_finish_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_FINISH_SCAN_REQ); + + msg_body.mode = mode; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal finish scan mode %d\n", + msg_body.mode); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_finish_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_finish_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_switch_channel_rsp(void *buf, size_t len) +{ + struct wcn36xx_hal_switch_channel_rsp_msg *rsp; + int ret = 0; + + ret = wcn36xx_smd_rsp_status_check(buf, len); + if (ret) + return ret; + rsp = (struct wcn36xx_hal_switch_channel_rsp_msg *)buf; + wcn36xx_dbg(WCN36XX_DBG_HAL, "channel switched to: %d, status: %d\n", + rsp->channel_number, rsp->status); + return ret; +} + +int wcn36xx_smd_switch_channel(struct wcn36xx *wcn, + struct ieee80211_vif *vif, int ch) +{ + struct wcn36xx_hal_switch_channel_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_CH_SWITCH_REQ); + + msg_body.channel_number = (u8)ch; + msg_body.tx_mgmt_power = 0xbf; + msg_body.max_tx_power = 0xbf; + memcpy(msg_body.self_sta_mac_addr, vif->addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_switch_channel failed\n"); + goto out; + } + ret = wcn36xx_smd_switch_channel_rsp(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_switch_channel response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len) +{ + struct wcn36xx_hal_update_scan_params_resp *rsp; + + rsp = (struct wcn36xx_hal_update_scan_params_resp *)buf; + + /* Remove the PNO version bit */ + rsp->status &= (~(WCN36XX_FW_MSG_PNO_VERSION_MASK)); + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) { + wcn36xx_warn("error response from update scan\n"); + return rsp->status; + } + + return 0; +} + +int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_update_scan_params_req msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ); + + msg_body.dot11d_enabled = 0; + msg_body.dot11d_resolved = 0; + msg_body.channel_count = 26; + msg_body.active_min_ch_time = 60; + msg_body.active_max_ch_time = 120; + msg_body.passive_min_ch_time = 60; + msg_body.passive_max_ch_time = 110; + msg_body.state = 0; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal update scan params channel_count %d\n", + msg_body.channel_count); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_update_scan_params failed\n"); + goto out; + } + ret = wcn36xx_smd_update_scan_params_rsp(wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_update_scan_params response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_add_sta_self_rsp(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + void *buf, + size_t len) +{ + struct wcn36xx_hal_add_sta_self_rsp_msg *rsp; + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_add_sta_self_rsp_msg *)buf; + + if (rsp->status != WCN36XX_FW_MSG_RESULT_SUCCESS) { + wcn36xx_warn("hal add sta self failure: %d\n", + rsp->status); + return rsp->status; + } + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal add sta self status %d self_sta_index %d dpu_index %d\n", + rsp->status, rsp->self_sta_index, rsp->dpu_index); + + priv_vif->self_sta_index = rsp->self_sta_index; + priv_vif->self_dpu_desc_index = rsp->dpu_index; + + return 0; +} + +int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_add_sta_self_req msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_STA_SELF_REQ); + + memcpy(&msg_body.self_addr, vif->addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal add sta self self_addr %pM status %d\n", + msg_body.self_addr, msg_body.status); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_add_sta_self failed\n"); + goto out; + } + ret = wcn36xx_smd_add_sta_self_rsp(wcn, + vif, + wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_add_sta_self response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr) +{ + struct wcn36xx_hal_del_sta_self_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DEL_STA_SELF_REQ); + + memcpy(&msg_body.self_addr, addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_delete_sta_self failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_delete_sta_self response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index) +{ + struct wcn36xx_hal_delete_sta_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_STA_REQ); + + msg_body.sta_index = sta_index; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal delete sta sta_index %d\n", + msg_body.sta_index); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_delete_sta failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_delete_sta response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_join_rsp(void *buf, size_t len) +{ + struct wcn36xx_hal_join_rsp_msg *rsp; + + if (wcn36xx_smd_rsp_status_check(buf, len)) + return -EIO; + + rsp = (struct wcn36xx_hal_join_rsp_msg *)buf; + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal rsp join status %d tx_mgmt_power %d\n", + rsp->status, rsp->tx_mgmt_power); + + return 0; +} + +int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch) +{ + struct wcn36xx_hal_join_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_JOIN_REQ); + + memcpy(&msg_body.bssid, bssid, ETH_ALEN); + memcpy(&msg_body.self_sta_mac_addr, vif, ETH_ALEN); + msg_body.channel = ch; + + if (conf_is_ht40_minus(&wcn->hw->conf)) + msg_body.secondary_channel_offset = + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + else if (conf_is_ht40_plus(&wcn->hw->conf)) + msg_body.secondary_channel_offset = + PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + else + msg_body.secondary_channel_offset = + PHY_SINGLE_CHANNEL_CENTERED; + + msg_body.link_state = WCN36XX_HAL_LINK_PREASSOC_STATE; + + msg_body.max_tx_power = 0xbf; + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal join req bssid %pM self_sta_mac_addr %pM channel %d link_state %d\n", + msg_body.bssid, msg_body.self_sta_mac_addr, + msg_body.channel, msg_body.link_state); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_join failed\n"); + goto out; + } + ret = wcn36xx_smd_join_rsp(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_join response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid, + const u8 *sta_mac, + enum wcn36xx_hal_link_state state) +{ + struct wcn36xx_hal_set_link_state_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_LINK_ST_REQ); + + memcpy(&msg_body.bssid, bssid, ETH_ALEN); + memcpy(&msg_body.self_mac_addr, sta_mac, ETH_ALEN); + msg_body.state = state; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal set link state bssid %pM self_mac_addr %pM state %d\n", + msg_body.bssid, msg_body.self_mac_addr, msg_body.state); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_link_st failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_set_link_st response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static void wcn36xx_smd_convert_sta_to_v1(struct wcn36xx *wcn, + const struct wcn36xx_hal_config_sta_params *orig, + struct wcn36xx_hal_config_sta_params_v1 *v1) +{ + /* convert orig to v1 format */ + memcpy(&v1->bssid, orig->bssid, ETH_ALEN); + memcpy(&v1->mac, orig->mac, ETH_ALEN); + v1->aid = orig->aid; + v1->type = orig->type; + v1->listen_interval = orig->listen_interval; + v1->ht_capable = orig->ht_capable; + + v1->max_ampdu_size = orig->max_ampdu_size; + v1->max_ampdu_density = orig->max_ampdu_density; + v1->sgi_40mhz = orig->sgi_40mhz; + v1->sgi_20Mhz = orig->sgi_20Mhz; + + memcpy(&v1->supported_rates, &orig->supported_rates, + sizeof(orig->supported_rates)); + v1->sta_index = orig->sta_index; +} + +static int wcn36xx_smd_config_sta_rsp(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + void *buf, + size_t len) +{ + struct wcn36xx_hal_config_sta_rsp_msg *rsp; + struct config_sta_rsp_params *params; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_config_sta_rsp_msg *)buf; + params = &rsp->params; + + if (params->status != WCN36XX_FW_MSG_RESULT_SUCCESS) { + wcn36xx_warn("hal config sta response failure: %d\n", + params->status); + return -EIO; + } + + sta_priv->sta_index = params->sta_index; + sta_priv->dpu_desc_index = params->dpu_index; + sta_priv->ucast_dpu_sign = params->uc_ucast_sig; + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config sta rsp status %d sta_index %d bssid_index %d uc_ucast_sig %d p2p %d\n", + params->status, params->sta_index, params->bssid_index, + params->uc_ucast_sig, params->p2p); + + return 0; +} + +static int wcn36xx_smd_config_sta_v1(struct wcn36xx *wcn, + const struct wcn36xx_hal_config_sta_req_msg *orig) +{ + struct wcn36xx_hal_config_sta_req_msg_v1 msg_body; + struct wcn36xx_hal_config_sta_params_v1 *sta = &msg_body.sta_params; + + INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_STA_REQ); + + wcn36xx_smd_convert_sta_to_v1(wcn, &orig->sta_params, + &msg_body.sta_params); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config sta v1 action %d sta_index %d bssid_index %d bssid %pM type %d mac %pM aid %d\n", + sta->action, sta->sta_index, sta->bssid_index, + sta->bssid, sta->type, sta->mac, sta->aid); + + return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); +} + +int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wcn36xx_hal_config_sta_req_msg msg; + struct wcn36xx_hal_config_sta_params *sta_params; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_STA_REQ); + + sta_params = &msg.sta_params; + + wcn36xx_smd_set_sta_params(wcn, vif, sta, sta_params); + + if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + ret = wcn36xx_smd_config_sta_v1(wcn, &msg); + } else { + PREPARE_HAL_BUF(wcn->hal_buf, msg); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config sta action %d sta_index %d bssid_index %d bssid %pM type %d mac %pM aid %d\n", + sta_params->action, sta_params->sta_index, + sta_params->bssid_index, sta_params->bssid, + sta_params->type, sta_params->mac, sta_params->aid); + + ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + } + if (ret) { + wcn36xx_err("Sending hal_config_sta failed\n"); + goto out; + } + ret = wcn36xx_smd_config_sta_rsp(wcn, + sta, + wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_config_sta response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_config_bss_v1(struct wcn36xx *wcn, + const struct wcn36xx_hal_config_bss_req_msg *orig) +{ + struct wcn36xx_hal_config_bss_req_msg_v1 msg_body; + struct wcn36xx_hal_config_bss_params_v1 *bss = &msg_body.bss_params; + struct wcn36xx_hal_config_sta_params_v1 *sta = &bss->sta; + + INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_BSS_REQ); + + /* convert orig to v1 */ + memcpy(&msg_body.bss_params.bssid, + &orig->bss_params.bssid, ETH_ALEN); + memcpy(&msg_body.bss_params.self_mac_addr, + &orig->bss_params.self_mac_addr, ETH_ALEN); + + msg_body.bss_params.bss_type = orig->bss_params.bss_type; + msg_body.bss_params.oper_mode = orig->bss_params.oper_mode; + msg_body.bss_params.nw_type = orig->bss_params.nw_type; + + msg_body.bss_params.short_slot_time_supported = + orig->bss_params.short_slot_time_supported; + msg_body.bss_params.lla_coexist = orig->bss_params.lla_coexist; + msg_body.bss_params.llb_coexist = orig->bss_params.llb_coexist; + msg_body.bss_params.llg_coexist = orig->bss_params.llg_coexist; + msg_body.bss_params.ht20_coexist = orig->bss_params.ht20_coexist; + msg_body.bss_params.lln_non_gf_coexist = + orig->bss_params.lln_non_gf_coexist; + + msg_body.bss_params.lsig_tx_op_protection_full_support = + orig->bss_params.lsig_tx_op_protection_full_support; + msg_body.bss_params.rifs_mode = orig->bss_params.rifs_mode; + msg_body.bss_params.beacon_interval = orig->bss_params.beacon_interval; + msg_body.bss_params.dtim_period = orig->bss_params.dtim_period; + msg_body.bss_params.tx_channel_width_set = + orig->bss_params.tx_channel_width_set; + msg_body.bss_params.oper_channel = orig->bss_params.oper_channel; + msg_body.bss_params.ext_channel = orig->bss_params.ext_channel; + + msg_body.bss_params.reserved = orig->bss_params.reserved; + + memcpy(&msg_body.bss_params.ssid, + &orig->bss_params.ssid, + sizeof(orig->bss_params.ssid)); + + msg_body.bss_params.action = orig->bss_params.action; + msg_body.bss_params.rateset = orig->bss_params.rateset; + msg_body.bss_params.ht = orig->bss_params.ht; + msg_body.bss_params.obss_prot_enabled = + orig->bss_params.obss_prot_enabled; + msg_body.bss_params.rmf = orig->bss_params.rmf; + msg_body.bss_params.ht_oper_mode = orig->bss_params.ht_oper_mode; + msg_body.bss_params.dual_cts_protection = + orig->bss_params.dual_cts_protection; + + msg_body.bss_params.max_probe_resp_retry_limit = + orig->bss_params.max_probe_resp_retry_limit; + msg_body.bss_params.hidden_ssid = orig->bss_params.hidden_ssid; + msg_body.bss_params.proxy_probe_resp = + orig->bss_params.proxy_probe_resp; + msg_body.bss_params.edca_params_valid = + orig->bss_params.edca_params_valid; + + memcpy(&msg_body.bss_params.acbe, + &orig->bss_params.acbe, + sizeof(orig->bss_params.acbe)); + memcpy(&msg_body.bss_params.acbk, + &orig->bss_params.acbk, + sizeof(orig->bss_params.acbk)); + memcpy(&msg_body.bss_params.acvi, + &orig->bss_params.acvi, + sizeof(orig->bss_params.acvi)); + memcpy(&msg_body.bss_params.acvo, + &orig->bss_params.acvo, + sizeof(orig->bss_params.acvo)); + + msg_body.bss_params.ext_set_sta_key_param_valid = + orig->bss_params.ext_set_sta_key_param_valid; + + memcpy(&msg_body.bss_params.ext_set_sta_key_param, + &orig->bss_params.ext_set_sta_key_param, + sizeof(orig->bss_params.acvo)); + + msg_body.bss_params.wcn36xx_hal_persona = + orig->bss_params.wcn36xx_hal_persona; + msg_body.bss_params.spectrum_mgt_enable = + orig->bss_params.spectrum_mgt_enable; + msg_body.bss_params.tx_mgmt_power = orig->bss_params.tx_mgmt_power; + msg_body.bss_params.max_tx_power = orig->bss_params.max_tx_power; + + wcn36xx_smd_convert_sta_to_v1(wcn, &orig->bss_params.sta, + &msg_body.bss_params.sta); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config bss v1 bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n", + bss->bssid, bss->self_mac_addr, bss->bss_type, + bss->oper_mode, bss->nw_type); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "- sta bssid %pM action %d sta_index %d bssid_index %d aid %d type %d mac %pM\n", + sta->bssid, sta->action, sta->sta_index, + sta->bssid_index, sta->aid, sta->type, sta->mac); + + return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); +} + + +static int wcn36xx_smd_config_bss_rsp(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + void *buf, + size_t len) +{ + struct wcn36xx_hal_config_bss_rsp_msg *rsp; + struct wcn36xx_hal_config_bss_rsp_params *params; + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_config_bss_rsp_msg *)buf; + params = &rsp->bss_rsp_params; + + if (params->status != WCN36XX_FW_MSG_RESULT_SUCCESS) { + wcn36xx_warn("hal config bss response failure: %d\n", + params->status); + return -EIO; + } + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config bss rsp status %d bss_idx %d dpu_desc_index %d" + " sta_idx %d self_idx %d bcast_idx %d mac %pM" + " power %d ucast_dpu_signature %d\n", + params->status, params->bss_index, params->dpu_desc_index, + params->bss_sta_index, params->bss_self_sta_index, + params->bss_bcast_sta_idx, params->mac, + params->tx_mgmt_power, params->ucast_dpu_signature); + + priv_vif->bss_index = params->bss_index; + + if (priv_vif->sta) { + priv_vif->sta->bss_sta_index = params->bss_sta_index; + priv_vif->sta->bss_dpu_desc_index = params->dpu_desc_index; + } + + priv_vif->self_ucast_dpu_sign = params->ucast_dpu_signature; + + return 0; +} + +int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, const u8 *bssid, + bool update) +{ + struct wcn36xx_hal_config_bss_req_msg msg; + struct wcn36xx_hal_config_bss_params *bss; + struct wcn36xx_hal_config_sta_params *sta_params; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_BSS_REQ); + + bss = &msg.bss_params; + sta_params = &bss->sta; + + WARN_ON(is_zero_ether_addr(bssid)); + + memcpy(&bss->bssid, bssid, ETH_ALEN); + + memcpy(bss->self_mac_addr, vif->addr, ETH_ALEN); + + if (vif->type == NL80211_IFTYPE_STATION) { + bss->bss_type = WCN36XX_HAL_INFRASTRUCTURE_MODE; + + /* STA */ + bss->oper_mode = 1; + bss->wcn36xx_hal_persona = WCN36XX_HAL_STA_MODE; + } else if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT) { + bss->bss_type = WCN36XX_HAL_INFRA_AP_MODE; + + /* AP */ + bss->oper_mode = 0; + bss->wcn36xx_hal_persona = WCN36XX_HAL_STA_SAP_MODE; + } else if (vif->type == NL80211_IFTYPE_ADHOC) { + bss->bss_type = WCN36XX_HAL_IBSS_MODE; + + /* STA */ + bss->oper_mode = 1; + } else { + wcn36xx_warn("Unknown type for bss config: %d\n", vif->type); + } + + if (vif->type == NL80211_IFTYPE_STATION) + wcn36xx_smd_set_bss_nw_type(wcn, sta, bss); + else + bss->nw_type = WCN36XX_HAL_11N_NW_TYPE; + + bss->short_slot_time_supported = vif->bss_conf.use_short_slot; + bss->lla_coexist = 0; + bss->llb_coexist = 0; + bss->llg_coexist = 0; + bss->rifs_mode = 0; + bss->beacon_interval = vif->bss_conf.beacon_int; + bss->dtim_period = vif_priv->dtim_period; + + wcn36xx_smd_set_bss_ht_params(vif, sta, bss); + + bss->oper_channel = WCN36XX_HW_CHANNEL(wcn); + + if (conf_is_ht40_minus(&wcn->hw->conf)) + bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + else if (conf_is_ht40_plus(&wcn->hw->conf)) + bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_NONE; + + bss->reserved = 0; + wcn36xx_smd_set_sta_params(wcn, vif, sta, sta_params); + + /* wcn->ssid is only valid in AP and IBSS mode */ + bss->ssid.length = vif_priv->ssid.length; + memcpy(bss->ssid.ssid, vif_priv->ssid.ssid, vif_priv->ssid.length); + + bss->obss_prot_enabled = 0; + bss->rmf = 0; + bss->max_probe_resp_retry_limit = 0; + bss->hidden_ssid = vif->bss_conf.hidden_ssid; + bss->proxy_probe_resp = 0; + bss->edca_params_valid = 0; + + /* FIXME: set acbe, acbk, acvi and acvo */ + + bss->ext_set_sta_key_param_valid = 0; + + /* FIXME: set ext_set_sta_key_param */ + + bss->spectrum_mgt_enable = 0; + bss->tx_mgmt_power = 0; + bss->max_tx_power = WCN36XX_MAX_POWER(wcn); + + bss->action = update; + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config bss bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n", + bss->bssid, bss->self_mac_addr, bss->bss_type, + bss->oper_mode, bss->nw_type); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "- sta bssid %pM action %d sta_index %d bssid_index %d aid %d type %d mac %pM\n", + sta_params->bssid, sta_params->action, + sta_params->sta_index, sta_params->bssid_index, + sta_params->aid, sta_params->type, + sta_params->mac); + + if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + ret = wcn36xx_smd_config_bss_v1(wcn, &msg); + } else { + PREPARE_HAL_BUF(wcn->hal_buf, msg); + + ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + } + if (ret) { + wcn36xx_err("Sending hal_config_bss failed\n"); + goto out; + } + ret = wcn36xx_smd_config_bss_rsp(wcn, + vif, + wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_config_bss response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_delete_bss_req_msg msg_body; + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_BSS_REQ); + + msg_body.bss_index = priv_vif->bss_index; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal delete bss %d\n", msg_body.bss_index); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_delete_bss failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_delete_bss response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct sk_buff *skb_beacon, u16 tim_off, + u16 p2p_off) +{ + struct wcn36xx_hal_send_beacon_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SEND_BEACON_REQ); + + /* TODO need to find out why this is needed? */ + msg_body.beacon_length = skb_beacon->len + 6; + + if (BEACON_TEMPLATE_SIZE > msg_body.beacon_length) { + memcpy(&msg_body.beacon, &skb_beacon->len, sizeof(u32)); + memcpy(&(msg_body.beacon[4]), skb_beacon->data, + skb_beacon->len); + } else { + wcn36xx_err("Beacon is to big: beacon size=%d\n", + msg_body.beacon_length); + ret = -ENOMEM; + goto out; + } + memcpy(msg_body.bssid, vif->addr, ETH_ALEN); + + /* TODO need to find out why this is needed? */ + if (vif->type == NL80211_IFTYPE_MESH_POINT) + /* mesh beacon don't need this, so push further down */ + msg_body.tim_ie_offset = 256; + else + msg_body.tim_ie_offset = tim_off+4; + msg_body.p2p_ie_offset = p2p_off; + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal send beacon beacon_length %d\n", + msg_body.beacon_length); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_send_beacon failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_send_beacon response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct wcn36xx_hal_send_probe_resp_req_msg msg; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg, WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_REQ); + + if (skb->len > BEACON_TEMPLATE_SIZE) { + wcn36xx_warn("probe response template is too big: %d\n", + skb->len); + ret = -E2BIG; + goto out; + } + + msg.probe_resp_template_len = skb->len; + memcpy(&msg.probe_resp_template, skb->data, skb->len); + + memcpy(msg.bssid, vif->addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal update probe rsp len %d bssid %pM\n", + msg.probe_resp_template_len, msg.bssid); + + ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + if (ret) { + wcn36xx_err("Sending hal_update_proberesp_tmpl failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_update_proberesp_tmpl response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_set_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key, + u8 sta_index) +{ + struct wcn36xx_hal_set_sta_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_STAKEY_REQ); + + msg_body.set_sta_key_params.sta_index = sta_index; + msg_body.set_sta_key_params.enc_type = enc_type; + + msg_body.set_sta_key_params.key[0].id = keyidx; + msg_body.set_sta_key_params.key[0].unicast = 1; + msg_body.set_sta_key_params.key[0].direction = WCN36XX_HAL_TX_RX; + msg_body.set_sta_key_params.key[0].pae_role = 0; + msg_body.set_sta_key_params.key[0].length = keylen; + memcpy(msg_body.set_sta_key_params.key[0].key, key, keylen); + msg_body.set_sta_key_params.single_tid_rc = 1; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_stakey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_set_stakey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key) +{ + struct wcn36xx_hal_set_bss_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_BSSKEY_REQ); + msg_body.bss_idx = 0; + msg_body.enc_type = enc_type; + msg_body.num_keys = 1; + msg_body.keys[0].id = keyidx; + msg_body.keys[0].unicast = 0; + msg_body.keys[0].direction = WCN36XX_HAL_RX_ONLY; + msg_body.keys[0].pae_role = 0; + msg_body.keys[0].length = keylen; + memcpy(msg_body.keys[0].key, key, keylen); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_bsskey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_set_bsskey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 sta_index) +{ + struct wcn36xx_hal_remove_sta_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_STAKEY_REQ); + + msg_body.sta_idx = sta_index; + msg_body.enc_type = enc_type; + msg_body.key_id = keyidx; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_remove_stakey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_remove_stakey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx) +{ + struct wcn36xx_hal_remove_bss_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_BSSKEY_REQ); + msg_body.bss_idx = 0; + msg_body.enc_type = enc_type; + msg_body.key_id = keyidx; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_remove_bsskey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_remove_bsskey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_enter_bmps_req_msg msg_body; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ENTER_BMPS_REQ); + + msg_body.bss_index = vif_priv->bss_index; + msg_body.tbtt = vif->bss_conf.sync_tsf; + msg_body.dtim_period = vif_priv->dtim_period; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_enter_bmps failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_enter_bmps response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_enter_bmps_req_msg msg_body; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_EXIT_BMPS_REQ); + + msg_body.bss_index = vif_priv->bss_index; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_exit_bmps failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} +int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim) +{ + struct wcn36xx_hal_set_power_params_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_POWER_PARAMS_REQ); + + /* + * When host is down ignore every second dtim + */ + if (ignore_dtim) { + msg_body.ignore_dtim = 1; + msg_body.dtim_period = 2; + } + msg_body.listen_interval = WCN36XX_LISTEN_INTERVAL(wcn); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_power_params failed\n"); + goto out; + } + +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} +/* Notice: This function should be called after associated, or else it + * will be invalid + */ +int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + int packet_type) +{ + struct wcn36xx_hal_keep_alive_req_msg msg_body; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_KEEP_ALIVE_REQ); + + if (packet_type == WCN36XX_HAL_KEEP_ALIVE_NULL_PKT) { + msg_body.bss_index = vif_priv->bss_index; + msg_body.packet_type = WCN36XX_HAL_KEEP_ALIVE_NULL_PKT; + msg_body.time_period = WCN36XX_KEEP_ALIVE_TIME_PERIOD; + } else if (packet_type == WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP) { + /* TODO: it also support ARP response type */ + } else { + wcn36xx_warn("unknow keep alive packet type %d\n", packet_type); + ret = -EINVAL; + goto out; + } + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_keep_alive failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_keep_alive response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 arg5) +{ + struct wcn36xx_hal_dump_cmd_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DUMP_COMMAND_REQ); + + msg_body.arg1 = arg1; + msg_body.arg2 = arg2; + msg_body.arg3 = arg3; + msg_body.arg4 = arg4; + msg_body.arg5 = arg5; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_dump_cmd failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_dump_cmd response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap) +{ + int arr_idx, bit_idx; + + if (cap < 0 || cap > 127) { + wcn36xx_warn("error cap idx %d\n", cap); + return; + } + + arr_idx = cap / 32; + bit_idx = cap % 32; + bitmap[arr_idx] |= (1 << bit_idx); +} + +int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap) +{ + int arr_idx, bit_idx; + int ret = 0; + + if (cap < 0 || cap > 127) { + wcn36xx_warn("error cap idx %d\n", cap); + return -EINVAL; + } + + arr_idx = cap / 32; + bit_idx = cap % 32; + ret = (bitmap[arr_idx] & (1 << bit_idx)) ? 1 : 0; + return ret; +} + +void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap) +{ + int arr_idx, bit_idx; + + if (cap < 0 || cap > 127) { + wcn36xx_warn("error cap idx %d\n", cap); + return; + } + + arr_idx = cap / 32; + bit_idx = cap % 32; + bitmap[arr_idx] &= ~(1 << bit_idx); +} + +int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_feat_caps_msg msg_body, *rsp; + int ret = 0, i; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ); + + set_feat_caps(msg_body.feat_caps, STA_POWERSAVE); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_feature_caps_exchange failed\n"); + goto out; + } + if (wcn->hal_rsp_len != sizeof(*rsp)) { + wcn36xx_err("Invalid hal_feature_caps_exchange response"); + goto out; + } + + rsp = (struct wcn36xx_hal_feat_caps_msg *) wcn->hal_buf; + + for (i = 0; i < WCN36XX_HAL_CAPS_SIZE; i++) + wcn->fw_feat_caps[i] = rsp->feat_caps[i]; +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + u16 tid, + u16 *ssn, + u8 direction, + u8 sta_index) +{ + struct wcn36xx_hal_add_ba_session_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_SESSION_REQ); + + msg_body.sta_index = sta_index; + memcpy(&msg_body.mac_addr, sta->addr, ETH_ALEN); + msg_body.dialog_token = 0x10; + msg_body.tid = tid; + + /* Immediate BA because Delayed BA is not supported */ + msg_body.policy = 1; + msg_body.buffer_size = WCN36XX_AGGR_BUFFER_SIZE; + msg_body.timeout = 0; + if (ssn) + msg_body.ssn = *ssn; + msg_body.direction = direction; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_add_ba_session failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_add_ba_session response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_add_ba(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_add_ba_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_REQ); + + msg_body.session_id = 0; + msg_body.win_size = WCN36XX_AGGR_BUFFER_SIZE; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_add_ba failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_add_ba response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index) +{ + struct wcn36xx_hal_del_ba_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DEL_BA_REQ); + + msg_body.sta_index = sta_index; + msg_body.tid = tid; + msg_body.direction = 0; + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_del_ba failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_del_ba response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index) +{ + struct wcn36xx_hal_trigger_ba_req_msg msg_body; + struct wcn36xx_hal_trigger_ba_req_candidate *candidate; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_TRIGGER_BA_REQ); + + msg_body.session_id = 0; + msg_body.candidate_cnt = 1; + msg_body.header.len += sizeof(*candidate); + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + candidate = (struct wcn36xx_hal_trigger_ba_req_candidate *) + (wcn->hal_buf + sizeof(msg_body)); + candidate->sta_index = sta_index; + candidate->tid_bitmap = 1; + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_trigger_ba failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_trigger_ba response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_tx_compl_ind(struct wcn36xx *wcn, void *buf, size_t len) +{ + struct wcn36xx_hal_tx_compl_ind_msg *rsp = buf; + + if (len != sizeof(*rsp)) { + wcn36xx_warn("Bad TX complete indication\n"); + return -EIO; + } + + wcn36xx_dxe_tx_ack_ind(wcn, rsp->status); + + return 0; +} + +static int wcn36xx_smd_missed_beacon_ind(struct wcn36xx *wcn, + void *buf, + size_t len) +{ + struct wcn36xx_hal_missed_beacon_ind_msg *rsp = buf; + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *tmp; + + /* Old FW does not have bss index */ + if (wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + list_for_each_entry(tmp, &wcn->vif_list, list) { + wcn36xx_dbg(WCN36XX_DBG_HAL, "beacon missed bss_index %d\n", + tmp->bss_index); + vif = container_of((void *)tmp, + struct ieee80211_vif, + drv_priv); + ieee80211_connection_loss(vif); + } + return 0; + } + + if (len != sizeof(*rsp)) { + wcn36xx_warn("Corrupted missed beacon indication\n"); + return -EIO; + } + + list_for_each_entry(tmp, &wcn->vif_list, list) { + if (tmp->bss_index == rsp->bss_index) { + wcn36xx_dbg(WCN36XX_DBG_HAL, "beacon missed bss_index %d\n", + rsp->bss_index); + vif = container_of((void *)tmp, + struct ieee80211_vif, + drv_priv); + ieee80211_connection_loss(vif); + return 0; + } + } + + wcn36xx_warn("BSS index %d not found\n", rsp->bss_index); + return -ENOENT; +} + +static int wcn36xx_smd_delete_sta_context_ind(struct wcn36xx *wcn, + void *buf, + size_t len) +{ + struct wcn36xx_hal_delete_sta_context_ind_msg *rsp = buf; + struct wcn36xx_vif *tmp; + struct ieee80211_sta *sta = NULL; + + if (len != sizeof(*rsp)) { + wcn36xx_warn("Corrupted delete sta indication\n"); + return -EIO; + } + + list_for_each_entry(tmp, &wcn->vif_list, list) { + if (sta && (tmp->sta->sta_index == rsp->sta_id)) { + sta = container_of((void *)tmp->sta, + struct ieee80211_sta, + drv_priv); + wcn36xx_dbg(WCN36XX_DBG_HAL, + "delete station indication %pM index %d\n", + rsp->addr2, + rsp->sta_id); + ieee80211_report_low_ack(sta, 0); + return 0; + } + } + + wcn36xx_warn("STA with addr %pM and index %d not found\n", + rsp->addr2, + rsp->sta_id); + return -ENOENT; +} + +int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value) +{ + struct wcn36xx_hal_update_cfg_req_msg msg_body, *body; + size_t len; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_CFG_REQ); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + body = (struct wcn36xx_hal_update_cfg_req_msg *) wcn->hal_buf; + len = msg_body.header.len; + + put_cfg_tlv_u32(wcn, &len, cfg_id, value); + body->header.len = len; + body->len = len - sizeof(*body); + + ret = wcn36xx_smd_send_and_wait(wcn, body->header.len); + if (ret) { + wcn36xx_err("Sending hal_update_cfg failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_update_cfg response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} +static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) +{ + struct wcn36xx_hal_msg_header *msg_header = buf; + struct wcn36xx_hal_ind_msg *msg_ind; + wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len); + + switch (msg_header->msg_type) { + case WCN36XX_HAL_START_RSP: + case WCN36XX_HAL_CONFIG_STA_RSP: + case WCN36XX_HAL_CONFIG_BSS_RSP: + case WCN36XX_HAL_ADD_STA_SELF_RSP: + case WCN36XX_HAL_STOP_RSP: + case WCN36XX_HAL_DEL_STA_SELF_RSP: + case WCN36XX_HAL_DELETE_STA_RSP: + case WCN36XX_HAL_INIT_SCAN_RSP: + case WCN36XX_HAL_START_SCAN_RSP: + case WCN36XX_HAL_END_SCAN_RSP: + case WCN36XX_HAL_FINISH_SCAN_RSP: + case WCN36XX_HAL_DOWNLOAD_NV_RSP: + case WCN36XX_HAL_DELETE_BSS_RSP: + case WCN36XX_HAL_SEND_BEACON_RSP: + case WCN36XX_HAL_SET_LINK_ST_RSP: + case WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP: + case WCN36XX_HAL_SET_BSSKEY_RSP: + case WCN36XX_HAL_SET_STAKEY_RSP: + case WCN36XX_HAL_RMV_STAKEY_RSP: + case WCN36XX_HAL_RMV_BSSKEY_RSP: + case WCN36XX_HAL_ENTER_BMPS_RSP: + case WCN36XX_HAL_SET_POWER_PARAMS_RSP: + case WCN36XX_HAL_EXIT_BMPS_RSP: + case WCN36XX_HAL_KEEP_ALIVE_RSP: + case WCN36XX_HAL_DUMP_COMMAND_RSP: + case WCN36XX_HAL_ADD_BA_SESSION_RSP: + case WCN36XX_HAL_ADD_BA_RSP: + case WCN36XX_HAL_DEL_BA_RSP: + case WCN36XX_HAL_TRIGGER_BA_RSP: + case WCN36XX_HAL_UPDATE_CFG_RSP: + case WCN36XX_HAL_JOIN_RSP: + case WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP: + case WCN36XX_HAL_CH_SWITCH_RSP: + case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP: + memcpy(wcn->hal_buf, buf, len); + wcn->hal_rsp_len = len; + complete(&wcn->hal_rsp_compl); + break; + + case WCN36XX_HAL_OTA_TX_COMPL_IND: + case WCN36XX_HAL_MISSED_BEACON_IND: + case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: + msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL); + if (!msg_ind) + goto nomem; + msg_ind->msg_len = len; + msg_ind->msg = kmemdup(buf, len, GFP_KERNEL); + if (!msg_ind->msg) { + kfree(msg_ind); +nomem: + /* + * FIXME: Do something smarter then just + * printing an error. + */ + wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n", + msg_header->msg_type); + break; + } + mutex_lock(&wcn->hal_ind_mutex); + list_add_tail(&msg_ind->list, &wcn->hal_ind_queue); + queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work); + mutex_unlock(&wcn->hal_ind_mutex); + wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n"); + break; + default: + wcn36xx_err("SMD_EVENT (%d) not supported\n", + msg_header->msg_type); + } +} +static void wcn36xx_ind_smd_work(struct work_struct *work) +{ + struct wcn36xx *wcn = + container_of(work, struct wcn36xx, hal_ind_work); + struct wcn36xx_hal_msg_header *msg_header; + struct wcn36xx_hal_ind_msg *hal_ind_msg; + + mutex_lock(&wcn->hal_ind_mutex); + + hal_ind_msg = list_first_entry(&wcn->hal_ind_queue, + struct wcn36xx_hal_ind_msg, + list); + + msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg; + + switch (msg_header->msg_type) { + case WCN36XX_HAL_OTA_TX_COMPL_IND: + wcn36xx_smd_tx_compl_ind(wcn, + hal_ind_msg->msg, + hal_ind_msg->msg_len); + break; + case WCN36XX_HAL_MISSED_BEACON_IND: + wcn36xx_smd_missed_beacon_ind(wcn, + hal_ind_msg->msg, + hal_ind_msg->msg_len); + break; + case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: + wcn36xx_smd_delete_sta_context_ind(wcn, + hal_ind_msg->msg, + hal_ind_msg->msg_len); + break; + default: + wcn36xx_err("SMD_EVENT (%d) not supported\n", + msg_header->msg_type); + } + list_del(wcn->hal_ind_queue.next); + kfree(hal_ind_msg->msg); + kfree(hal_ind_msg); + mutex_unlock(&wcn->hal_ind_mutex); +} +int wcn36xx_smd_open(struct wcn36xx *wcn) +{ + int ret = 0; + wcn->hal_ind_wq = create_freezable_workqueue("wcn36xx_smd_ind"); + if (!wcn->hal_ind_wq) { + wcn36xx_err("failed to allocate wq\n"); + ret = -ENOMEM; + goto out; + } + INIT_WORK(&wcn->hal_ind_work, wcn36xx_ind_smd_work); + INIT_LIST_HEAD(&wcn->hal_ind_queue); + mutex_init(&wcn->hal_ind_mutex); + + ret = wcn->ctrl_ops->open(wcn, wcn36xx_smd_rsp_process); + if (ret) { + wcn36xx_err("failed to open control channel\n"); + goto free_wq; + } + + return ret; + +free_wq: + destroy_workqueue(wcn->hal_ind_wq); +out: + return ret; +} + +void wcn36xx_smd_close(struct wcn36xx *wcn) +{ + wcn->ctrl_ops->close(); + destroy_workqueue(wcn->hal_ind_wq); + mutex_destroy(&wcn->hal_ind_mutex); +} diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h new file mode 100644 index 00000000000..008d03423db --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _SMD_H_ +#define _SMD_H_ + +#include "wcn36xx.h" + +/* Max shared size is 4k but we take less.*/ +#define WCN36XX_NV_FRAGMENT_SIZE 3072 + +#define WCN36XX_HAL_BUF_SIZE 4096 + +#define HAL_MSG_TIMEOUT 500 +#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400 +#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200 +/* The PNO version info be contained in the rsp msg */ +#define WCN36XX_FW_MSG_PNO_VERSION_MASK 0x8000 + +enum wcn36xx_fw_msg_result { + WCN36XX_FW_MSG_RESULT_SUCCESS = 0, + WCN36XX_FW_MSG_RESULT_SUCCESS_SYNC = 1, + + WCN36XX_FW_MSG_RESULT_MEM_FAIL = 5, +}; + +/******************************/ +/* SMD requests and responses */ +/******************************/ +struct wcn36xx_fw_msg_status_rsp { + u32 status; +} __packed; + +struct wcn36xx_hal_ind_msg { + struct list_head list; + u8 *msg; + size_t msg_len; +}; + +struct wcn36xx; + +int wcn36xx_smd_open(struct wcn36xx *wcn); +void wcn36xx_smd_close(struct wcn36xx *wcn); + +int wcn36xx_smd_load_nv(struct wcn36xx *wcn); +int wcn36xx_smd_start(struct wcn36xx *wcn); +int wcn36xx_smd_stop(struct wcn36xx *wcn); +int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode); +int wcn36xx_smd_start_scan(struct wcn36xx *wcn); +int wcn36xx_smd_end_scan(struct wcn36xx *wcn); +int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, + enum wcn36xx_hal_sys_mode mode); +int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn); +int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr); +int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index); +int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch); +int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid, + const u8 *sta_mac, + enum wcn36xx_hal_link_state state); +int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, const u8 *bssid, + bool update); +int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct sk_buff *skb_beacon, u16 tim_off, + u16 p2p_off); +int wcn36xx_smd_switch_channel(struct wcn36xx *wcn, + struct ieee80211_vif *vif, int ch); +int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + struct sk_buff *skb); +int wcn36xx_smd_set_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key, + u8 sta_index); +int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key); +int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 sta_index); +int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx); +int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim); +int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + int packet_type); +int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 arg5); +int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn); +void set_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap); +int get_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap); +void clear_feat_caps(u32 *bitmap, enum place_holder_in_cap_bitmap cap); + +int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + u16 tid, + u16 *ssn, + u8 direction, + u8 sta_index); +int wcn36xx_smd_add_ba(struct wcn36xx *wcn); +int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index); +int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index); + +int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); +#endif /* _SMD_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c new file mode 100644 index 00000000000..32bb26a0db2 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 "txrx.h" + +static inline int get_rssi0(struct wcn36xx_rx_bd *bd) +{ + return 100 - ((bd->phy_stat0 >> 24) & 0xff); +} + +int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) +{ + struct ieee80211_rx_status status; + struct ieee80211_hdr *hdr; + struct wcn36xx_rx_bd *bd; + u16 fc, sn; + + /* + * All fields must be 0, otherwise it can lead to + * unexpected consequences. + */ + memset(&status, 0, sizeof(status)); + + bd = (struct wcn36xx_rx_bd *)skb->data; + buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); + wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, + "BD <<< ", (char *)bd, + sizeof(struct wcn36xx_rx_bd)); + + skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len); + skb_pull(skb, bd->pdu.mpdu_header_off); + + status.mactime = 10; + status.freq = WCN36XX_CENTER_FREQ(wcn); + status.band = WCN36XX_BAND(wcn); + status.signal = -get_rssi0(bd); + status.antenna = 1; + status.rate_idx = 1; + status.flag = 0; + status.rx_flags = 0; + status.flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED | + RX_FLAG_DECRYPTED; + + wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag); + + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + + hdr = (struct ieee80211_hdr *) skb->data; + fc = __le16_to_cpu(hdr->frame_control); + sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); + + if (ieee80211_is_beacon(hdr->frame_control)) { + wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n", + skb, skb->len, fc, sn); + wcn36xx_dbg_dump(WCN36XX_DBG_BEACON_DUMP, "SKB <<< ", + (char *)skb->data, skb->len); + } else { + wcn36xx_dbg(WCN36XX_DBG_RX, "rx skb %p len %d fc %04x sn %d\n", + skb, skb->len, fc, sn); + wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "SKB <<< ", + (char *)skb->data, skb->len); + } + + ieee80211_rx_irqsafe(wcn->hw, skb); + + return 0; +} + +static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd, + u32 mpdu_header_len, + u32 len, + u16 tid) +{ + bd->pdu.mpdu_header_len = mpdu_header_len; + bd->pdu.mpdu_header_off = sizeof(*bd); + bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len + + bd->pdu.mpdu_header_off; + bd->pdu.mpdu_len = len; + bd->pdu.tid = tid; +} + +static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, + u8 *addr) +{ + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_vif *vif = NULL; + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (memcmp(vif->addr, addr, ETH_ALEN) == 0) + return vif_priv; + } + wcn36xx_warn("vif %pM not found\n", addr); + return NULL; +} +static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, + struct wcn36xx *wcn, + struct wcn36xx_vif **vif_priv, + struct wcn36xx_sta *sta_priv, + struct ieee80211_hdr *hdr, + bool bcast) +{ + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *__vif_priv = NULL; + bd->bd_rate = WCN36XX_BD_RATE_DATA; + + /* + * For not unicast frames mac80211 will not set sta pointer so use + * self_sta_index instead. + */ + if (sta_priv) { + __vif_priv = sta_priv->vif; + vif = container_of((void *)__vif_priv, + struct ieee80211_vif, + drv_priv); + + bd->dpu_sign = sta_priv->ucast_dpu_sign; + if (vif->type == NL80211_IFTYPE_STATION) { + bd->sta_index = sta_priv->bss_sta_index; + bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index; + } else if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) { + bd->sta_index = sta_priv->sta_index; + bd->dpu_desc_idx = sta_priv->dpu_desc_index; + } + } else { + __vif_priv = get_vif_by_addr(wcn, hdr->addr2); + bd->sta_index = __vif_priv->self_sta_index; + bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index; + bd->dpu_sign = __vif_priv->self_ucast_dpu_sign; + } + + if (ieee80211_is_nullfunc(hdr->frame_control) || + (sta_priv && !sta_priv->is_data_encrypted)) + bd->dpu_ne = 1; + + if (bcast) { + bd->ub = 1; + bd->ack_policy = 1; + } + *vif_priv = __vif_priv; +} + +static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, + struct wcn36xx *wcn, + struct wcn36xx_vif **vif_priv, + struct ieee80211_hdr *hdr, + bool bcast) +{ + struct wcn36xx_vif *__vif_priv = + get_vif_by_addr(wcn, hdr->addr2); + bd->sta_index = __vif_priv->self_sta_index; + bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index; + bd->dpu_ne = 1; + + /* default rate for unicast */ + if (ieee80211_is_mgmt(hdr->frame_control)) + bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ? + WCN36XX_BD_RATE_CTRL : + WCN36XX_BD_RATE_MGMT; + else if (ieee80211_is_ctl(hdr->frame_control)) + bd->bd_rate = WCN36XX_BD_RATE_CTRL; + else + wcn36xx_warn("frame control type unknown\n"); + + /* + * In joining state trick hardware that probe is sent as + * unicast even if address is broadcast. + */ + if (__vif_priv->is_joining && + ieee80211_is_probe_req(hdr->frame_control)) + bcast = false; + + if (bcast) { + /* broadcast */ + bd->ub = 1; + /* No ack needed not unicast */ + bd->ack_policy = 1; + bd->queue_id = WCN36XX_TX_B_WQ_ID; + } else + bd->queue_id = WCN36XX_TX_U_WQ_ID; + *vif_priv = __vif_priv; +} + +int wcn36xx_start_tx(struct wcn36xx *wcn, + struct wcn36xx_sta *sta_priv, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned long flags; + bool is_low = ieee80211_is_data(hdr->frame_control); + bool bcast = is_broadcast_ether_addr(hdr->addr1) || + is_multicast_ether_addr(hdr->addr1); + struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low); + + if (!bd) { + /* + * TX DXE are used in pairs. One for the BD and one for the + * actual frame. The BD DXE's has a preallocated buffer while + * the skb ones does not. If this isn't true something is really + * wierd. TODO: Recover from this situation + */ + + wcn36xx_err("bd address may not be NULL for BD DXE\n"); + return -EINVAL; + } + + memset(bd, 0, sizeof(*bd)); + + wcn36xx_dbg(WCN36XX_DBG_TX, + "tx skb %p len %d fc %04x sn %d %s %s\n", + skb, skb->len, __le16_to_cpu(hdr->frame_control), + IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)), + is_low ? "low" : "high", bcast ? "bcast" : "ucast"); + + wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len); + + bd->dpu_rf = WCN36XX_BMU_WQ_TX; + + bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; + if (bd->tx_comp) { + wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n"); + spin_lock_irqsave(&wcn->dxe_lock, flags); + if (wcn->tx_ack_skb) { + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + wcn36xx_warn("tx_ack_skb already set\n"); + return -EINVAL; + } + + wcn->tx_ack_skb = skb; + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + + /* Only one at a time is supported by fw. Stop the TX queues + * until the ack status gets back. + * + * TODO: Add watchdog in case FW does not answer + */ + ieee80211_stop_queues(wcn->hw); + } + + /* Data frames served first*/ + if (is_low) { + wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast); + wcn36xx_set_tx_pdu(bd, + ieee80211_is_data_qos(hdr->frame_control) ? + sizeof(struct ieee80211_qos_hdr) : + sizeof(struct ieee80211_hdr_3addr), + skb->len, sta_priv ? sta_priv->tid : 0); + } else { + /* MGMT and CTRL frames are handeld here*/ + wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast); + wcn36xx_set_tx_pdu(bd, + ieee80211_is_data_qos(hdr->frame_control) ? + sizeof(struct ieee80211_qos_hdr) : + sizeof(struct ieee80211_hdr_3addr), + skb->len, WCN36XX_TID); + } + + buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); + bd->tx_bd_sign = 0xbdbdbdbd; + + return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low); +} diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.h b/drivers/net/wireless/ath/wcn36xx/txrx.h new file mode 100644 index 00000000000..bbfbcf808c7 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/txrx.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _TXRX_H_ +#define _TXRX_H_ + +#include <linux/etherdevice.h> +#include "wcn36xx.h" + +/* TODO describe all properties */ +#define WCN36XX_802_11_HEADER_LEN 24 +#define WCN36XX_BMU_WQ_TX 25 +#define WCN36XX_TID 7 +/* broadcast wq ID */ +#define WCN36XX_TX_B_WQ_ID 0xA +#define WCN36XX_TX_U_WQ_ID 0x9 +/* bd_rate */ +#define WCN36XX_BD_RATE_DATA 0 +#define WCN36XX_BD_RATE_MGMT 2 +#define WCN36XX_BD_RATE_CTRL 3 + +struct wcn36xx_pdu { + u32 dpu_fb:8; + u32 adu_fb:8; + u32 pdu_id:16; + + /* 0x04*/ + u32 tail_pdu_idx:16; + u32 head_pdu_idx:16; + + /* 0x08*/ + u32 pdu_count:7; + u32 mpdu_data_off:9; + u32 mpdu_header_off:8; + u32 mpdu_header_len:8; + + /* 0x0c*/ + u32 reserved4:8; + u32 tid:4; + u32 reserved3:4; + u32 mpdu_len:16; +}; + +struct wcn36xx_rx_bd { + u32 bdt:2; + u32 ft:1; + u32 dpu_ne:1; + u32 rx_key_id:3; + u32 ub:1; + u32 rmf:1; + u32 uma_bypass:1; + u32 csr11:1; + u32 reserved0:1; + u32 scan_learn:1; + u32 rx_ch:4; + u32 rtsf:1; + u32 bsf:1; + u32 a2hf:1; + u32 st_auf:1; + u32 dpu_sign:3; + u32 dpu_rf:8; + + struct wcn36xx_pdu pdu; + + /* 0x14*/ + u32 addr3:8; + u32 addr2:8; + u32 addr1:8; + u32 dpu_desc_idx:8; + + /* 0x18*/ + u32 rxp_flags:23; + u32 rate_id:9; + + u32 phy_stat0; + u32 phy_stat1; + + /* 0x24 */ + u32 rx_times; + + u32 pmi_cmd[6]; + + /* 0x40 */ + u32 reserved7:4; + u32 reorder_slot_id:6; + u32 reorder_fwd_id:6; + u32 reserved6:12; + u32 reorder_code:4; + + /* 0x44 */ + u32 exp_seq_num:12; + u32 cur_seq_num:12; + u32 fr_type_subtype:8; + + /* 0x48 */ + u32 msdu_size:16; + u32 sub_fr_id:4; + u32 proc_order:4; + u32 reserved9:4; + u32 aef:1; + u32 lsf:1; + u32 esf:1; + u32 asf:1; +}; + +struct wcn36xx_tx_bd { + u32 bdt:2; + u32 ft:1; + u32 dpu_ne:1; + u32 fw_tx_comp:1; + u32 tx_comp:1; + u32 reserved1:1; + u32 ub:1; + u32 rmf:1; + u32 reserved0:12; + u32 dpu_sign:3; + u32 dpu_rf:8; + + struct wcn36xx_pdu pdu; + + /* 0x14*/ + u32 reserved5:7; + u32 queue_id:5; + u32 bd_rate:2; + u32 ack_policy:2; + u32 sta_index:8; + u32 dpu_desc_idx:8; + + u32 tx_bd_sign; + u32 reserved6; + u32 dxe_start_time; + u32 dxe_end_time; + + /*u32 tcp_udp_start_off:10; + u32 header_cks:16; + u32 reserved7:6;*/ +}; + +struct wcn36xx_sta; +struct wcn36xx; + +int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb); +int wcn36xx_start_tx(struct wcn36xx *wcn, + struct wcn36xx_sta *sta_priv, + struct sk_buff *skb); + +#endif /* _TXRX_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h new file mode 100644 index 00000000000..f0fb81dfd17 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> + * + * 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 _WCN36XX_H_ +#define _WCN36XX_H_ + +#include <linux/completion.h> +#include <linux/printk.h> +#include <linux/spinlock.h> +#include <net/mac80211.h> + +#include "hal.h" +#include "smd.h" +#include "txrx.h" +#include "dxe.h" +#include "pmc.h" +#include "debug.h" + +#define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" +#define WCN36XX_AGGR_BUFFER_SIZE 64 + +extern unsigned int wcn36xx_dbg_mask; + +enum wcn36xx_debug_mask { + WCN36XX_DBG_DXE = 0x00000001, + WCN36XX_DBG_DXE_DUMP = 0x00000002, + WCN36XX_DBG_SMD = 0x00000004, + WCN36XX_DBG_SMD_DUMP = 0x00000008, + WCN36XX_DBG_RX = 0x00000010, + WCN36XX_DBG_RX_DUMP = 0x00000020, + WCN36XX_DBG_TX = 0x00000040, + WCN36XX_DBG_TX_DUMP = 0x00000080, + WCN36XX_DBG_HAL = 0x00000100, + WCN36XX_DBG_HAL_DUMP = 0x00000200, + WCN36XX_DBG_MAC = 0x00000400, + WCN36XX_DBG_BEACON = 0x00000800, + WCN36XX_DBG_BEACON_DUMP = 0x00001000, + WCN36XX_DBG_PMC = 0x00002000, + WCN36XX_DBG_PMC_DUMP = 0x00004000, + WCN36XX_DBG_ANY = 0xffffffff, +}; + +#define wcn36xx_err(fmt, arg...) \ + printk(KERN_ERR pr_fmt("ERROR " fmt), ##arg) + +#define wcn36xx_warn(fmt, arg...) \ + printk(KERN_WARNING pr_fmt("WARNING " fmt), ##arg) + +#define wcn36xx_info(fmt, arg...) \ + printk(KERN_INFO pr_fmt(fmt), ##arg) + +#define wcn36xx_dbg(mask, fmt, arg...) do { \ + if (wcn36xx_dbg_mask & mask) \ + printk(KERN_DEBUG pr_fmt(fmt), ##arg); \ +} while (0) + +#define wcn36xx_dbg_dump(mask, prefix_str, buf, len) do { \ + if (wcn36xx_dbg_mask & mask) \ + print_hex_dump(KERN_DEBUG, pr_fmt(prefix_str), \ + DUMP_PREFIX_OFFSET, 32, 1, \ + buf, len, false); \ +} while (0) + +#define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value) +#define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band) +#define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq) +#define WCN36XX_LISTEN_INTERVAL(__wcn) (__wcn->hw->conf.listen_interval) +#define WCN36XX_FLAGS(__wcn) (__wcn->hw->flags) +#define WCN36XX_MAX_POWER(__wcn) (__wcn->hw->conf.chandef.chan->max_power) + +static inline void buff_to_be(u32 *buf, size_t len) +{ + int i; + for (i = 0; i < len; i++) + buf[i] = cpu_to_be32(buf[i]); +} + +struct nv_data { + int is_valid; + u8 table; +}; + +/* Interface for platform control path + * + * @open: hook must be called when wcn36xx wants to open control channel. + * @tx: sends a buffer. + */ +struct wcn36xx_platform_ctrl_ops { + int (*open)(void *drv_priv, void *rsp_cb); + void (*close)(void); + int (*tx)(char *buf, size_t len); + int (*get_hw_mac)(u8 *addr); + int (*smsm_change_state)(u32 clear_mask, u32 set_mask); +}; + +/** + * struct wcn36xx_vif - holds VIF related fields + * + * @bss_index: bss_index is initially set to 0xFF. bss_index is received from + * HW after first config_bss call and must be used in delete_bss and + * enter/exit_bmps. + */ +struct wcn36xx_vif { + struct list_head list; + struct wcn36xx_sta *sta; + u8 dtim_period; + enum ani_ed_type encrypt_type; + bool is_joining; + struct wcn36xx_hal_mac_ssid ssid; + + /* Power management */ + enum wcn36xx_power_state pw_state; + + u8 bss_index; + /* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */ + u8 self_sta_index; + u8 self_dpu_desc_index; + u8 self_ucast_dpu_sign; +}; + +/** + * struct wcn36xx_sta - holds STA related fields + * + * @tid: traffic ID that is used during AMPDU and in TX BD. + * @sta_index: STA index is returned from HW after config_sta call and is + * used in both SMD channel and TX BD. + * @dpu_desc_index: DPU descriptor index is returned from HW after config_sta + * call and is used in TX BD. + * @bss_sta_index: STA index is returned from HW after config_bss call and is + * used in both SMD channel and TX BD. See table bellow when it is used. + * @bss_dpu_desc_index: DPU descriptor index is returned from HW after + * config_bss call and is used in TX BD. + * ______________________________________________ + * | | STA | AP | + * |______________|_____________|_______________| + * | TX BD |bss_sta_index| sta_index | + * |______________|_____________|_______________| + * |all SMD calls |bss_sta_index| sta_index | + * |______________|_____________|_______________| + * |smd_delete_sta| sta_index | sta_index | + * |______________|_____________|_______________| + */ +struct wcn36xx_sta { + struct wcn36xx_vif *vif; + u16 aid; + u16 tid; + u8 sta_index; + u8 dpu_desc_index; + u8 ucast_dpu_sign; + u8 bss_sta_index; + u8 bss_dpu_desc_index; + bool is_data_encrypted; + /* Rates */ + struct wcn36xx_hal_supported_rates supported_rates; +}; +struct wcn36xx_dxe_ch; +struct wcn36xx { + struct ieee80211_hw *hw; + struct device *dev; + struct list_head vif_list; + + const struct firmware *nv; + + u8 fw_revision; + u8 fw_version; + u8 fw_minor; + u8 fw_major; + u32 fw_feat_caps[WCN36XX_HAL_CAPS_SIZE]; + u32 chip_version; + + /* extra byte for the NULL termination */ + u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1]; + u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1]; + + /* IRQs */ + int tx_irq; + int rx_irq; + void __iomem *mmio; + + struct wcn36xx_platform_ctrl_ops *ctrl_ops; + /* + * smd_buf must be protected with smd_mutex to garantee + * that all messages are sent one after another + */ + u8 *hal_buf; + size_t hal_rsp_len; + struct mutex hal_mutex; + struct completion hal_rsp_compl; + struct workqueue_struct *hal_ind_wq; + struct work_struct hal_ind_work; + struct mutex hal_ind_mutex; + struct list_head hal_ind_queue; + + /* DXE channels */ + struct wcn36xx_dxe_ch dxe_tx_l_ch; /* TX low */ + struct wcn36xx_dxe_ch dxe_tx_h_ch; /* TX high */ + struct wcn36xx_dxe_ch dxe_rx_l_ch; /* RX low */ + struct wcn36xx_dxe_ch dxe_rx_h_ch; /* RX high */ + + /* For synchronization of DXE resources from BH, IRQ and WQ contexts */ + spinlock_t dxe_lock; + bool queues_stopped; + + /* Memory pools */ + struct wcn36xx_dxe_mem_pool mgmt_mem_pool; + struct wcn36xx_dxe_mem_pool data_mem_pool; + + struct sk_buff *tx_ack_skb; + +#ifdef CONFIG_WCN36XX_DEBUGFS + /* Debug file system entry */ + struct wcn36xx_dfs_entry dfs; +#endif /* CONFIG_WCN36XX_DEBUGFS */ + +}; + +#define WCN36XX_CHIP_3660 0 +#define WCN36XX_CHIP_3680 1 + +static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, + u8 major, + u8 minor, + u8 version, + u8 revision) +{ + return (wcn->fw_major == major && + wcn->fw_minor == minor && + wcn->fw_version == version && + wcn->fw_revision == revision); +} +void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates); + +#endif /* _WCN36XX_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 990dd42ae79..c7a3465fd02 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -9,6 +9,7 @@ wil6210-y += wmi.o wil6210-y += interrupt.o wil6210-y += txrx.o wil6210-y += debug.o +wil6210-y += rx_reorder.o wil6210-$(CONFIG_WIL6210_TRACING) += trace.o # for tracing framework to find trace.h diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 61c302a6bde..820d4ebd932 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -104,41 +104,125 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type) return -EOPNOTSUPP; } -static int wil_cfg80211_get_station(struct wiphy *wiphy, - struct net_device *ndev, - u8 *mac, struct station_info *sinfo) +static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, + struct station_info *sinfo) { - struct wil6210_priv *wil = wiphy_to_wil(wiphy); - int rc; struct wmi_notify_req_cmd cmd = { - .cid = 0, + .cid = cid, .interval_usec = 0, }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_notify_req_done_event evt; + } __packed reply; + struct wil_net_stats *stats = &wil->sta[cid].stats; + int rc; - if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) - return -ENOENT; - - /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), - WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); + WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); if (rc) return rc; + wil_dbg_wmi(wil, "Link status for CID %d: {\n" + " MCS %d TSF 0x%016llx\n" + " BF status 0x%08x SNR 0x%08x SQI %d%%\n" + " Tx Tpt %d goodput %d Rx goodput %d\n" + " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", + cid, le16_to_cpu(reply.evt.bf_mcs), + le64_to_cpu(reply.evt.tsf), reply.evt.status, + le32_to_cpu(reply.evt.snr_val), + reply.evt.sqi, + le32_to_cpu(reply.evt.tx_tpt), + le32_to_cpu(reply.evt.tx_goodput), + le32_to_cpu(reply.evt.rx_goodput), + le16_to_cpu(reply.evt.my_rx_sector), + le16_to_cpu(reply.evt.my_tx_sector), + le16_to_cpu(reply.evt.other_rx_sector), + le16_to_cpu(reply.evt.other_tx_sector)); + sinfo->generation = wil->sinfo_gen; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled = STATION_INFO_RX_BYTES | + STATION_INFO_TX_BYTES | + STATION_INFO_RX_PACKETS | + STATION_INFO_TX_PACKETS | + STATION_INFO_RX_BITRATE | + STATION_INFO_TX_BITRATE | + STATION_INFO_RX_DROP_MISC | + STATION_INFO_TX_FAILED; + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; - sinfo->txrate.mcs = wil->stats.bf_mcs; - sinfo->filled |= STATION_INFO_RX_BITRATE; + sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; - sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + sinfo->rxrate.mcs = stats->last_mcs_rx; + sinfo->rx_bytes = stats->rx_bytes; + sinfo->rx_packets = stats->rx_packets; + sinfo->rx_dropped_misc = stats->rx_dropped; + sinfo->tx_bytes = stats->tx_bytes; + sinfo->tx_packets = stats->tx_packets; + sinfo->tx_failed = stats->tx_errors; if (test_bit(wil_status_fwconnected, &wil->status)) { sinfo->filled |= STATION_INFO_SIGNAL; - sinfo->signal = 12; /* TODO: provide real value */ + sinfo->signal = reply.evt.sqi; } - return 0; + return rc; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *ndev, + const u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + int cid = wil_find_cid(wil, mac); + + wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); + if (cid < 0) + return cid; + + rc = wil_cid_fill_sinfo(wil, cid, sinfo); + + return rc; +} + +/* + * Find @idx-th active STA for station dump. + */ +static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { + if (wil->sta[i].status == wil_sta_unused) + continue; + if (idx == 0) + return i; + idx--; + } + + return -ENOENT; +} + +static int wil_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + u8 *mac, struct station_info *sinfo) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + int cid = wil_find_cid_by_idx(wil, idx); + + if (cid < 0) + return -ENOENT; + + memcpy(mac, wil->sta[cid].addr, ETH_ALEN); + wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); + + rc = wil_cid_fill_sinfo(wil, cid, sinfo); + + return rc; } static int wil_cfg80211_change_iface(struct wiphy *wiphy, @@ -181,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"); @@ -198,11 +283,12 @@ 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; } wil->scan_request = request; + mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO); memset(&cmd, 0, sizeof(cmd)); cmd.cmd.num_channels = 0; @@ -221,8 +307,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, @@ -237,6 +328,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); @@ -316,12 +411,9 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, } conn.channel = ch - 1; - memcpy(conn.bssid, bss->bssid, 6); - memcpy(conn.dst_mac, bss->bssid, 6); - /* - * FW don't support scan after connection attempt - */ - set_bit(wil_status_dontscan, &wil->status); + memcpy(conn.bssid, bss->bssid, ETH_ALEN); + memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); + set_bit(wil_status_fwconnecting, &wil->status); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); @@ -330,7 +422,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); } @@ -352,6 +443,40 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, return rc; } +static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, + u64 *cookie) +{ + const u8 *buf = params->buf; + size_t len = params->len; + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + struct ieee80211_mgmt *mgmt_frame = (void *)buf; + struct wmi_sw_tx_req_cmd *cmd; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_sw_tx_complete_event evt; + } __packed evt; + + cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); + cmd->len = cpu_to_le16(len); + memcpy(cmd->payload, buf, len); + + rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len, + WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); + if (rc == 0) + rc = evt.evt.status; + + kfree(cmd); + + return rc; +} + static int wil_cfg80211_set_channel(struct wiphy *wiphy, struct cfg80211_chan_def *chandef) { @@ -402,6 +527,41 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy, return 0; } +static int wil_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, + u64 *cookie) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + /* TODO: handle duration */ + wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration); + + rc = wmi_set_channel(wil, chan->hw_value); + if (rc) + return rc; + + rc = wmi_rxon(wil, true); + + return rc; +} + +static int wil_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + int rc; + + wil_info(wil, "%s()\n", __func__); + + rc = wmi_rxon(wil, false); + + return rc; +} + static int wil_fix_bcon(struct wil6210_priv *wil, struct cfg80211_beacon_data *bcon) { @@ -450,18 +610,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); @@ -485,11 +647,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; } @@ -499,17 +663,36 @@ 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; } +static int wil_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *dev, const u8 *mac) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + + mutex_lock(&wil->mutex); + wil6210_disconnect(wil, mac); + mutex_unlock(&wil->mutex); + + return 0; +} + static struct cfg80211_ops wil_cfg80211_ops = { .scan = wil_cfg80211_scan, .connect = wil_cfg80211_connect, .disconnect = wil_cfg80211_disconnect, .change_virtual_intf = wil_cfg80211_change_iface, .get_station = wil_cfg80211_get_station, + .dump_station = wil_cfg80211_dump_station, + .remain_on_channel = wil_remain_on_channel, + .cancel_remain_on_channel = wil_cancel_remain_on_channel, + .mgmt_tx = wil_cfg80211_mgmt_tx, .set_monitor_channel = wil_cfg80211_set_channel, .add_key = wil_cfg80211_add_key, .del_key = wil_cfg80211_del_key, @@ -517,6 +700,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { /* AP mode */ .start_ap = wil_cfg80211_start_ap, .stop_ap = wil_cfg80211_stop_ap, + .del_station = wil_cfg80211_del_station, }; static void wil_wiphy_init(struct wiphy *wiphy) @@ -542,7 +726,7 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; /* TODO: figure this out */ - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; wiphy->cipher_suites = wil_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 1caa31992a7..8d4bc4bfb66 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -26,14 +26,16 @@ /* Nasty hack. Better have per device instances */ static u32 mem_addr; static u32 dbg_txdesc_index; +static u32 dbg_vring_index; /* 24+ for Rx, 0..23 for Tx */ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, - const char *name, struct vring *vring) + const char *name, struct vring *vring, + char _s, char _h) { void __iomem *x = wmi_addr(wil, vring->hwtail); seq_printf(s, "VRING %s = {\n", name); - seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); + seq_printf(s, " pa = %pad\n", &vring->pa); seq_printf(s, " va = 0x%p\n", vring->va); seq_printf(s, " size = %d\n", vring->size); seq_printf(s, " swtail = %d\n", vring->swtail); @@ -50,8 +52,8 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, volatile struct vring_tx_desc *d = &vring->va[i].tx; if ((i % 64) == 0 && (i != 0)) seq_printf(s, "\n"); - seq_printf(s, "%s", (d->dma.status & BIT(0)) ? - "S" : (vring->ctx[i].skb ? "H" : "h")); + seq_printf(s, "%c", (d->dma.status & BIT(0)) ? + _s : (vring->ctx[i].skb ? _h : 'h')); } seq_printf(s, "\n"); } @@ -63,14 +65,19 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) uint i; struct wil6210_priv *wil = s->private; - wil_print_vring(s, wil, "rx", &wil->vring_rx); + wil_print_vring(s, wil, "rx", &wil->vring_rx, 'S', '_'); for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { struct vring *vring = &(wil->vring_tx[i]); if (vring->va) { + int cid = wil->vring2cid_tid[i][0]; + int tid = wil->vring2cid_tid[i][1]; char name[10]; snprintf(name, sizeof(name), "tx_%2d", i); - wil_print_vring(s, wil, name, vring); + + seq_printf(s, "\n%pM CID %d TID %d\n", + wil->sta[cid].addr, cid, tid); + wil_print_vring(s, wil, name, vring, '_', 'H'); } } @@ -390,57 +397,98 @@ static const struct file_operations fops_reset = { .write = wil_write_file_reset, .open = simple_open, }; -/*---------Tx descriptor------------*/ +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) { struct wil6210_priv *wil = s->private; - struct vring *vring = &(wil->vring_tx[0]); + struct vring *vring; + bool tx = (dbg_vring_index < WIL6210_MAX_TX_RINGS); + if (tx) + vring = &(wil->vring_tx[dbg_vring_index]); + else + vring = &wil->vring_rx; if (!vring->va) { - seq_printf(s, "No Tx VRING\n"); + if (tx) + seq_printf(s, "No Tx[%2d] VRING\n", dbg_vring_index); + else + seq_puts(s, "No Rx VRING\n"); return 0; } if (dbg_txdesc_index < vring->size) { + /* use struct vring_tx_desc for Rx as well, + * only field used, .dma.length, is the same + */ volatile struct vring_tx_desc *d = &(vring->va[dbg_txdesc_index].tx); volatile u32 *u = (volatile u32 *)d; struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb; - seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); + if (tx) + seq_printf(s, "Tx[%2d][%3d] = {\n", dbg_vring_index, + dbg_txdesc_index); + else + seq_printf(s, "Rx[%3d] = {\n", dbg_txdesc_index); seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", u[0], u[1], u[2], u[3]); seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", u[4], u[5], u[6], u[7]); - seq_printf(s, " SKB = %p\n", skb); + seq_printf(s, " SKB = 0x%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 { - seq_printf(s, "TxDesc index (%d) >= size (%d)\n", - dbg_txdesc_index, vring->size); + if (tx) + seq_printf(s, "[%2d] TxDesc index (%d) >= size (%d)\n", + dbg_vring_index, dbg_txdesc_index, + vring->size); + else + seq_printf(s, "RxDesc index (%d) >= size (%d)\n", + dbg_txdesc_index, vring->size); } return 0; @@ -570,6 +618,69 @@ static const struct file_operations fops_temp = { .llseek = seq_lseek, }; +/*---------Station matrix------------*/ +static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) +{ + int i; + u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size; + seq_printf(s, "0x%03x [", r->head_seq_num); + for (i = 0; i < r->buf_size; i++) { + if (i == index) + seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|'); + else + seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_'); + } + seq_puts(s, "]\n"); +} + +static int wil_sta_debugfs_show(struct seq_file *s, void *data) +{ + struct wil6210_priv *wil = s->private; + int i, tid; + + for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { + struct wil_sta_info *p = &wil->sta[i]; + char *status = "unknown"; + switch (p->status) { + case wil_sta_unused: + status = "unused "; + break; + case wil_sta_conn_pending: + status = "pending "; + break; + case wil_sta_connected: + status = "connected"; + break; + } + 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++) { + struct wil_tid_ampdu_rx *r = p->tid_rx[tid]; + if (r) { + seq_printf(s, "[%2d] ", tid); + wil_print_rxtid(s, r); + } + } + } + } + + return 0; +} + +static int wil_sta_seq_open(struct inode *inode, struct file *file) +{ + return single_open(file, wil_sta_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_sta = { + .open = wil_sta_seq_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek, +}; + /*----------------*/ int wil6210_debugfs_init(struct wil6210_priv *wil) { @@ -581,9 +692,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); - debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); - debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, + debugfs_create_file("stations", S_IRUGO, dbg, wil, &fops_sta); + debugfs_create_file("desc", S_IRUGO, dbg, wil, &fops_txdesc); + debugfs_create_u32("desc_index", S_IRUGO | S_IWUSR, dbg, &dbg_txdesc_index); + debugfs_create_u32("vring_index", S_IRUGO | S_IWUSR, dbg, + &dbg_vring_index); + debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 8205d3e4ab6..73593aa3cd9 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -156,6 +156,19 @@ void wil6210_enable_irq(struct wil6210_priv *wil) iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICC)); + /* interrupt moderation parameters */ + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { + /* disable interrupt moderation for monitor + * to get better timestamp precision + */ + iowrite32(0, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + } else { + iowrite32(WIL6210_ITR_TRSH, + wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); + iowrite32(BIT_DMA_ITR_CNT_CRL_EN, + wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + } + wil6210_unmask_irq_pseudo(wil); wil6210_unmask_irq_tx(wil); wil6210_unmask_irq_rx(wil); @@ -182,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) @@ -213,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) @@ -306,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) { @@ -315,7 +338,7 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) } if (isr) - wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + wil_dbg_irq(wil, "un-handled MISC ISR bits 0x%08x\n", isr); wil->isr_misc = 0; @@ -480,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 0a2844c48a6..11e6d9d22ea 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -16,8 +16,14 @@ #include <linux/moduleparam.h> #include <linux/if_arp.h> +#include <linux/etherdevice.h> #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, @@ -52,29 +58,74 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, __raw_writel(*s++, d++); } -static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +static void wil_disconnect_cid(struct wil6210_priv *wil, int cid) { uint i; - struct net_device *ndev = wil_to_ndev(wil); + struct wil_sta_info *sta = &wil->sta[cid]; - wil_dbg_misc(wil, "%s()\n", __func__); + 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; + } - wil_link_off(wil); - if (test_bit(wil_status_fwconnected, &wil->status)) { - clear_bit(wil_status_fwconnected, &wil->status); - cfg80211_disconnected(ndev, - WLAN_STATUS_UNSPECIFIED_FAILURE, - NULL, 0, GFP_KERNEL); - } else if (test_bit(wil_status_fwconnecting, &wil->status)) { - cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); + for (i = 0; i < WIL_STA_TID_NUM; i++) { + struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; + sta->tid_rx[i] = NULL; + wil_tid_ampdu_rx_free(wil, r); + } + for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { + if (wil->vring2cid_tid[i][0] == cid) + wil_vring_fini_tx(wil, i); } - clear_bit(wil_status_fwconnecting, &wil->status); - for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) - wil_vring_fini_tx(wil, i); + memset(&sta->stats, 0, sizeof(sta->stats)); +} - clear_bit(wil_status_dontscan, &wil->status); +static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) +{ + int cid = -ENOENT; + struct net_device *ndev = wil_to_ndev(wil); + struct wireless_dev *wdev = wil->wdev; + + might_sleep(); + if (bssid) { + cid = wil_find_cid(wil, bssid); + wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); + } else { + wil_dbg_misc(wil, "%s(all)\n", __func__); + } + + if (cid >= 0) /* disconnect 1 peer */ + wil_disconnect_cid(wil, cid); + else /* disconnect all */ + for (cid = 0; cid < WIL6210_MAX_CID; cid++) + wil_disconnect_cid(wil, cid); + + /* link state */ + switch (wdev->iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + wil_link_off(wil); + if (test_bit(wil_status_fwconnected, &wil->status)) { + clear_bit(wil_status_fwconnected, &wil->status); + cfg80211_disconnected(ndev, + WLAN_STATUS_UNSPECIFIED_FAILURE, + NULL, 0, GFP_KERNEL); + } else if (test_bit(wil_status_fwconnecting, &wil->status)) { + cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } + clear_bit(wil_status_fwconnecting, &wil->status); + break; + default: + /* AP-like interface and monitor: + * never scan, always connected + */ + if (bssid) + cfg80211_del_sta(ndev, bssid, GFP_KERNEL); + break; + } } static void wil_disconnect_worker(struct work_struct *work) @@ -82,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) @@ -97,12 +150,82 @@ static void wil_connect_timer_fn(ulong x) schedule_work(&wil->disconnect_worker); } +static void wil_scan_timer_fn(ulong x) +{ + struct wil6210_priv *wil = (void *)x; + + clear_bit(wil_status_fwready, &wil->status); + wil_err(wil, "Scan timeout detected, start fw error recovery\n"); + schedule_work(&wil->fw_error_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; + + /* increment @recovery_count if less then WIL6210_FW_RECOVERY_TO + * passed since last recovery attempt + */ + if (time_is_after_jiffies(wil->last_fw_recovery + + WIL6210_FW_RECOVERY_TO)) + wil->recovery_count++; + else + wil->recovery_count = 1; /* fw was alive for a long time */ + + if (wil->recovery_count > WIL6210_FW_RECOVERY_RETRIES) { + wil_err(wil, "too many recovery attempts (%d), giving up\n", + wil->recovery_count); + return; + } + + wil->last_fw_recovery = jiffies; + + 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 (try %d)...\n", + wil->recovery_count); + 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; + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + if (!wil->vring_tx[i].va) + return i; + } + return -EINVAL; +} + static void wil_connect_worker(struct work_struct *work) { int rc; struct wil6210_priv *wil = container_of(work, struct wil6210_priv, connect_worker); int cid = wil->pending_connect_cid; + int ringid = wil_find_free_vring(wil); if (cid < 0) { wil_err(wil, "No connection pending\n"); @@ -111,16 +234,22 @@ static void wil_connect_worker(struct work_struct *work) wil_dbg_wmi(wil, "Configure for connection CID %d\n", cid); - rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, cid, 0); + rc = wil_vring_init_tx(wil, ringid, WIL6210_TX_RING_SIZE, cid, 0); wil->pending_connect_cid = -1; - if (rc == 0) + if (rc == 0) { + wil->sta[cid].status = wil_sta_connected; wil_link_on(wil); + } else { + wil->sta[cid].status = wil_sta_unused; + } } int wil_priv_init(struct wil6210_priv *wil) { wil_dbg_misc(wil, "%s()\n", __func__); + memset(wil->sta, 0, sizeof(wil->sta)); + mutex_init(&wil->mutex); mutex_init(&wil->wmi_mutex); @@ -128,10 +257,12 @@ int wil_priv_init(struct wil6210_priv *wil) wil->pending_connect_cid = -1; setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + setup_timer(&wil->scan_timer, wil_scan_timer_fn, (ulong)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); @@ -146,10 +277,12 @@ int wil_priv_init(struct wil6210_priv *wil) return -EAGAIN; } + wil->last_fw_recovery = jiffies; + return 0; } -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid) { del_timer_sync(&wil->connect_timer); _wil6210_disconnect(wil, bssid); @@ -157,8 +290,12 @@ void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) void wil_priv_deinit(struct wil6210_priv *wil) { + del_timer_sync(&wil->scan_timer); 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); @@ -166,40 +303,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 */ - wil_dbg_misc(wil, "Reset completed\n"); + /* 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)); + + 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) @@ -219,8 +394,8 @@ static int wil_wait_for_fw_ready(struct wil6210_priv *wil) wil_err(wil, "Firmware not ready\n"); return -ETIME; } else { - wil_dbg_misc(wil, "FW ready after %d ms\n", - jiffies_to_msecs(to-left)); + wil_info(wil, "FW ready after %d ms. HW version 0x%08x\n", + jiffies_to_msecs(to-left), wil->hw_version); } return 0; } @@ -234,11 +409,25 @@ 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); + del_timer_sync(&wil->scan_timer); + cfg80211_scan_done(wil->scan_request, true); + wil->scan_request = NULL; + } + wil6210_disable_irq(wil); - wil->status = 0; wmi_event_flush(wil); @@ -248,9 +437,11 @@ 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; - INIT_COMPLETION(wil->wmi_ready); + reinit_completion(&wil->wmi_ready); /* TODO: release MAC reset */ wil6210_enable_irq(wil); @@ -261,6 +452,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) { @@ -288,6 +484,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; @@ -329,6 +527,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; } @@ -346,10 +545,14 @@ 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); if (wil->scan_request) { + del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, true); wil->scan_request = NULL; } @@ -370,3 +573,19 @@ int wil_down(struct wil6210_priv *wil) return rc; } + +int wil_find_cid(struct wil6210_priv *wil, const u8 *mac) +{ + int i; + int rc = -ENOENT; + + for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { + if ((wil->sta[i].status != wil_sta_unused) && + ether_addr_equal(wil->sta[i].addr, mac)) { + rc = i; + break; + } + } + + return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index 717178f09aa..106b6dcb773 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -32,12 +32,26 @@ static int wil_stop(struct net_device *ndev) return wil_down(wil); } +static int wil_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct wil6210_priv *wil = ndev_to_wil(ndev); + + if (new_mtu < 68 || new_mtu > IEEE80211_MAX_DATA_LEN_DMG) + return -EINVAL; + + wil_dbg_misc(wil, "change MTU %d -> %d\n", ndev->mtu, new_mtu); + ndev->mtu = new_mtu; + + return 0; +} + static const struct net_device_ops wil_netdev_ops = { .ndo_open = wil_open, .ndo_stop = wil_stop, .ndo_start_xmit = wil_start_xmit, .ndo_set_mac_address = eth_mac_addr, .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = wil_change_mtu, }; static int wil6210_netdev_poll_rx(struct napi_struct *napi, int budget) @@ -127,8 +141,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 eb1dc7ad80f..1e2e07b9d13 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -41,36 +41,36 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil) switch (use_msi) { case 3: case 1: + wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); + break; case 0: + wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n"); break; default: - wil_err(wil, "Invalid use_msi=%d, default to 1\n", - use_msi); + wil_err(wil, "Invalid use_msi=%d, default to 1\n", use_msi); use_msi = 1; } - wil->n_msi = use_msi; - if (wil->n_msi) { - wil_dbg_misc(wil, "Setup %d MSI interrupts\n", use_msi); - rc = pci_enable_msi_block(pdev, wil->n_msi); - if (rc && (wil->n_msi == 3)) { - wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); - wil->n_msi = 1; - rc = pci_enable_msi_block(pdev, wil->n_msi); - } - if (rc) { - wil_err(wil, "pci_enable_msi failed, use INTx\n"); - wil->n_msi = 0; - } - } else { - wil_dbg_misc(wil, "MSI interrupts disabled, use INTx\n"); + + if (use_msi == 3 && pci_enable_msi_range(pdev, 3, 3) < 0) { + wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); + use_msi = 1; + } + + if (use_msi == 1 && pci_enable_msi(pdev)) { + wil_err(wil, "pci_enable_msi failed, use INTx\n"); + use_msi = 0; } + wil->n_msi = use_msi; + rc = wil6210_init_irq(wil, pdev->irq); if (rc) 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; @@ -138,7 +138,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_release_reg; } /* rollback to err_iounmap */ - dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + dev_info(&pdev->dev, "CSR at %pR -> 0x%p\n", &pdev->resource[0], csr); wil = wil_if_alloc(dev, csr); if (IS_ERR(wil)) { @@ -151,6 +151,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) { @@ -197,7 +198,6 @@ static void wil_pcie_remove(struct pci_dev *pdev) pci_iounmap(pdev, wil->csr); pci_release_region(pdev, 0); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c new file mode 100644 index 00000000000..747ae127587 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -0,0 +1,201 @@ +#include "wil6210.h" +#include "txrx.h" + +#define SEQ_MODULO 0x1000 +#define SEQ_MASK 0xfff + +static inline int seq_less(u16 sq1, u16 sq2) +{ + return ((sq1 - sq2) & SEQ_MASK) > (SEQ_MODULO >> 1); +} + +static inline u16 seq_inc(u16 sq) +{ + return (sq + 1) & SEQ_MASK; +} + +static inline u16 seq_sub(u16 sq1, u16 sq2) +{ + return (sq1 - sq2) & SEQ_MASK; +} + +static inline int reorder_index(struct wil_tid_ampdu_rx *r, u16 seq) +{ + return seq_sub(seq, r->ssn) % r->buf_size; +} + +static void wil_release_reorder_frame(struct wil6210_priv *wil, + struct wil_tid_ampdu_rx *r, + int index) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct sk_buff *skb = r->reorder_buf[index]; + + if (!skb) + goto no_frame; + + /* release the frame from the reorder ring buffer */ + r->stored_mpdu_num--; + r->reorder_buf[index] = NULL; + wil_netif_rx_any(skb, ndev); + +no_frame: + r->head_seq_num = seq_inc(r->head_seq_num); +} + +static void wil_release_reorder_frames(struct wil6210_priv *wil, + struct wil_tid_ampdu_rx *r, + u16 hseq) +{ + int index; + + /* note: this function is never called with + * hseq preceding r->head_seq_num, i.e it is always true + * !seq_less(hseq, r->head_seq_num) + * and thus on loop exit it should be + * r->head_seq_num == hseq + */ + while (seq_less(r->head_seq_num, hseq) && r->stored_mpdu_num) { + index = reorder_index(r, r->head_seq_num); + wil_release_reorder_frame(wil, r, index); + } + r->head_seq_num = hseq; +} + +static void wil_reorder_release(struct wil6210_priv *wil, + struct wil_tid_ampdu_rx *r) +{ + int index = reorder_index(r, r->head_seq_num); + + while (r->reorder_buf[index]) { + wil_release_reorder_frame(wil, r, index); + index = reorder_index(r, r->head_seq_num); + } +} + +void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct vring_rx_desc *d = wil_skb_rxdesc(skb); + int tid = wil_rxdesc_tid(d); + int cid = wil_rxdesc_cid(d); + int mid = wil_rxdesc_mid(d); + u16 seq = wil_rxdesc_seq(d); + struct wil_sta_info *sta = &wil->sta[cid]; + struct wil_tid_ampdu_rx *r = sta->tid_rx[tid]; + u16 hseq; + int index; + + wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n", + mid, cid, tid, seq); + + if (!r) { + wil_netif_rx_any(skb, ndev); + return; + } + + hseq = r->head_seq_num; + + spin_lock(&r->reorder_lock); + + /** Due to the race between WMI events, where BACK establishment + * reported, and data Rx, few packets may be pass up before reorder + * buffer get allocated. Catch up by pretending SSN is what we + * see in the 1-st Rx packet + */ + if (r->first_time) { + r->first_time = false; + if (seq != r->head_seq_num) { + wil_err(wil, "Error: 1-st frame with wrong sequence" + " %d, should be %d. Fixing...\n", seq, + r->head_seq_num); + r->head_seq_num = seq; + r->ssn = seq; + } + } + + /* frame with out of date sequence number */ + if (seq_less(seq, r->head_seq_num)) { + dev_kfree_skb(skb); + goto out; + } + + /* + * If frame the sequence number exceeds our buffering window + * size release some previous frames to make room for this one. + */ + if (!seq_less(seq, r->head_seq_num + r->buf_size)) { + hseq = seq_inc(seq_sub(seq, r->buf_size)); + /* release stored frames up to new head to stack */ + wil_release_reorder_frames(wil, r, hseq); + } + + /* Now the new frame is always in the range of the reordering buffer */ + + index = reorder_index(r, seq); + + /* check if we already stored this frame */ + if (r->reorder_buf[index]) { + dev_kfree_skb(skb); + goto out; + } + + /* + * If the current MPDU is in the right order and nothing else + * is stored we can process it directly, no need to buffer it. + * If it is first but there's something stored, we may be able + * to release frames after this one. + */ + if (seq == r->head_seq_num && r->stored_mpdu_num == 0) { + r->head_seq_num = seq_inc(r->head_seq_num); + wil_netif_rx_any(skb, ndev); + goto out; + } + + /* put the frame in the reordering buffer */ + r->reorder_buf[index] = skb; + r->reorder_time[index] = jiffies; + r->stored_mpdu_num++; + wil_reorder_release(wil, r); + +out: + spin_unlock(&r->reorder_lock); +} + +struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, + int size, u16 ssn) +{ + struct wil_tid_ampdu_rx *r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + return NULL; + + r->reorder_buf = + kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL); + r->reorder_time = + kcalloc(size, sizeof(unsigned long), GFP_KERNEL); + if (!r->reorder_buf || !r->reorder_time) { + kfree(r->reorder_buf); + kfree(r->reorder_time); + kfree(r); + return NULL; + } + + spin_lock_init(&r->reorder_lock); + r->ssn = ssn; + r->head_seq_num = ssn; + r->buf_size = size; + r->stored_mpdu_num = 0; + r->first_time = true; + return r; +} + +void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, + struct wil_tid_ampdu_rx *r) +{ + if (!r) + return; + wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size); + kfree(r->reorder_buf); + kfree(r->reorder_time); + kfree(r); +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index d505b2676a7..0784ef3d4ce 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -21,6 +21,7 @@ #include <linux/ip.h> #include <linux/ipv6.h> #include <net/ipv6.h> +#include <linux/prefetch.h> #include "wil6210.h" #include "wmi.h" @@ -63,6 +64,22 @@ static inline int wil_vring_avail_tx(struct vring *vring) return vring->size - used - 1; } +/** + * wil_vring_wmark_low - low watermark for available descriptor space + */ +static inline int wil_vring_wmark_low(struct vring *vring) +{ + return vring->size/8; +} + +/** + * wil_vring_wmark_high - high watermark for available descriptor space + */ +static inline int wil_vring_wmark_high(struct vring *vring) +{ + return vring->size/4; +} + static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) { struct device *dev = wil_to_dev(wil); @@ -97,12 +114,29 @@ static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) _d->dma.status = TX_DMA_STATUS_DU; } - wil_dbg_misc(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, - vring->va, (unsigned long long)vring->pa, vring->ctx); + wil_dbg_misc(wil, "vring[%d] 0x%p:%pad 0x%p\n", vring->size, + vring->va, &vring->pa, vring->ctx); 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) { @@ -121,15 +155,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); @@ -343,6 +369,9 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, u16 dmalen; u8 ftype; u8 ds_bits; + int cid; + struct wil_net_stats *stats; + BUILD_BUG_ON(sizeof(struct vring_rx_desc) > sizeof(skb->cb)); @@ -377,11 +406,15 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, } skb_trim(skb, dmalen); + prefetch(skb->data); + wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); - - wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); + cid = wil_rxdesc_cid(d); + stats = &wil->sta[cid].stats; + stats->last_mcs_rx = wil_rxdesc_mcs(d); + wil->stats.last_mcs_rx = stats->last_mcs_rx; /* use radiotap header only if required */ if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) @@ -469,21 +502,28 @@ static int wil_rx_refill(struct wil6210_priv *wil, int count) * Pass Rx packet to the netif. Update statistics. * Called in softirq context (NAPI poll). */ -static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +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); + int cid = wil_rxdesc_cid(d); + struct wil_net_stats *stats = &wil->sta[cid].stats; 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; - - } else { - ndev->stats.rx_dropped++; + stats->rx_bytes += len; } } @@ -512,12 +552,18 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota) skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); - + wil_netif_rx_any(skb, ndev); } else { + struct ethhdr *eth = (void *)skb->data; + skb->protocol = eth_type_trans(skb, ndev); + + if (is_unicast_ether_addr(eth->h_dest)) + wil_rx_reorder(wil, skb); + else + wil_netif_rx_any(skb, ndev); } - wil_netif_rx_any(skb, ndev); } wil_rx_refill(wil, v->size); } @@ -527,6 +573,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) @@ -567,7 +618,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, @@ -583,6 +634,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); @@ -590,11 +642,15 @@ 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) goto out; + wil->vring2cid_tid[id][0] = cid; + wil->vring2cid_tid[id][1] = tid; + cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), @@ -610,6 +666,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); @@ -622,23 +680,116 @@ 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); } static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, struct sk_buff *skb) { - struct vring *v = &wil->vring_tx[0]; + int i; + struct ethhdr *eth = (void *)skb->data; + int cid = wil_find_cid(wil, eth->h_dest); + + if (cid < 0) + return NULL; - if (v->va) - return v; + 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) { + struct vring *v = &wil->vring_tx[i]; + wil_dbg_txrx(wil, "%s(%pM) -> [%d]\n", + __func__, eth->h_dest, i); + if (v->va) { + return v; + } else { + wil_dbg_txrx(wil, "vring[%d] not valid\n", i); + return NULL; + } + } + } return NULL; } +static void wil_set_da_for_vring(struct wil6210_priv *wil, + struct sk_buff *skb, int vring_index) +{ + struct ethhdr *eth = (void *)skb->data; + int cid = wil->vring2cid_tid[vring_index][0]; + memcpy(eth->h_dest, wil->sta[cid].addr, ETH_ALEN); +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb); +/* + * Find 1-st vring and return it; set dest address for this vring in skb + * duplicate skb and send it to other active vrings + */ +static struct vring *wil_tx_bcast(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v, *v2; + struct sk_buff *skb2; + int i; + u8 cid; + + /* find 1-st vring eligible for data */ + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + v = &wil->vring_tx[i]; + 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"); + + return NULL; + +found: + wil_dbg_txrx(wil, "BCAST -> ring %d\n", i); + wil_set_da_for_vring(wil, skb, i); + + /* find other active vrings and duplicate skb for each */ + for (i++; i < WIL6210_MAX_TX_RINGS; i++) { + 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); + wil_set_da_for_vring(wil, skb2, i); + wil_tx_vring(wil, v2, skb2); + } else { + wil_err(wil, "skb_copy failed\n"); + } + } + + return v; +} + static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, int vring_index) { @@ -664,6 +815,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) @@ -673,9 +831,12 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil, if (skb->ip_summed != CHECKSUM_PARTIAL) return 0; + d->dma.b11 = ETH_HLEN; /* MAC header length */ + switch (skb->protocol) { case cpu_to_be16(ETH_P_IP): protocol = ip_hdr(skb)->protocol; + d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS); break; case cpu_to_be16(ETH_P_IPV6): protocol = ipv6_hdr(skb)->nexthdr; @@ -701,8 +862,6 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil, } d->dma.ip_length = skb_network_header_len(skb); - d->dma.b11 = ETH_HLEN; /* MAC header length */ - d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS); /* Enable TCP/UDP checksum */ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS); /* Calculate pseudo-header */ @@ -727,8 +886,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); @@ -736,19 +893,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, } _d = &(vring->va[i].tx); - /* FIXME FW can accept only unicast frames for the peer */ - memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); - pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - wil_dbg_txrx(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), - skb->data, (unsigned long long)pa); + wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb), + skb->data, &pa); wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); 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 */ @@ -758,8 +913,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; @@ -774,8 +929,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 */ @@ -804,7 +964,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; @@ -812,12 +971,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); @@ -832,12 +986,17 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, 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)) { @@ -848,16 +1007,25 @@ 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 */ - vring = wil_find_tx_vring(wil, skb); + if (is_unicast_ether_addr(eth->h_dest)) { + vring = wil_find_tx_vring(wil, skb); + } else { + vring = wil_tx_bcast(wil, skb); + } if (!vring) { - wil_err(wil, "No Tx VRING available\n"); + wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest); goto drop; } /* 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) < wil_vring_wmark_low(vring)) + netif_tx_stop_all_queues(wil_to_ndev(wil)); + switch (rc) { case 0: /* statistics will be updated on the tx_complete */ @@ -887,66 +1055,84 @@ 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++; - ndev->stats.tx_bytes += skb->len; - } else { - ndev->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; + + 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); + 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) + if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) netif_tx_wake_all_queues(wil_to_ndev(wil)); return done; diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index b3828279204..bc5706a4f00 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -436,4 +436,11 @@ static inline struct vring_rx_desc *wil_skb_rxdesc(struct sk_buff *skb) return (void *)skb->cb; } +void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev); +void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb); +struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, + int size, u16 ssn); +void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, + struct wil_tid_ampdu_rx *r); + #endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index c4a51638736..e25edc52398 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -35,10 +35,14 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MEM_SIZE (2*1024*1024UL) #define WIL6210_RX_RING_SIZE (128) -#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (512) #define WIL6210_MAX_TX_RINGS (24) /* HW limit */ #define WIL6210_MAX_CID (8) /* HW limit */ #define WIL6210_NAPI_BUDGET (16) /* arbitrary */ +#define WIL6210_ITR_TRSH (10000) /* arbitrary - about 15 IRQs/msec */ +#define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */ +#define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000) +#define WIL6210_SCAN_TO msecs_to_jiffies(10000) /* Hardware definitions begin */ @@ -73,23 +77,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) @@ -104,13 +106,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) + \ @@ -124,6 +135,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 */ @@ -183,12 +219,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; @@ -203,6 +246,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, @@ -210,10 +261,52 @@ 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; +/** + * struct tid_ampdu_rx - TID aggregation information (Rx). + * + * @reorder_buf: buffer to reorder incoming aggregated MPDUs + * @reorder_time: jiffies when skb was added + * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) + * @reorder_timer: releases expired frames from the reorder buffer. + * @last_rx: jiffies of last rx activity + * @head_seq_num: head sequence number in reordering buffer. + * @stored_mpdu_num: number of MPDUs in reordering buffer + * @ssn: Starting Sequence Number expected to be aggregated. + * @buf_size: buffer size for incoming A-MPDUs + * @timeout: reset timer value (in TUs). + * @dialog_token: dialog token for aggregation session + * @rcu_head: RCU head used for freeing this struct + * @reorder_lock: serializes access to reorder buffer, see below. + * + * This structure's lifetime is managed by RCU, assignments to + * the array holding it must hold the aggregation mutex. + * + * The @reorder_lock is used to protect the members of this + * struct, except for @timeout, @buf_size and @dialog_token, + * which are constant across the lifetime of the struct (the + * dialog token being used only for debugging). + */ +struct wil_tid_ampdu_rx { + spinlock_t reorder_lock; /* see above */ + struct sk_buff **reorder_buf; + unsigned long *reorder_time; + struct timer_list session_timer; + struct timer_list reorder_timer; + unsigned long last_rx; + u16 head_seq_num; + u16 stored_mpdu_num; + u16 ssn; + u16 buf_size; + u16 timeout; + u8 dialog_token; + bool first_time; /* is it 1-st time this buffer used? */ +}; + struct wil6210_stats { u64 tsf; u32 snr; @@ -225,6 +318,43 @@ struct wil6210_stats { u16 peer_tx_sector; }; +enum wil_sta_status { + wil_sta_unused = 0, + wil_sta_conn_pending = 1, + wil_sta_connected = 2, +}; + +#define WIL_STA_TID_NUM (16) + +struct wil_net_stats { + unsigned long rx_packets; + unsigned long tx_packets; + unsigned long rx_bytes; + unsigned long tx_bytes; + unsigned long tx_errors; + unsigned long rx_dropped; + u16 last_mcs_rx; +}; + +/** + * struct wil_sta_info - data for peer + * + * Peer identified by its CID (connection ID) + * NIC performs beam forming for each peer; + * if no beam forming done, frame exchange is not + * possible. + */ +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)]; + unsigned long tid_rx_stop_requested[BITS_TO_LONGS(WIL_STA_TID_NUM)]; +}; + struct wil6210_priv { struct pci_dev *pdev; int n_msi; @@ -232,7 +362,10 @@ 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 */ + int recovery_count; /* num of FW recovery attempts in a short time */ + unsigned long last_fw_recovery; /* jiffies of last fw recovery */ /* profile */ u32 monitor_flags; u32 secure_pcp; /* create secure PCP? */ @@ -252,7 +385,9 @@ 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; + struct timer_list scan_timer; /* detect scan timeout */ int pending_connect_cid; struct list_head pending_wmi_ev; /* @@ -266,7 +401,9 @@ struct wil6210_priv { /* DMA related */ struct vring vring_rx; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; - u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; + 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 */ struct cfg80211_scan_request *scan_request; @@ -328,11 +465,13 @@ 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); int wil_down(struct wil6210_priv *wil); void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); +int wil_find_cid(struct wil6210_priv *wil, const u8 *mac); void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); @@ -356,8 +495,11 @@ int wmi_echo(struct wil6210_priv *wil); int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring); int wmi_p2p_cfg(struct wil6210_priv *wil, int channel); +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); @@ -372,7 +514,7 @@ void wil_wdev_free(struct wil6210_priv *wil); int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan); int wmi_pcp_stop(struct wil6210_priv *wil); -void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); +void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid); int wil_rx_init(struct wil6210_priv *wil); void wil_rx_fini(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 063963ee422..6cc0e182cc7 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -192,7 +192,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) might_sleep(); if (!test_bit(wil_status_fwready, &wil->status)) { - wil_err(wil, "FW not ready\n"); + wil_err(wil, "WMI: cannot send command while FW not ready\n"); return -EAGAIN; } @@ -276,8 +276,8 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) wil->fw_version = le32_to_cpu(evt->sw_version); wil->n_mids = evt->numof_additional_mids; - wil_dbg_wmi(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version, - evt->mac, wil->n_mids); + wil_info(wil, "FW ver. %d; MAC %pM; %d MID's\n", wil->fw_version, + evt->mac, wil->n_mids); if (!is_valid_ether_addr(ndev->dev_addr)) { memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); @@ -290,7 +290,7 @@ static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, int len) { - wil_dbg_wmi(wil, "WMI: FW ready\n"); + wil_dbg_wmi(wil, "WMI: got FW ready event\n"); set_bit(wil_status_fwready, &wil->status); /* reuse wmi_ready for the firmware ready indication */ @@ -307,14 +307,14 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) u32 freq = ieee80211_channel_to_frequency(ch_no, IEEE80211_BAND_60GHZ); struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); - /* TODO convert LE to CPU */ - s32 signal = 0; /* TODO */ + s32 signal = data->info.sqi; __le16 fc = rx_mgmt_frame->frame_control; u32 d_len = le32_to_cpu(data->info.len); u16 d_status = le16_to_cpu(data->info.status); - wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d\n", - data->info.channel, data->info.mcs, data->info.snr); + wil_dbg_wmi(wil, "MGMT: channel %d MCS %d SNR %d SQI %d%%\n", + data->info.channel, data->info.mcs, data->info.snr, + data->info.sqi); wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len, le16_to_cpu(fc)); wil_dbg_wmi(wil, "qid %d mid %d cid %d\n", @@ -348,9 +348,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, { if (wil->scan_request) { struct wmi_scan_complete_event *data = d; - bool aborted = (data->status != 0); + bool aborted = (data->status != WMI_SCAN_SUCCESS); wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); + del_timer_sync(&wil->scan_timer); cfg80211_scan_done(wil->scan_request, aborted); wil->scan_request = NULL; } else { @@ -384,6 +385,11 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) evt->assoc_req_len, evt->assoc_resp_len); return; } + if (evt->cid >= WIL6210_MAX_CID) { + wil_err(wil, "Connect CID invalid : %d\n", evt->cid); + return; + } + ch = evt->channel + 1; wil_dbg_wmi(wil, "Connect %pM channel [%d] cid %d\n", evt->bssid, ch, evt->cid); @@ -439,7 +445,8 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) /* FIXME FW can transmit only ucast frames to peer */ /* FIXME real ring_id instead of hard coded 0 */ - memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + memcpy(wil->sta[evt->cid].addr, evt->bssid, ETH_ALEN); + wil->sta[evt->cid].status = wil_sta_conn_pending; wil->pending_connect_cid = evt->cid; queue_work(wil->wmi_wq_conn, &wil->connect_worker); @@ -456,7 +463,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) @@ -476,11 +485,11 @@ static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); wil_dbg_wmi(wil, "Link status, MCS %d TSF 0x%016llx\n" - "BF status 0x%08x SNR 0x%08x\n" + "BF status 0x%08x SNR 0x%08x SQI %d%%\n" "Tx Tpt %d goodput %d Rx goodput %d\n" "Sectors(rx:tx) my %d:%d peer %d:%d\n", wil->stats.bf_mcs, wil->stats.tsf, evt->status, - wil->stats.snr, le32_to_cpu(evt->tx_tpt), + wil->stats.snr, evt->sqi, le32_to_cpu(evt->tx_tpt), le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), wil->stats.my_rx_sector, wil->stats.my_tx_sector, wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); @@ -499,10 +508,16 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, int sz = eapol_len + ETH_HLEN; struct sk_buff *skb; struct ethhdr *eth; + int cid; + struct wil_net_stats *stats = NULL; wil_dbg_wmi(wil, "EAPOL len %d from %pM\n", eapol_len, evt->src_mac); + cid = wil_find_cid(wil, evt->src_mac); + if (cid >= 0) + stats = &wil->sta[cid].stats; + if (eapol_len > 196) { /* TODO: revisit size limit */ wil_err(wil, "EAPOL too large\n"); return; @@ -513,6 +528,7 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, wil_err(wil, "Failed to allocate skb\n"); return; } + eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); memcpy(eth->h_source, evt->src_mac, ETH_ALEN); @@ -521,9 +537,15 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, skb->protocol = eth_type_trans(skb, ndev); if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { ndev->stats.rx_packets++; - ndev->stats.rx_bytes += skb->len; + ndev->stats.rx_bytes += sz; + if (stats) { + stats->rx_packets++; + stats->rx_bytes += sz; + } } else { ndev->stats.rx_dropped++; + if (stats) + stats->rx_dropped++; } } @@ -531,9 +553,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", cid); - wil_dbg_wmi(wil, "Link UP for CID %d\n", evt->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); } @@ -541,10 +570,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); } @@ -552,10 +588,42 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d, int len) { struct wmi_vring_ba_status_event *evt = d; + struct wil_sta_info *sta; + uint i, cid; + + /* TODO: use Rx BA status, not Tx one */ wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n", - evt->ringid, evt->status ? "N/A" : "OK", evt->agg_wsize, - __le16_to_cpu(evt->ba_timeout)); + evt->ringid, + evt->status == WMI_BA_AGREED ? "OK" : "N/A", + evt->agg_wsize, __le16_to_cpu(evt->ba_timeout)); + + if (evt->ringid >= WIL6210_MAX_TX_RINGS) { + wil_err(wil, "invalid ring id %d\n", evt->ringid); + return; + } + + cid = wil->vring2cid_tid[evt->ringid][0]; + if (cid >= WIL6210_MAX_CID) { + wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid); + return; + } + + sta = &wil->sta[cid]; + if (sta->status == wil_sta_unused) { + wil_err(wil, "CID %d unused\n", cid); + return; + } + + wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr); + for (i = 0; i < WIL_STA_TID_NUM; i++) { + struct wil_tid_ampdu_rx *r = sta->tid_rx[i]; + sta->tid_rx[i] = NULL; + wil_tid_ampdu_rx_free(wil, r); + if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize) + sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil, + evt->agg_wsize, 0); + } } static const struct { @@ -591,21 +659,27 @@ void wmi_recv_cmd(struct wil6210_priv *wil) u8 *cmd; void __iomem *src; ulong flags; + unsigned n; if (!test_bit(wil_status_reset_done, &wil->status)) { wil_err(wil, "Reset not completed\n"); return; } - for (;;) { + for (n = 0;; n++) { u16 len; r->head = ioread32(wil->csr + HOST_MBOX + offsetof(struct wil6210_mbox_ctl, rx.head)); - if (r->tail == r->head) + if (r->tail == r->head) { + if (n == 0) + wil_dbg_wmi(wil, "No events?\n"); return; + } - /* read cmd from tail */ + wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n", + r->head, r->tail); + /* read cmd descriptor from tail */ wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), sizeof(struct wil6210_mbox_ring_desc)); if (d_tail.sync == 0) { @@ -613,13 +687,18 @@ void wmi_recv_cmd(struct wil6210_priv *wil) return; } + /* read cmd header from descriptor */ if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { wil_err(wil, "Mbox evt at 0x%08x?\n", le32_to_cpu(d_tail.addr)); return; } - len = le16_to_cpu(hdr.len); + wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", + le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), + hdr.flags); + + /* read cmd buffer from descriptor */ src = wmi_buffer(wil, d_tail.addr) + sizeof(struct wil6210_mbox_hdr); evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, @@ -635,9 +714,6 @@ void wmi_recv_cmd(struct wil6210_priv *wil) iowrite32(0, wil->csr + HOSTADDR(r->tail) + offsetof(struct wil6210_mbox_ring_desc, sync)); /* indicate */ - wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n", - le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), - hdr.flags); if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi; @@ -667,6 +743,8 @@ void wmi_recv_cmd(struct wil6210_priv *wil) wil_dbg_wmi(wil, "queue_work -> %d\n", q); } } + if (n > 1) + wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n); } int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, @@ -735,6 +813,7 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan) .network_type = wmi_nettype, .disable_sec_offload = 1, .channel = chan - 1, + .pcp_max_assoc_sta = WIL6210_MAX_CID, }; struct { struct wil6210_mbox_hdr_wmi wmi; @@ -893,6 +972,38 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) return rc; } +/** + * wmi_rxon - turn radio on/off + * @on: turn on if true, off otherwise + * + * Only switch radio. Channel should be set separately. + * No timeout for rxon - radio turned on forever unless some other call + * turns it off + */ +int wmi_rxon(struct wil6210_priv *wil, bool on) +{ + int rc; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_listen_started_event evt; + } __packed reply; + + wil_info(wil, "%s(%s)\n", __func__, on ? "on" : "off"); + + if (on) { + rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0, + WMI_LISTEN_STARTED_EVENTID, + &reply, sizeof(reply), 100); + if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS)) + rc = -EINVAL; + } else { + rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0, + WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20); + } + + return rc; +} + int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) { struct wireless_dev *wdev = wil->wdev; @@ -906,6 +1017,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) }, .mid = 0, /* TODO - what is it? */ .decap_trans_type = WMI_DECAP_TYPE_802_3, + .reorder_type = WMI_RX_SW_REORDER, }; struct { struct wil6210_mbox_hdr_wmi wmi; @@ -973,6 +1085,18 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r) return 0; } +int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason) +{ + struct wmi_disconnect_sta_cmd cmd = { + .disconnect_reason = cpu_to_le16(reason), + }; + memcpy(cmd.dst_mac, mac, ETH_ALEN); + + wil_dbg_wmi(wil, "%s(%pM, reason %d)\n", __func__, mac, reason); + + return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd)); +} + void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 50b8528394f..17334c85286 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -28,7 +28,7 @@ #define __WILOCITY_WMI_H__ /* General */ - +#define WILOCITY_MAX_ASSOC_STA (8) #define WMI_MAC_LEN (6) #define WMI_PROX_RANGE_NUM (3) @@ -219,15 +219,6 @@ struct wmi_disconnect_sta_cmd { __le16 disconnect_reason; } __packed; -/* - * WMI_RECONNECT_CMDID - */ -struct wmi_reconnect_cmd { - u8 channel; /* hint */ - u8 reserved; - u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ -} __packed; - /* * WMI_SET_PMK_CMDID @@ -296,11 +287,13 @@ enum wmi_scan_type { WMI_LONG_SCAN = 0, WMI_SHORT_SCAN = 1, WMI_PBC_SCAN = 2, + WMI_ACTIVE_SCAN = 3, + WMI_DIRECT_SCAN = 4, }; struct wmi_start_scan_cmd { - u8 reserved[8]; - + u8 direct_scan_mac_addr[6]; + u8 reserved[2]; __le32 home_dwell_time; /* Max duration in the home channel(ms) */ __le32 force_scan_interval; /* Time interval between scans (ms)*/ u8 scan_type; /* wmi_scan_type */ @@ -332,6 +325,7 @@ struct wmi_probed_ssid_cmd { u8 ssid[WMI_MAX_SSID_LEN]; } __packed; + /* * WMI_SET_APPIE_CMDID * Add Application specified IE to a management frame @@ -427,7 +421,7 @@ struct wmi_bcon_ctrl_cmd { __le16 frag_num; __le64 ss_mask; u8 network_type; - u8 reserved; + u8 pcp_max_assoc_sta; u8 disable_sec_offload; u8 disable_sec; } __packed; @@ -450,7 +444,7 @@ enum wmi_port_role { struct wmi_port_allocate_cmd { u8 mac[WMI_MAC_LEN]; u8 port_role; - u8 midid; + u8 mid; } __packed; /* @@ -467,6 +461,7 @@ struct wmi_delete_port_cmd { enum wmi_discovery_mode { WMI_DISCOVERY_MODE_NON_OFFLOAD = 0, WMI_DISCOVERY_MODE_OFFLOAD = 1, + WMI_DISCOVERY_MODE_PEER2PEER = 2, }; struct wmi_p2p_cfg_cmd { @@ -493,7 +488,8 @@ struct wmi_power_mgmt_cfg_cmd { */ struct wmi_pcp_start_cmd { __le16 bcon_interval; - u8 reserved0[10]; + u8 pcp_max_assoc_sta; + u8 reserved0[9]; u8 network_type; u8 channel; u8 disable_sec_offload; @@ -857,6 +853,7 @@ enum wmi_event_id { WMI_RF_MGMT_STATUS_EVENTID = 0x1853, WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, WMI_RX_MGMT_PACKET_EVENTID = 0x1840, + WMI_TX_MGMT_PACKET_EVENTID = 0x1841, /* Performance monitoring events */ WMI_DATA_PORT_OPEN_EVENTID = 0x1860, @@ -1040,16 +1037,23 @@ enum wmi_disconnect_reason { struct wmi_disconnect_event { __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ u8 bssid[WMI_MAC_LEN]; /* set if known */ - u8 disconnect_reason; /* see wmi_disconnect_reason_e */ - u8 assoc_resp_len; - u8 assoc_info[0]; + u8 disconnect_reason; /* see wmi_disconnect_reason */ + u8 assoc_resp_len; /* not in use */ + u8 assoc_info[0]; /* not in use */ } __packed; /* * WMI_SCAN_COMPLETE_EVENTID */ +enum scan_status { + WMI_SCAN_SUCCESS = 0, + WMI_SCAN_FAILED = 1, + WMI_SCAN_ABORTED = 2, + WMI_SCAN_REJECTED = 3, +}; + struct wmi_scan_complete_event { - __le32 status; + __le32 status; /* scan_status */ } __packed; /* @@ -1256,6 +1260,14 @@ struct wmi_rx_mgmt_info { u8 channel; /* From Radio MNGR */ } __packed; + +/* + * WMI_TX_MGMT_PACKET_EVENTID + */ +struct wmi_tx_mgmt_packet_event { + u8 payload[0]; +} __packed; + struct wmi_rx_mgmt_packet_event { struct wmi_rx_mgmt_info info; u8 payload[0]; diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index b827d51c30a..1fe41af81a5 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -28,8 +28,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with Atmel wireless lan drivers; if not, see + <http://www.gnu.org/licenses/>. For all queries about this code, please contact the current author, Simon Kelley <simon@thekelleys.org.uk> and not Atmel Corporation. @@ -39,7 +39,6 @@ ******************************************************************************/ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -68,7 +67,7 @@ #include <linux/moduleparam.h> #include <linux/firmware.h> #include <linux/jiffies.h> -#include <linux/ieee80211.h> +#include <net/cfg80211.h> #include "atmel.h" #define DRIVER_MAJOR 0 @@ -844,18 +843,18 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) if (priv->wep_is_on) frame_ctl |= IEEE80211_FCTL_PROTECTED; if (priv->operating_mode == IW_MODE_ADHOC) { - skb_copy_from_linear_data(skb, &header.addr1, 6); - memcpy(&header.addr2, dev->dev_addr, 6); - memcpy(&header.addr3, priv->BSSID, 6); + skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&header.addr3, priv->BSSID, ETH_ALEN); } else { frame_ctl |= IEEE80211_FCTL_TODS; - memcpy(&header.addr1, priv->CurrentBSSID, 6); - memcpy(&header.addr2, dev->dev_addr, 6); - skb_copy_from_linear_data(skb, &header.addr3, 6); + memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); } if (priv->use_wpa) - memcpy(&header.addr4, SNAP_RFC1024, 6); + memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN); header.frame_control = cpu_to_le16(frame_ctl); /* Copy the wireless header into the card */ @@ -929,11 +928,11 @@ static void fast_rx_path(struct atmel_private *priv, } } - memcpy(skbp, header->addr1, 6); /* destination address */ + memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(&skbp[6], header->addr3, 6); + memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); else - memcpy(&skbp[6], header->addr2, 6); /* source address */ + memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ skb->protocol = eth_type_trans(skb, priv->dev); skb->ip_summed = CHECKSUM_NONE; @@ -969,14 +968,14 @@ static void frag_rx_path(struct atmel_private *priv, u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, u8 frag_no, int more_frags) { - u8 mac4[6]; - u8 source[6]; + u8 mac4[ETH_ALEN]; + u8 source[ETH_ALEN]; struct sk_buff *skb; if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(source, header->addr3, 6); + memcpy(source, header->addr3, ETH_ALEN); else - memcpy(source, header->addr2, 6); + memcpy(source, header->addr2, ETH_ALEN); rx_packet_loc += 24; /* skip header */ @@ -984,9 +983,9 @@ static void frag_rx_path(struct atmel_private *priv, msdu_size -= 4; if (frag_no == 0) { /* first fragment */ - atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, 6); - msdu_size -= 6; - rx_packet_loc += 6; + atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); + msdu_size -= ETH_ALEN; + rx_packet_loc += ETH_ALEN; if (priv->do_rx_crc) crc = crc32_le(crc, mac4, 6); @@ -994,9 +993,9 @@ static void frag_rx_path(struct atmel_private *priv, priv->frag_seq = seq_no; priv->frag_no = 1; priv->frag_len = msdu_size; - memcpy(priv->frag_source, source, 6); - memcpy(&priv->rx_buf[6], source, 6); - memcpy(priv->rx_buf, header->addr1, 6); + memcpy(priv->frag_source, source, ETH_ALEN); + memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); + memcpy(priv->rx_buf, header->addr1, ETH_ALEN); atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); @@ -1006,13 +1005,13 @@ static void frag_rx_path(struct atmel_private *priv, atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); if ((crc ^ 0xffffffff) != netcrc) { priv->dev->stats.rx_crc_errors++; - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); } } } else if (priv->frag_no == frag_no && priv->frag_seq == seq_no && - memcmp(priv->frag_source, source, 6) == 0) { + memcmp(priv->frag_source, source, ETH_ALEN) == 0) { atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], rx_packet_loc, msdu_size); @@ -1024,7 +1023,7 @@ static void frag_rx_path(struct atmel_private *priv, atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); if ((crc ^ 0xffffffff) != netcrc) { priv->dev->stats.rx_crc_errors++; - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); more_frags = 1; /* don't send broken assembly */ } } @@ -1033,7 +1032,7 @@ static void frag_rx_path(struct atmel_private *priv, priv->frag_no++; if (!more_frags) { /* last one */ - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { priv->dev->stats.rx_dropped++; } else { @@ -1129,7 +1128,7 @@ static void rx_done_irq(struct atmel_private *priv) atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); /* we use the same buffer for frag reassembly and control packets */ - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); if (priv->do_rx_crc) { /* last 4 octets is crc */ @@ -1557,7 +1556,7 @@ struct net_device *init_atmel_card(unsigned short irq, unsigned long port, priv->last_qual = jiffies; priv->last_beacon_timestamp = 0; memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); - memset(priv->BSSID, 0, 6); + memset(priv->BSSID, 0, ETH_ALEN); priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ priv->station_was_associated = 0; @@ -1718,7 +1717,7 @@ static int atmel_get_wap(struct net_device *dev, char *extra) { struct atmel_private *priv = netdev_priv(dev); - memcpy(awrq->sa_data, priv->CurrentBSSID, 6); + memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); awrq->sa_family = ARPHRD_ETHER; return 0; @@ -2274,7 +2273,7 @@ static int atmel_set_freq(struct net_device *dev, /* Hack to fall through... */ fwrq->e = 0; - fwrq->m = ieee80211_freq_to_dsss_chan(f); + fwrq->m = ieee80211_frequency_to_channel(f); } /* Setting by channel number */ if ((fwrq->m > 1000) || (fwrq->e > 0)) @@ -2356,7 +2355,7 @@ static int atmel_get_scan(struct net_device *dev, for (i = 0; i < priv->BSS_list_entries; i++) { iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, 6); + memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_ADDR_LEN); @@ -2435,8 +2434,8 @@ static int atmel_get_range(struct net_device *dev, range->freq[k].i = i; /* List index */ /* Values in MHz -> * 10^5 * 10 */ - range->freq[k].m = (ieee80211_dsss_chan_to_freq(i) * - 100000); + range->freq[k].m = 100000 * + ieee80211_channel_to_frequency(i, IEEE80211_BAND_2GHZ); range->freq[k++].e = 1; } range->num_frequency = k; @@ -2760,7 +2759,7 @@ static void atmel_enter_state(struct atmel_private *priv, int new_state) static void atmel_scan(struct atmel_private *priv, int specific_ssid) { struct { - u8 BSSID[6]; + u8 BSSID[ETH_ALEN]; u8 SSID[MAX_SSID_LENGTH]; u8 scan_type; u8 channel; @@ -2771,7 +2770,7 @@ static void atmel_scan(struct atmel_private *priv, int specific_ssid) u8 SSID_size; } cmd; - memset(cmd.BSSID, 0xff, 6); + memset(cmd.BSSID, 0xff, ETH_ALEN); if (priv->fast_scan) { cmd.SSID_size = priv->SSID_size; @@ -2816,7 +2815,7 @@ static void join(struct atmel_private *priv, int type) cmd.SSID_size = priv->SSID_size; memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->CurrentBSSID, 6); + memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); cmd.channel = (priv->channel & 0x7f); cmd.BSS_type = type; cmd.timeout = cpu_to_le16(2000); @@ -2837,7 +2836,7 @@ static void start(struct atmel_private *priv, int type) cmd.SSID_size = priv->SSID_size; memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->BSSID, 6); + memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); cmd.BSS_type = type; cmd.channel = (priv->channel & 0x7f); @@ -2883,9 +2882,9 @@ static void send_authentication_request(struct atmel_private *priv, u16 system, header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); header.duration_id = cpu_to_le16(0x8000); header.seq_ctrl = 0; - memcpy(header.addr1, priv->CurrentBSSID, 6); - memcpy(header.addr2, priv->dev->dev_addr, 6); - memcpy(header.addr3, priv->CurrentBSSID, 6); + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) /* no WEP for authentication frames with TrSeqNo 1 */ @@ -2916,7 +2915,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) struct ass_req_format { __le16 capability; __le16 listen_interval; - u8 ap[6]; /* nothing after here directly accessible */ + u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ u8 ssid_el_id; u8 ssid_len; u8 ssid[MAX_SSID_LENGTH]; @@ -2930,9 +2929,9 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) header.duration_id = cpu_to_le16(0x8000); header.seq_ctrl = 0; - memcpy(header.addr1, priv->CurrentBSSID, 6); - memcpy(header.addr2, priv->dev->dev_addr, 6); - memcpy(header.addr3, priv->CurrentBSSID, 6); + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); if (priv->wep_is_on) @@ -2944,7 +2943,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) /* current AP address - only in reassoc frame */ if (is_reassoc) { - memcpy(body.ap, priv->CurrentBSSID, 6); + memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); ssid_el_p = &body.ssid_el_id; bodysize = 18 + priv->SSID_size; } else { @@ -3021,7 +3020,7 @@ static void store_bss_info(struct atmel_private *priv, int i, index; for (index = -1, i = 0; i < priv->BSS_list_entries; i++) - if (memcmp(bss, priv->BSSinfo[i].BSSID, 6) == 0) + if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) index = i; /* If we process a probe and an entry from this BSS exists @@ -3032,7 +3031,7 @@ static void store_bss_info(struct atmel_private *priv, if (priv->BSS_list_entries == MAX_BSS_ENTRIES) return; index = priv->BSS_list_entries++; - memcpy(priv->BSSinfo[index].BSSID, bss, 6); + memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); priv->BSSinfo[index].RSSI = rssi; } else { if (rssi > priv->BSSinfo[index].RSSI) @@ -3212,7 +3211,7 @@ static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) if (subtype == IEEE80211_STYPE_REASSOC_RESP && status != WLAN_STATUS_ASSOC_DENIED_RATES && status != WLAN_STATUS_CAPS_UNSUPPORTED && - priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { + priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); priv->ReAssociationRequestRetryCnt++; send_association_request(priv, 1); @@ -3235,7 +3234,7 @@ static void atmel_join_bss(struct atmel_private *priv, int bss_index) { struct bss_info *bss = &priv->BSSinfo[bss_index]; - memcpy(priv->CurrentBSSID, bss->BSSID, 6); + memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); /* The WPA stuff cares about the current AP address */ @@ -3767,7 +3766,7 @@ static int probe_atmel_card(struct net_device *dev) 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 }; printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); - memcpy(dev->dev_addr, default_mac, 6); + memcpy(dev->dev_addr, default_mac, ETH_ALEN); } } @@ -3819,7 +3818,7 @@ static void build_wpa_mib(struct atmel_private *priv) struct { /* NB this is matched to the hardware, don't change. */ u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; - u8 receiver_address[6]; + u8 receiver_address[ETH_ALEN]; u8 wep_is_on; u8 default_key; /* 0..3 */ u8 group_key; @@ -3837,7 +3836,7 @@ static void build_wpa_mib(struct atmel_private *priv) mib.wep_is_on = priv->wep_is_on; mib.exclude_unencrypted = priv->exclude_unencrypted; - memcpy(mib.receiver_address, priv->CurrentBSSID, 6); + memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); /* zero all the keys before adding in valid ones. */ memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); @@ -4278,8 +4277,7 @@ static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data) GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with AtmelMACFW; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with AtmelMACFW; if not, see <http://www.gnu.org/licenses/>. ****************************************************************************/ /* This firmware should work on the 76C502 RFMD, RFMD_D, and RFMD_E */ diff --git a/drivers/net/wireless/atmel.h b/drivers/net/wireless/atmel.h index b9b3e5b7654..96f7318cbb0 100644 --- a/drivers/net/wireless/atmel.h +++ b/drivers/net/wireless/atmel.h @@ -15,8 +15,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with Atmel wireless lan drivers; if not, see + <http://www.gnu.org/licenses/>. ******************************************************************************/ diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c index 52257221921..4cfb4d99ced 100644 --- a/drivers/net/wireless/atmel_cs.c +++ b/drivers/net/wireless/atmel_cs.c @@ -24,15 +24,14 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with Atmel wireless lan drivers; if not, see + <http://www.gnu.org/licenses/>. ******************************************************************************/ #ifdef __IN_PCMCIA_PACKAGE__ #include <pcmcia/k_compat.h> #endif -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/ptrace.h> diff --git a/drivers/net/wireless/atmel_pci.c b/drivers/net/wireless/atmel_pci.c index c1b159ebcff..5cd97e3cbee 100644 --- a/drivers/net/wireless/atmel_pci.c +++ b/drivers/net/wireless/atmel_pci.c @@ -15,14 +15,13 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with Atmel wireless lan drivers; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + along with Atmel wireless lan drivers; if not, see + <http://www.gnu.org/licenses/>. ******************************************************************************/ #include <linux/pci.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/netdevice.h> #include "atmel.h" diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 51ff0b198d0..40fd9b7b142 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -1,7 +1,8 @@ config B43 tristate "Broadcom 43xx wireless support (mac80211 stack)" - depends on SSB_POSSIBLE && MAC80211 && HAS_DMA - select SSB + depends on (BCMA_POSSIBLE || SSB_POSSIBLE) && MAC80211 && HAS_DMA + select BCMA if B43_BCMA + select SSB if B43_SSB select FW_LOADER ---help--- b43 is a driver for the Broadcom 43xx series wireless devices. @@ -27,14 +28,33 @@ config B43 If unsure, say M. config B43_BCMA - bool "Support for BCMA bus" - depends on B43 && (BCMA = y || BCMA = B43) - default y + bool config B43_SSB bool - depends on B43 && (SSB = y || SSB = B43) - default y + +choice + prompt "Supported bus types" + depends on B43 + default B43_BUSES_BCMA_AND_SSB + +config B43_BUSES_BCMA_AND_SSB + bool "BCMA and SSB" + depends on BCMA_POSSIBLE && SSB_POSSIBLE + select B43_BCMA + select B43_SSB + +config B43_BUSES_BCMA + bool "BCMA only" + depends on BCMA_POSSIBLE + select B43_BCMA + +config B43_BUSES_SSB + bool "SSB only" + depends on SSB_POSSIBLE + select B43_SSB + +endchoice # Auto-select SSB PCI-HOST support, if possible config B43_PCI_AUTOSELECT @@ -53,7 +73,7 @@ config B43_PCICORE_AUTOSELECT config B43_PCMCIA bool "Broadcom 43xx PCMCIA device support" - depends on B43 && SSB_PCMCIAHOST_POSSIBLE + depends on B43 && B43_SSB && SSB_PCMCIAHOST_POSSIBLE select SSB_PCMCIAHOST ---help--- Broadcom 43xx PCMCIA device support. @@ -73,7 +93,7 @@ config B43_PCMCIA config B43_SDIO bool "Broadcom 43xx SDIO device support" - depends on B43 && SSB_SDIOHOST_POSSIBLE + depends on B43 && B43_SSB && SSB_SDIOHOST_POSSIBLE select SSB_SDIOHOST ---help--- Broadcom 43xx device support for Soft-MAC SDIO devices. @@ -92,13 +112,13 @@ config B43_SDIO # if we can do DMA. config B43_BCMA_PIO bool - depends on B43_BCMA + depends on B43 && B43_BCMA select BCMA_BLOCKIO default y config B43_PIO bool - depends on B43 + depends on B43 && B43_SSB select SSB_BLOCKIO default y @@ -116,7 +136,7 @@ config B43_PHY_N config B43_PHY_LP bool "Support for low-power (LP-PHY) devices" - depends on B43 + depends on B43 && B43_SSB default y ---help--- Support for the LP-PHY. diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 7f3d461f7e8..4113b693476 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -731,8 +731,6 @@ enum b43_firmware_file_type { struct b43_request_fw_context { /* The device we are requesting the fw for. */ struct b43_wldev *dev; - /* a completion event structure needed if this call is asynchronous */ - struct completion fw_load_complete; /* a pointer to the firmware object */ const struct firmware *blob; /* The type of firmware to request. */ @@ -809,6 +807,8 @@ enum { struct b43_wldev { struct b43_bus_dev *dev; struct b43_wl *wl; + /* a completion event structure needed if this call is asynchronous */ + struct completion fw_load_complete; /* The device initialization status. * Use b43_status() to query. */ @@ -915,10 +915,6 @@ struct b43_wl { char rng_name[30 + 1]; #endif /* CONFIG_B43_HWRNG */ - /* List of all wireless devices on this chip */ - struct list_head devlist; - u8 nr_devs; - bool radiotap_enabled; bool radio_enabled; diff --git a/drivers/net/wireless/b43/bus.h b/drivers/net/wireless/b43/bus.h index 184c9565927..f3205c6988b 100644 --- a/drivers/net/wireless/b43/bus.h +++ b/drivers/net/wireless/b43/bus.h @@ -5,7 +5,9 @@ enum b43_bus_type { #ifdef CONFIG_B43_BCMA B43_BUS_BCMA, #endif +#ifdef CONFIG_B43_SSB B43_BUS_SSB, +#endif }; struct b43_bus_dev { @@ -52,13 +54,21 @@ struct b43_bus_dev { static inline bool b43_bus_host_is_pcmcia(struct b43_bus_dev *dev) { +#ifdef CONFIG_B43_SSB return (dev->bus_type == B43_BUS_SSB && dev->sdev->bus->bustype == SSB_BUSTYPE_PCMCIA); +#else + return false; +#endif } static inline bool b43_bus_host_is_sdio(struct b43_bus_dev *dev) { +#ifdef CONFIG_B43_SSB return (dev->bus_type == B43_BUS_SSB && dev->sdev->bus->bustype == SSB_BUSTYPE_SDIO); +#else + return false; +#endif } struct b43_bus_dev *b43_bus_dev_bcma_init(struct bcma_device *core); diff --git a/drivers/net/wireless/b43/debugfs.h b/drivers/net/wireless/b43/debugfs.h index 822aad8842f..50517b801cb 100644 --- a/drivers/net/wireless/b43/debugfs.h +++ b/drivers/net/wireless/b43/debugfs.h @@ -86,7 +86,7 @@ void b43_debugfs_log_txstat(struct b43_wldev *dev, static inline bool b43_debug(struct b43_wldev *dev, enum b43_dyndbg feature) { - return 0; + return false; } static inline void b43_debugfs_init(void) diff --git a/drivers/net/wireless/b43/dma.c b/drivers/net/wireless/b43/dma.c index c51d2dc489e..1d7982afc0a 100644 --- a/drivers/net/wireless/b43/dma.c +++ b/drivers/net/wireless/b43/dma.c @@ -1065,12 +1065,9 @@ static int b43_dma_set_mask(struct b43_wldev *dev, u64 mask) /* Try to set the DMA mask. If it fails, try falling back to a * lower mask, as we can always also support a lower one. */ while (1) { - err = dma_set_mask(dev->dev->dma_dev, mask); - if (!err) { - err = dma_set_coherent_mask(dev->dev->dma_dev, mask); - if (!err) - break; - } + err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); + if (!err) + break; if (mask == DMA_BIT_MASK(64)) { mask = DMA_BIT_MASK(32); fallback = true; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ccd24f0acb8..0d6a0bb1f87 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -182,7 +182,7 @@ static struct ieee80211_rate __b43_ratetable[] = { #define b43_g_ratetable (__b43_ratetable + 0) #define b43_g_ratetable_size 12 -#define CHAN4G(_channel, _freq, _flags) { \ +#define CHAN2G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ @@ -191,23 +191,31 @@ static struct ieee80211_rate __b43_ratetable[] = { .max_power = 30, \ } static struct ieee80211_channel b43_2ghz_chantable[] = { - CHAN4G(1, 2412, 0), - CHAN4G(2, 2417, 0), - CHAN4G(3, 2422, 0), - CHAN4G(4, 2427, 0), - CHAN4G(5, 2432, 0), - CHAN4G(6, 2437, 0), - CHAN4G(7, 2442, 0), - CHAN4G(8, 2447, 0), - CHAN4G(9, 2452, 0), - CHAN4G(10, 2457, 0), - CHAN4G(11, 2462, 0), - CHAN4G(12, 2467, 0), - CHAN4G(13, 2472, 0), - CHAN4G(14, 2484, 0), + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), }; -#undef CHAN4G +#undef CHAN2G +#define CHAN4G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 4000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} #define CHAN5G(_channel, _flags) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ @@ -217,6 +225,18 @@ static struct ieee80211_channel b43_2ghz_chantable[] = { .max_power = 30, \ } static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { + CHAN4G(184, 0), CHAN4G(186, 0), + CHAN4G(188, 0), CHAN4G(190, 0), + CHAN4G(192, 0), CHAN4G(194, 0), + CHAN4G(196, 0), CHAN4G(198, 0), + CHAN4G(200, 0), CHAN4G(202, 0), + CHAN4G(204, 0), CHAN4G(206, 0), + CHAN4G(208, 0), CHAN4G(210, 0), + CHAN4G(212, 0), CHAN4G(214, 0), + CHAN4G(216, 0), CHAN4G(218, 0), + CHAN4G(220, 0), CHAN4G(222, 0), + CHAN4G(224, 0), CHAN4G(226, 0), + CHAN4G(228, 0), CHAN5G(32, 0), CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), @@ -260,18 +280,7 @@ static struct ieee80211_channel b43_5ghz_nphy_chantable[] = { CHAN5G(170, 0), CHAN5G(172, 0), CHAN5G(174, 0), CHAN5G(176, 0), CHAN5G(178, 0), CHAN5G(180, 0), - CHAN5G(182, 0), CHAN5G(184, 0), - CHAN5G(186, 0), CHAN5G(188, 0), - CHAN5G(190, 0), CHAN5G(192, 0), - CHAN5G(194, 0), CHAN5G(196, 0), - CHAN5G(198, 0), CHAN5G(200, 0), - CHAN5G(202, 0), CHAN5G(204, 0), - CHAN5G(206, 0), CHAN5G(208, 0), - CHAN5G(210, 0), CHAN5G(212, 0), - CHAN5G(214, 0), CHAN5G(216, 0), - CHAN5G(218, 0), CHAN5G(220, 0), - CHAN5G(222, 0), CHAN5G(224, 0), - CHAN5G(226, 0), CHAN5G(228, 0), + CHAN5G(182, 0), }; static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { @@ -295,6 +304,7 @@ static struct ieee80211_channel b43_5ghz_aphy_chantable[] = { CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; +#undef CHAN4G #undef CHAN5G static struct ieee80211_supported_band b43_band_5GHz_nphy = { @@ -1175,18 +1185,7 @@ static void b43_bcma_phy_reset(struct b43_wldev *dev) bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); udelay(2); - /* Take PHY out of reset */ - flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - flags &= ~B43_BCMA_IOCTL_PHY_RESET; - flags |= BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); - udelay(1); - - /* Do not force clock anymore */ - flags = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); - flags &= ~BCMA_IOCTL_FGC; - bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, flags); - udelay(1); + b43_phy_take_out_of_reset(dev); } static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) @@ -1195,18 +1194,22 @@ static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) B43_BCMA_CLKCTLST_PHY_PLL_REQ; u32 status = B43_BCMA_CLKCTLST_80211_PLL_ST | B43_BCMA_CLKCTLST_PHY_PLL_ST; + u32 flags; + + flags = B43_BCMA_IOCTL_PHY_CLKEN; + if (gmode) + flags |= B43_BCMA_IOCTL_GMODE; + b43_device_enable(dev, flags); - b43_device_enable(dev, B43_BCMA_IOCTL_PHY_CLKEN); bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST); b43_bcma_phy_reset(dev); bcma_core_pll_ctl(dev->dev->bdev, req, status, true); } #endif +#ifdef CONFIG_B43_SSB static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) { - struct ssb_device *sdev = dev->dev->sdev; - u32 tmslow; u32 flags = 0; if (gmode) @@ -1218,18 +1221,9 @@ static void b43_ssb_wireless_core_reset(struct b43_wldev *dev, bool gmode) b43_device_enable(dev, flags); msleep(2); /* Wait for the PLL to turn on. */ - /* Now take the PHY out of Reset again */ - tmslow = ssb_read32(sdev, SSB_TMSLOW); - tmslow |= SSB_TMSLOW_FGC; - tmslow &= ~B43_TMSLOW_PHYRESET; - ssb_write32(sdev, SSB_TMSLOW, tmslow); - ssb_read32(sdev, SSB_TMSLOW); /* flush */ - msleep(1); - tmslow &= ~SSB_TMSLOW_FGC; - ssb_write32(sdev, SSB_TMSLOW, tmslow); - ssb_read32(sdev, SSB_TMSLOW); /* flush */ - msleep(1); + b43_phy_take_out_of_reset(dev); } +#endif void b43_wireless_core_reset(struct b43_wldev *dev, bool gmode) { @@ -1549,7 +1543,7 @@ static void b43_write_beacon_template(struct b43_wldev *dev, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); - len = min((size_t) dev->wl->current_beacon->len, + len = min_t(size_t, dev->wl->current_beacon->len, 0x200 - sizeof(struct b43_plcp_hdr6)); rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; @@ -2070,6 +2064,7 @@ void b43_do_release_fw(struct b43_firmware_file *fw) static void b43_release_firmware(struct b43_wldev *dev) { + complete(&dev->fw_load_complete); b43_do_release_fw(&dev->fw.ucode); b43_do_release_fw(&dev->fw.pcm); b43_do_release_fw(&dev->fw.initvals); @@ -2095,7 +2090,7 @@ static void b43_fw_cb(const struct firmware *firmware, void *context) struct b43_request_fw_context *ctx = context; ctx->blob = firmware; - complete(&ctx->fw_load_complete); + complete(&ctx->dev->fw_load_complete); } int b43_do_request_fw(struct b43_request_fw_context *ctx, @@ -2142,7 +2137,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, } if (async) { /* do this part asynchronously */ - init_completion(&ctx->fw_load_complete); + init_completion(&ctx->dev->fw_load_complete); err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, ctx->dev->dev->dev, GFP_KERNEL, ctx, b43_fw_cb); @@ -2150,12 +2145,11 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx, pr_err("Unable to load firmware\n"); return err; } - /* stall here until fw ready */ - wait_for_completion(&ctx->fw_load_complete); + wait_for_completion(&ctx->dev->fw_load_complete); if (ctx->blob) goto fw_ready; /* On some ARM systems, the async request will fail, but the next sync - * request works. For this reason, we dall through here + * request works. For this reason, we fall through here */ } err = request_firmware(&ctx->blob, ctx->fwname, @@ -2424,6 +2418,7 @@ error: static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl); static void b43_one_core_detach(struct b43_bus_dev *dev); +static int b43_rng_init(struct b43_wl *wl); static void b43_request_firmware(struct work_struct *work) { @@ -2475,6 +2470,10 @@ start_ieee80211: goto err_one_core_detach; wl->hw_registred = true; b43_leds_register(wl->current_dev); + + /* Register HW RNG driver */ + b43_rng_init(wl); + goto out; err_one_core_detach: @@ -2699,32 +2698,37 @@ static int b43_upload_initvals(struct b43_wldev *dev) struct b43_firmware *fw = &dev->fw; const struct b43_iv *ivals; size_t count; - int err; hdr = (const struct b43_fw_header *)(fw->initvals.data->data); ivals = (const struct b43_iv *)(fw->initvals.data->data + hdr_len); count = be32_to_cpu(hdr->size); - err = b43_write_initvals(dev, ivals, count, + return b43_write_initvals(dev, ivals, count, fw->initvals.data->size - hdr_len); - if (err) - goto out; - if (fw->initvals_band.data) { - hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); - ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); - count = be32_to_cpu(hdr->size); - err = b43_write_initvals(dev, ivals, count, - fw->initvals_band.data->size - hdr_len); - if (err) - goto out; - } -out: +} - return err; +static int b43_upload_initvals_band(struct b43_wldev *dev) +{ + const size_t hdr_len = sizeof(struct b43_fw_header); + const struct b43_fw_header *hdr; + struct b43_firmware *fw = &dev->fw; + const struct b43_iv *ivals; + size_t count; + + if (!fw->initvals_band.data) + return 0; + + hdr = (const struct b43_fw_header *)(fw->initvals_band.data->data); + ivals = (const struct b43_iv *)(fw->initvals_band.data->data + hdr_len); + count = be32_to_cpu(hdr->size); + return b43_write_initvals(dev, ivals, count, + fw->initvals_band.data->size - hdr_len); } /* Initialize the GPIOs * http://bcm-specs.sipsolutions.net/GPIO */ + +#ifdef CONFIG_B43_SSB static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) { struct ssb_bus *bus = dev->dev->sdev->bus; @@ -2735,10 +2739,13 @@ static struct ssb_device *b43_ssb_gpio_dev(struct b43_wldev *dev) return bus->chipco.dev; #endif } +#endif static int b43_gpio_init(struct b43_wldev *dev) { +#ifdef CONFIG_B43_SSB struct ssb_device *gpiodev; +#endif u32 mask, set; b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_GPOUTSMSK, 0); @@ -2797,7 +2804,9 @@ static int b43_gpio_init(struct b43_wldev *dev) /* Turn off all GPIO stuff. Call this on module unload, for example. */ static void b43_gpio_cleanup(struct b43_wldev *dev) { +#ifdef CONFIG_B43_SSB struct ssb_device *gpiodev; +#endif switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA @@ -3081,6 +3090,10 @@ static int b43_chip_init(struct b43_wldev *dev) if (err) goto err_gpio_clean; + err = b43_upload_initvals_band(dev); + if (err) + goto err_gpio_clean; + /* Turn the Analog on and initialize the PHY. */ phy->ops->switch_analog(dev, 1); err = b43_phy_init(dev); @@ -3680,37 +3693,6 @@ static void b43_op_set_tsf(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } -static void b43_put_phy_into_reset(struct b43_wldev *dev) -{ - u32 tmp; - - switch (dev->dev->bus_type) { -#ifdef CONFIG_B43_BCMA - case B43_BUS_BCMA: - b43err(dev->wl, - "Putting PHY into reset not supported on BCMA\n"); - break; -#endif -#ifdef CONFIG_B43_SSB - case B43_BUS_SSB: - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~B43_TMSLOW_GMODE; - tmp |= B43_TMSLOW_PHYRESET; - tmp |= SSB_TMSLOW_FGC; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - msleep(1); - - tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); - tmp &= ~SSB_TMSLOW_FGC; - tmp |= B43_TMSLOW_PHYRESET; - ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); - msleep(1); - - break; -#endif - } -} - static const char *band_to_string(enum ieee80211_band band) { switch (band) { @@ -3726,94 +3708,75 @@ static const char *band_to_string(enum ieee80211_band band) } /* Expects wl->mutex locked */ -static int b43_switch_band(struct b43_wl *wl, struct ieee80211_channel *chan) +static int b43_switch_band(struct b43_wldev *dev, + struct ieee80211_channel *chan) { - struct b43_wldev *up_dev = NULL; - struct b43_wldev *down_dev; - struct b43_wldev *d; - int err; - bool uninitialized_var(gmode); - int prev_status; + struct b43_phy *phy = &dev->phy; + bool gmode; + u32 tmp; - /* Find a device and PHY which supports the band. */ - list_for_each_entry(d, &wl->devlist, list) { - switch (chan->band) { - case IEEE80211_BAND_5GHZ: - if (d->phy.supports_5ghz) { - up_dev = d; - gmode = false; - } - break; - case IEEE80211_BAND_2GHZ: - if (d->phy.supports_2ghz) { - up_dev = d; - gmode = true; - } - break; - default: - B43_WARN_ON(1); - return -EINVAL; - } - if (up_dev) - break; + switch (chan->band) { + case IEEE80211_BAND_5GHZ: + gmode = false; + break; + case IEEE80211_BAND_2GHZ: + gmode = true; + break; + default: + B43_WARN_ON(1); + return -EINVAL; } - if (!up_dev) { - b43err(wl, "Could not find a device for %s-GHz band operation\n", + + if (!((gmode && phy->supports_2ghz) || + (!gmode && phy->supports_5ghz))) { + b43err(dev->wl, "This device doesn't support %s-GHz band\n", band_to_string(chan->band)); return -ENODEV; } - if ((up_dev == wl->current_dev) && - (!!wl->current_dev->phy.gmode == !!gmode)) { + + if (!!phy->gmode == !!gmode) { /* This device is already running. */ return 0; } - b43dbg(wl, "Switching to %s-GHz band\n", + + b43dbg(dev->wl, "Switching to %s GHz band\n", band_to_string(chan->band)); - down_dev = wl->current_dev; - prev_status = b43_status(down_dev); - /* Shutdown the currently running core. */ - if (prev_status >= B43_STAT_STARTED) - down_dev = b43_wireless_core_stop(down_dev); - if (prev_status >= B43_STAT_INITIALIZED) - b43_wireless_core_exit(down_dev); + /* Some new devices don't need disabling radio for band switching */ + if (!(phy->type == B43_PHYTYPE_N && phy->rev >= 3)) + b43_software_rfkill(dev, true); - if (down_dev != up_dev) { - /* We switch to a different core, so we put PHY into - * RESET on the old core. */ - b43_put_phy_into_reset(down_dev); + phy->gmode = gmode; + b43_phy_put_into_reset(dev); + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + if (gmode) + tmp |= B43_BCMA_IOCTL_GMODE; + else + tmp &= ~B43_BCMA_IOCTL_GMODE; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + if (gmode) + tmp |= B43_TMSLOW_GMODE; + else + tmp &= ~B43_TMSLOW_GMODE; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + break; +#endif } + b43_phy_take_out_of_reset(dev); - /* Now start the new core. */ - up_dev->phy.gmode = gmode; - if (prev_status >= B43_STAT_INITIALIZED) { - err = b43_wireless_core_init(up_dev); - if (err) { - b43err(wl, "Fatal: Could not initialize device for " - "selected %s-GHz band\n", - band_to_string(chan->band)); - goto init_failure; - } - } - if (prev_status >= B43_STAT_STARTED) { - err = b43_wireless_core_start(up_dev); - if (err) { - b43err(wl, "Fatal: Could not start device for " - "selected %s-GHz band\n", - band_to_string(chan->band)); - b43_wireless_core_exit(up_dev); - goto init_failure; - } - } - B43_WARN_ON(b43_status(up_dev) != prev_status); + b43_upload_initvals_band(dev); - wl->current_dev = up_dev; + b43_phy_init(dev); return 0; -init_failure: - /* Whoops, failed to init the new core. No core is operating now. */ - wl->current_dev = NULL; - return err; } /* Write the short and long frame retry limit values. */ @@ -3846,8 +3809,10 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) dev = wl->current_dev; + b43_mac_suspend(dev); + /* Switch the band (if necessary). This might change the active core. */ - err = b43_switch_band(wl, conf->chandef.chan); + err = b43_switch_band(dev, conf->chandef.chan); if (err) goto out_unlock_mutex; @@ -3866,8 +3831,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed) else phy->is_40mhz = false; - b43_mac_suspend(dev); - if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) b43_set_retry_limits(dev, conf->short_frame_max_tx_count, conf->long_frame_max_tx_count); @@ -4577,8 +4540,12 @@ static void b43_imcfglo_timeouts_workaround(struct b43_wldev *dev) struct ssb_bus *bus; u32 tmp; +#ifdef CONFIG_B43_SSB if (dev->dev->bus_type != B43_BUS_SSB) return; +#else + return; +#endif bus = dev->dev->sdev->bus; @@ -4636,9 +4603,6 @@ static void b43_wireless_core_exit(struct b43_wldev *dev) if (!dev || b43_status(dev) != B43_STAT_INITIALIZED) return; - /* Unregister HW RNG driver */ - b43_rng_exit(dev->wl); - b43_set_status(dev, B43_STAT_UNINIT); /* Stop the microcode PSM. */ @@ -4736,7 +4700,7 @@ static int b43_wireless_core_init(struct b43_wldev *dev) } if (sprom->boardflags_lo & B43_BFL_XTAL_NOSLOW) hf |= B43_HF_DSCRQ; /* Disable slowclock requests from ucode. */ -#ifdef CONFIG_SSB_DRIVER_PCICORE +#if defined(CONFIG_B43_SSB) && defined(CONFIG_SSB_DRIVER_PCICORE) if (dev->dev->bus_type == B43_BUS_SSB && dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI && dev->dev->sdev->bus->pcicore.dev->id.revision <= 10) @@ -4795,9 +4759,6 @@ static int b43_wireless_core_init(struct b43_wldev *dev) b43_set_status(dev, B43_STAT_INITIALIZED); - /* Register HW RNG driver */ - b43_rng_init(dev->wl); - out: return err; @@ -5130,10 +5091,82 @@ static void b43_wireless_core_detach(struct b43_wldev *dev) b43_phy_free(dev); } +static void b43_supported_bands(struct b43_wldev *dev, bool *have_2ghz_phy, + bool *have_5ghz_phy) +{ + u16 dev_id = 0; + +#ifdef CONFIG_B43_BCMA + if (dev->dev->bus_type == B43_BUS_BCMA && + dev->dev->bdev->bus->hosttype == BCMA_HOSTTYPE_PCI) + dev_id = dev->dev->bdev->bus->host_pci->device; +#endif +#ifdef CONFIG_B43_SSB + if (dev->dev->bus_type == B43_BUS_SSB && + dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) + dev_id = dev->dev->sdev->bus->host_pci->device; +#endif + /* Override with SPROM value if available */ + if (dev->dev->bus_sprom->dev_id) + dev_id = dev->dev->bus_sprom->dev_id; + + /* Note: below IDs can be "virtual" (not maching e.g. real PCI ID) */ + switch (dev_id) { + case 0x4324: /* BCM4306 */ + case 0x4312: /* BCM4311 */ + case 0x4319: /* BCM4318 */ + case 0x4328: /* BCM4321 */ + case 0x432b: /* BCM4322 */ + case 0x4350: /* BCM43222 */ + case 0x4353: /* BCM43224 */ + case 0x0576: /* BCM43224 */ + case 0x435f: /* BCM6362 */ + case 0x4331: /* BCM4331 */ + case 0x4359: /* BCM43228 */ + case 0x43a0: /* BCM4360 */ + case 0x43b1: /* BCM4352 */ + /* Dual band devices */ + *have_2ghz_phy = true; + *have_5ghz_phy = true; + return; + case 0x4321: /* BCM4306 */ + case 0x4313: /* BCM4311 */ + case 0x431a: /* BCM4318 */ + case 0x432a: /* BCM4321 */ + case 0x432d: /* BCM4322 */ + case 0x4352: /* BCM43222 */ + case 0x4333: /* BCM4331 */ + case 0x43a2: /* BCM4360 */ + case 0x43b3: /* BCM4352 */ + /* 5 GHz only devices */ + *have_2ghz_phy = false; + *have_5ghz_phy = true; + return; + } + + /* As a fallback, try to guess using PHY type */ + switch (dev->phy.type) { + case B43_PHYTYPE_A: + *have_2ghz_phy = false; + *have_5ghz_phy = true; + return; + case B43_PHYTYPE_G: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + case B43_PHYTYPE_LCN: + *have_2ghz_phy = true; + *have_5ghz_phy = false; + return; + } + + B43_WARN_ON(1); +} + static int b43_wireless_core_attach(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; - struct pci_dev *pdev = NULL; + struct b43_phy *phy = &dev->phy; int err; u32 tmp; bool have_2ghz_phy = false, have_5ghz_phy = false; @@ -5145,19 +5178,15 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) * that in core_init(), too. */ -#ifdef CONFIG_B43_SSB - if (dev->dev->bus_type == B43_BUS_SSB && - dev->dev->sdev->bus->bustype == SSB_BUSTYPE_PCI) - pdev = dev->dev->sdev->bus->host_pci; -#endif - err = b43_bus_powerup(dev, 0); if (err) { b43err(wl, "Bus powerup failed\n"); goto out; } - /* Get the PHY type. */ + phy->do_full_init = true; + + /* Try to guess supported bands for the first init needs */ switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA case B43_BUS_BCMA: @@ -5179,51 +5208,32 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) } dev->phy.gmode = have_2ghz_phy; - dev->phy.radio_on = true; b43_wireless_core_reset(dev, dev->phy.gmode); + /* Get the PHY type. */ err = b43_phy_versioning(dev); if (err) goto err_powerdown; - /* Check if this device supports multiband. */ - if (!pdev || - (pdev->device != 0x4312 && - pdev->device != 0x4319 && pdev->device != 0x4324)) { - /* No multiband support. */ - have_2ghz_phy = false; + + /* Get real info about supported bands */ + b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy); + + /* We don't support 5 GHz on some PHYs yet */ + switch (dev->phy.type) { + case B43_PHYTYPE_A: + case B43_PHYTYPE_G: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + b43warn(wl, "5 GHz band is unsupported on this PHY\n"); have_5ghz_phy = false; - switch (dev->phy.type) { - case B43_PHYTYPE_A: - have_5ghz_phy = true; - break; - case B43_PHYTYPE_LP: //FIXME not always! -#if 0 //FIXME enabling 5GHz causes a NULL pointer dereference - have_5ghz_phy = 1; -#endif - case B43_PHYTYPE_G: - case B43_PHYTYPE_N: - case B43_PHYTYPE_HT: - case B43_PHYTYPE_LCN: - have_2ghz_phy = true; - break; - default: - B43_WARN_ON(1); - } } - if (dev->phy.type == B43_PHYTYPE_A) { - /* FIXME */ - b43err(wl, "IEEE 802.11a devices are unsupported\n"); + + if (!have_2ghz_phy && !have_5ghz_phy) { + b43err(wl, "b43 can't support any band on this device\n"); err = -EOPNOTSUPP; goto err_powerdown; } - if (1 /* disable A-PHY */) { - /* FIXME: For now we disable the A-PHY on multi-PHY devices. */ - if (dev->phy.type != B43_PHYTYPE_N && - dev->phy.type != B43_PHYTYPE_LP) { - have_2ghz_phy = true; - have_5ghz_phy = false; - } - } err = b43_phy_allocate(dev); if (err) @@ -5271,7 +5281,6 @@ static void b43_one_core_detach(struct b43_bus_dev *dev) b43_debugfs_remove_device(wldev); b43_wireless_core_detach(wldev); list_del(&wldev->list); - wl->nr_devs--; b43_bus_set_wldev(dev, NULL); kfree(wldev); } @@ -5296,8 +5305,6 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) if (err) goto err_kfree_wldev; - list_add(&wldev->list, &wl->devlist); - wl->nr_devs++; b43_bus_set_wldev(dev, wldev); b43_debugfs_add_device(wldev); @@ -5315,6 +5322,7 @@ static int b43_one_core_attach(struct b43_bus_dev *dev, struct b43_wl *wl) (pdev->subsystem_vendor == PCI_VENDOR_ID_##_subvendor) && \ (pdev->subsystem_device == _subdevice) ) +#ifdef CONFIG_B43_SSB static void b43_sprom_fixup(struct ssb_bus *bus) { struct pci_dev *pdev; @@ -5346,6 +5354,7 @@ static void b43_wireless_exit(struct b43_bus_dev *dev, struct b43_wl *wl) ssb_set_devtypedata(dev->sdev, NULL); ieee80211_free_hw(hw); } +#endif static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) { @@ -5387,7 +5396,6 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) wl->hw = hw; mutex_init(&wl->mutex); spin_lock_init(&wl->hardirq_lock); - INIT_LIST_HEAD(&wl->devlist); INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work); INIT_WORK(&wl->tx_work, b43_tx_work); @@ -5464,6 +5472,9 @@ static void b43_bcma_remove(struct bcma_device *core) b43_one_core_detach(wldev->dev); + /* Unregister HW RNG driver */ + b43_rng_exit(wl); + b43_leds_unregister(wl); ieee80211_free_hw(wl->hw); @@ -5484,39 +5495,42 @@ int b43_ssb_probe(struct ssb_device *sdev, const struct ssb_device_id *id) struct b43_bus_dev *dev; struct b43_wl *wl; int err; - int first = 0; dev = b43_bus_dev_ssb_init(sdev); if (!dev) return -ENOMEM; wl = ssb_get_devtypedata(sdev); - if (!wl) { - /* Probing the first core. Must setup common struct b43_wl */ - first = 1; - b43_sprom_fixup(sdev->bus); - wl = b43_wireless_init(dev); - if (IS_ERR(wl)) { - err = PTR_ERR(wl); - goto out; - } - ssb_set_devtypedata(sdev, wl); - B43_WARN_ON(ssb_get_devtypedata(sdev) != wl); + if (wl) { + b43err(NULL, "Dual-core devices are not supported\n"); + err = -ENOTSUPP; + goto err_ssb_kfree_dev; } + + b43_sprom_fixup(sdev->bus); + + wl = b43_wireless_init(dev); + if (IS_ERR(wl)) { + err = PTR_ERR(wl); + goto err_ssb_kfree_dev; + } + ssb_set_devtypedata(sdev, wl); + B43_WARN_ON(ssb_get_devtypedata(sdev) != wl); + err = b43_one_core_attach(dev, wl); if (err) - goto err_wireless_exit; + goto err_ssb_wireless_exit; /* setup and start work to load firmware */ INIT_WORK(&wl->firmware_load, b43_request_firmware); schedule_work(&wl->firmware_load); - out: return err; - err_wireless_exit: - if (first) - b43_wireless_exit(dev, wl); +err_ssb_wireless_exit: + b43_wireless_exit(dev, wl); +err_ssb_kfree_dev: + kfree(dev); return err; } @@ -5541,13 +5555,11 @@ static void b43_ssb_remove(struct ssb_device *sdev) b43_one_core_detach(dev); - if (list_empty(&wl->devlist)) { - b43_leds_unregister(wl); - /* Last core on the chip unregistered. - * We can destroy common struct b43_wl. - */ - b43_wireless_exit(dev, wl); - } + /* Unregister HW RNG driver */ + b43_rng_exit(wl); + + b43_leds_unregister(wl); + b43_wireless_exit(dev, wl); } static struct ssb_driver b43_ssb_driver = { diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index abac25ee958..f476fc337d6 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -58,41 +58,6 @@ enum b43_verbosity { #endif }; - -/* Lightweight function to convert a frequency (in Mhz) to a channel number. */ -static inline u8 b43_freq_to_channel_5ghz(int freq) -{ - return ((freq - 5000) / 5); -} -static inline u8 b43_freq_to_channel_2ghz(int freq) -{ - u8 channel; - - if (freq == 2484) - channel = 14; - else - channel = (freq - 2407) / 5; - - return channel; -} - -/* Lightweight function to convert a channel number to a frequency (in Mhz). */ -static inline int b43_channel_to_freq_5ghz(u8 channel) -{ - return (5000 + (5 * channel)); -} -static inline int b43_channel_to_freq_2ghz(u8 channel) -{ - int freq; - - if (channel == 14) - freq = 2484; - else - freq = 2407 + (5 * channel); - - return freq; -} - static inline int b43_is_cck_rate(int rate) { return (rate == B43_CCK_RATE_1MB || diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index f01676ac481..08244b3b327 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -96,12 +96,16 @@ int b43_phy_init(struct b43_wldev *dev) phy->channel = ops->get_default_chan(dev); - ops->software_rfkill(dev, false); + phy->ops->switch_analog(dev, true); + b43_software_rfkill(dev, false); + err = ops->init(dev); if (err) { b43err(dev->wl, "PHY init failed\n"); goto err_block_rf; } + phy->do_full_init = false; + /* Make sure to switch hardware and firmware (SHM) to * the default channel. */ err = b43_switch_channel(dev, ops->get_default_chan(dev)); @@ -113,10 +117,11 @@ int b43_phy_init(struct b43_wldev *dev) return 0; err_phy_exit: + phy->do_full_init = true; if (ops->exit) ops->exit(dev); err_block_rf: - ops->software_rfkill(dev, true); + b43_software_rfkill(dev, true); return err; } @@ -125,7 +130,8 @@ void b43_phy_exit(struct b43_wldev *dev) { const struct b43_phy_operations *ops = dev->phy.ops; - ops->software_rfkill(dev, true); + b43_software_rfkill(dev, true); + dev->phy.do_full_init = true; if (ops->exit) ops->exit(dev); } @@ -133,9 +139,9 @@ void b43_phy_exit(struct b43_wldev *dev) bool b43_has_hardware_pctl(struct b43_wldev *dev) { if (!dev->phy.hardware_power_control) - return 0; + return false; if (!dev->phy.ops->supports_hwpctl) - return 0; + return false; return dev->phy.ops->supports_hwpctl(dev); } @@ -312,6 +318,90 @@ void b43_phy_maskset(struct b43_wldev *dev, u16 offset, u16 mask, u16 set) } } +void b43_phy_put_into_reset(struct b43_wldev *dev) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_GMODE; + tmp |= B43_BCMA_IOCTL_PHY_RESET; + tmp |= BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~B43_TMSLOW_GMODE; + tmp |= B43_TMSLOW_PHYRESET; + tmp |= SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + usleep_range(1000, 2000); + + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + usleep_range(1000, 2000); + + break; +#endif + } +} + +void b43_phy_take_out_of_reset(struct b43_wldev *dev) +{ + u32 tmp; + + switch (dev->dev->bus_type) { +#ifdef CONFIG_B43_BCMA + case B43_BUS_BCMA: + /* Unset reset bit (with forcing clock) */ + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_PHY_RESET; + tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; + tmp |= BCMA_IOCTL_FGC; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + + /* Do not force clock anymore */ + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~BCMA_IOCTL_FGC; + tmp |= B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + udelay(1); + break; +#endif +#ifdef CONFIG_B43_SSB + case B43_BUS_SSB: + /* Unset reset bit (with forcing clock) */ + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~B43_TMSLOW_PHYRESET; + tmp &= ~B43_TMSLOW_PHYCLKEN; + tmp |= SSB_TMSLOW_FGC; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ + usleep_range(1000, 2000); + + tmp = ssb_read32(dev->dev->sdev, SSB_TMSLOW); + tmp &= ~SSB_TMSLOW_FGC; + tmp |= B43_TMSLOW_PHYCLKEN; + ssb_write32(dev->dev->sdev, SSB_TMSLOW, tmp); + ssb_read32(dev->dev->sdev, SSB_TMSLOW); /* flush */ + usleep_range(1000, 2000); + break; +#endif + } +} + int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel) { struct b43_phy *phy = &(dev->phy); diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index f1b99934987..4ad6240d9ff 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -231,9 +231,12 @@ struct b43_phy { /* HT info */ bool is_40mhz; - /* GMODE bit enabled? */ + /* Is GMODE (2 GHz mode) bit enabled? */ bool gmode; + /* After power reset full init has to be performed */ + bool do_full_init; + /* Analog Type */ u8 analog; /* B43_PHYTYPE_ */ @@ -390,6 +393,9 @@ void b43_phy_lock(struct b43_wldev *dev); */ void b43_phy_unlock(struct b43_wldev *dev); +void b43_phy_put_into_reset(struct b43_wldev *dev); +void b43_phy_take_out_of_reset(struct b43_wldev *dev); + /** * b43_switch_channel - Switch to another channel */ diff --git a/drivers/net/wireless/b43/phy_g.c b/drivers/net/wireless/b43/phy_g.c index 12f467b8d56..8f5c14bc10e 100644 --- a/drivers/net/wireless/b43/phy_g.c +++ b/drivers/net/wireless/b43/phy_g.c @@ -1587,6 +1587,7 @@ static void b43_phy_initb5(struct b43_wldev *dev) b43_write16(dev, 0x03E4, (b43_read16(dev, 0x03E4) & 0xFFC0) | 0x0004); } +/* http://bcm-v4.sipsolutions.net/802.11/PHY/Init/B6 */ static void b43_phy_initb6(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; @@ -1670,7 +1671,7 @@ static void b43_phy_initb6(struct b43_wldev *dev) b43_radio_write16(dev, 0x50, 0x20); } if (phy->radio_rev <= 2) { - b43_radio_write16(dev, 0x7C, 0x20); + b43_radio_write16(dev, 0x50, 0x20); b43_radio_write16(dev, 0x5A, 0x70); b43_radio_write16(dev, 0x5B, 0x7B); b43_radio_write16(dev, 0x5C, 0xB0); @@ -1686,9 +1687,8 @@ static void b43_phy_initb6(struct b43_wldev *dev) b43_phy_write(dev, 0x2A, 0x8AC0); b43_phy_write(dev, 0x0038, 0x0668); b43_set_txpower_g(dev, &gphy->bbatt, &gphy->rfatt, gphy->tx_control); - if (phy->radio_rev <= 5) { + if (phy->radio_rev == 4 || phy->radio_rev == 5) b43_phy_maskset(dev, 0x5D, 0xFF80, 0x0003); - } if (phy->radio_rev <= 2) b43_radio_write16(dev, 0x005D, 0x000D); diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 7c970d3ae35..86569f6a870 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -164,7 +164,8 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, } en_addr = en_addrs[override][i]; - val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; + if (e) + val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; if (off) { b43_phy_mask(dev, en_addr, ~en_mask); @@ -256,6 +257,72 @@ static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field, } } +static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev, + enum n_intc_override intc_override, + u16 value, u8 core_sel) +{ + u16 reg, tmp, tmp2, val; + int core; + + for (core = 0; core < 2; core++) { + if ((core_sel == 1 && core != 0) || + (core_sel == 2 && core != 1)) + continue; + + reg = (core == 0) ? B43_NPHY_RFCTL_INTC1 : B43_NPHY_RFCTL_INTC2; + + switch (intc_override) { + case N_INTC_OVERRIDE_OFF: + b43_phy_write(dev, reg, 0); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + break; + case N_INTC_OVERRIDE_TRSW: + b43_phy_maskset(dev, reg, ~0xC0, value << 6); + b43_phy_set(dev, reg, 0x400); + + b43_phy_mask(dev, 0x2ff, ~0xC000 & 0xFFFF); + b43_phy_set(dev, 0x2ff, 0x2000); + b43_phy_set(dev, 0x2ff, 0x0001); + break; + case N_INTC_OVERRIDE_PA: + tmp = 0x0030; + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + val = value << 5; + else + val = value << 4; + b43_phy_maskset(dev, reg, ~tmp, val); + b43_phy_set(dev, reg, 0x1000); + break; + case N_INTC_OVERRIDE_EXT_LNA_PU: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0001; + tmp2 = 0x0004; + val = value; + } else { + tmp = 0x0004; + tmp2 = 0x0001; + val = value << 2; + } + b43_phy_maskset(dev, reg, ~tmp, val); + b43_phy_mask(dev, reg, ~tmp2); + break; + case N_INTC_OVERRIDE_EXT_LNA_GAIN: + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + tmp = 0x0002; + tmp2 = 0x0008; + val = value << 1; + } else { + tmp = 0x0008; + tmp2 = 0x0002; + val = value << 3; + } + b43_phy_maskset(dev, reg, ~tmp, val); + b43_phy_mask(dev, reg, ~tmp2); + break; + } + } +} + /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlIntcOverride */ static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev, enum n_intc_override intc_override, @@ -264,6 +331,12 @@ static void b43_nphy_rf_ctl_intc_override(struct b43_wldev *dev, u8 i, j; u16 reg, tmp, val; + if (dev->phy.rev >= 7) { + b43_nphy_rf_ctl_intc_override_rev7(dev, intc_override, value, + core); + return; + } + B43_WARN_ON(dev->phy.rev < 3); for (i = 0; i < 2; i++) { @@ -418,7 +491,8 @@ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) static const u16 clip[] = { 0xFFFF, 0xFFFF }; if (nphy->deaf_count++ == 0) { nphy->classifier_state = b43_nphy_classifier(dev, 0, 0); - b43_nphy_classifier(dev, 0x7, 0); + b43_nphy_classifier(dev, 0x7, + B43_NPHY_CLASSCTL_WAITEDEN); b43_nphy_read_clip_detection(dev, nphy->clip_state); b43_nphy_write_clip_detection(dev, clip); } @@ -626,13 +700,11 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev) b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x78); b43_radio_mask(dev, R2057_XTAL_CONFIG2, ~0x80); - if (dev->phy.n->init_por) { + if (dev->phy.do_full_init) { b43_radio_2057_rcal(dev); b43_radio_2057_rccal(dev); } b43_radio_mask(dev, R2057_RFPLL_MASTER, ~0x8); - - dev->phy.n->init_por = false; } /* http://bcm-v4.sipsolutions.net/802.11/Radio/2057/Init */ @@ -733,9 +805,16 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, u16 bias, cbias; u16 pag_boost, padg_boost, pgag_boost, mixg_boost; u16 paa_boost, pada_boost, pgaa_boost, mixa_boost; + bool is_pkg_fab_smic; B43_WARN_ON(dev->phy.rev < 3); + is_pkg_fab_smic = + ((dev->dev->chip_id == BCMA_CHIP_ID_BCM43224 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM43225 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM43421) && + dev->dev->chip_pkg == BCMA_PKG_ID_BCM43224_FAB_SMIC); + b43_chantab_radio_2056_upload(dev, e); b2056_upload_syn_pll_cp2(dev, band == IEEE80211_BAND_5GHZ); @@ -743,7 +822,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1F); - if (dev->dev->chip_id == 0x4716) { + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x14); b43_radio_write(dev, B2056_SYN_PLL_CP2, 0); } else { @@ -751,6 +831,13 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x14); } } + if (sprom->boardflags2_hi & B43_BFH2_GPLL_WAR2 && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1f); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER2, 0x1f); + b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER4, 0x0b); + b43_radio_write(dev, B2056_SYN_PLL_CP2, 0x20); + } if (sprom->boardflags2_lo & B43_BFL2_APLL_WAR && b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { b43_radio_write(dev, B2056_SYN_PLL_LOOPFILTER1, 0x1F); @@ -766,7 +853,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, b43_radio_write(dev, offset | B2056_TX_PADG_IDAC, 0xcc); - if (dev->dev->chip_id == 0x4716) { + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM4716 || + dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) { bias = 0x40; cbias = 0x45; pag_boost = 0x5; @@ -775,6 +863,10 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, } else { bias = 0x25; cbias = 0x20; + if (is_pkg_fab_smic) { + bias = 0x2a; + cbias = 0x38; + } pag_boost = 0x4; pgag_boost = 0x03; mixg_boost = 0x65; @@ -843,6 +935,8 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, mixa_boost = 0xF; } + cbias = is_pkg_fab_smic ? 0x35 : 0x30; + for (i = 0; i < 2; i++) { offset = i ? B2056_TX1 : B2056_TX0; @@ -861,11 +955,11 @@ static void b43_radio_2056_setup(struct b43_wldev *dev, b43_radio_write(dev, offset | B2056_TX_PADA_CASCBIAS, 0x03); b43_radio_write(dev, - offset | B2056_TX_INTPAA_IAUX_STAT, 0x50); + offset | B2056_TX_INTPAA_IAUX_STAT, 0x30); b43_radio_write(dev, - offset | B2056_TX_INTPAA_IMAIN_STAT, 0x50); + offset | B2056_TX_INTPAA_IMAIN_STAT, 0x30); b43_radio_write(dev, - offset | B2056_TX_INTPAA_CASCBIAS, 0x30); + offset | B2056_TX_INTPAA_CASCBIAS, cbias); } } @@ -932,7 +1026,7 @@ static void b43_radio_init2056_post(struct b43_wldev *dev) b43_radio_mask(dev, B2056_SYN_COM_RESET, ~0x2); b43_radio_mask(dev, B2056_SYN_PLL_MAST2, ~0xFC); b43_radio_mask(dev, B2056_SYN_RCCAL_CTRL0, ~0x1); - if (dev->phy.n->init_por) + if (dev->phy.do_full_init) b43_radio_2056_rcal(dev); } @@ -945,8 +1039,6 @@ static void b43_radio_init2056(struct b43_wldev *dev) b43_radio_init2056_pre(dev); b2056_upload_inittabs(dev, 0, 0); b43_radio_init2056_post(dev); - - dev->phy.n->init_por = false; } /************************************************** @@ -1163,23 +1255,20 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, u16 seq_mode; u32 tmp; - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, true); + b43_nphy_stay_in_carrier_search(dev, true); if ((nphy->bb_mult_save & 0x80000000) == 0) { tmp = b43_ntab_read(dev, B43_NTAB16(15, 87)); nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; } + /* TODO: add modify_bbmult argument */ if (!dev->phy.is_40mhz) tmp = 0x6464; else tmp = 0x4747; b43_ntab_write(dev, B43_NTAB16(15, 87), tmp); - if (nphy->hang_avoid) - b43_nphy_stay_in_carrier_search(dev, false); - b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1)); if (loops != 0xFFFF) @@ -1212,6 +1301,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, b43err(dev->wl, "run samples timeout\n"); b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); + + b43_nphy_stay_in_carrier_search(dev, false); } /************************************************** @@ -1587,8 +1678,8 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) struct b43_phy_n *nphy = dev->phy.n; u16 saved_regs_phy_rfctl[2]; - u16 saved_regs_phy[13]; - u16 regs_to_store[] = { + u16 saved_regs_phy[22]; + u16 regs_to_store_rev3[] = { B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, @@ -1597,6 +1688,20 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 }; + u16 regs_to_store_rev7[] = { + B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, + B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, + B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, + 0x342, 0x343, 0x346, 0x347, + 0x2ff, + B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, + B43_NPHY_RFCTL_CMD, + B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, + 0x340, 0x341, 0x344, 0x345, + B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 + }; + u16 *regs_to_store; + int regs_amount; u16 class; @@ -1616,6 +1721,15 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) u8 rx_core_state; int core, i, j, vcm; + if (dev->phy.rev >= 7) { + regs_to_store = regs_to_store_rev7; + regs_amount = ARRAY_SIZE(regs_to_store_rev7); + } else { + regs_to_store = regs_to_store_rev3; + regs_amount = ARRAY_SIZE(regs_to_store_rev3); + } + BUG_ON(regs_amount > ARRAY_SIZE(saved_regs_phy)); + class = b43_nphy_classifier(dev, 0, 0); b43_nphy_classifier(dev, 7, 4); b43_nphy_read_clip_detection(dev, clip_state); @@ -1623,22 +1737,29 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) saved_regs_phy_rfctl[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); saved_regs_phy_rfctl[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - for (i = 0; i < ARRAY_SIZE(regs_to_store); i++) + for (i = 0; i < regs_amount; i++) saved_regs_phy[i] = b43_phy_read(dev, regs_to_store[i]); b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_OFF, 0, 7); b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7); - b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false); - b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false); - b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false); - b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false); - - if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false); - b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false); + + if (dev->phy.rev >= 7) { + /* TODO */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + } else { + } } else { - b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false); - b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false); + b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false); + b43_nphy_rf_ctl_override(dev, 0x2, 1, 0, false); + b43_nphy_rf_ctl_override(dev, 0x80, 1, 0, false); + b43_nphy_rf_ctl_override(dev, 0x40, 1, 0, false); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_nphy_rf_ctl_override(dev, 0x20, 0, 0, false); + b43_nphy_rf_ctl_override(dev, 0x10, 1, 0, false); + } else { + b43_nphy_rf_ctl_override(dev, 0x10, 0, 0, false); + b43_nphy_rf_ctl_override(dev, 0x20, 1, 0, false); + } } rx_core_state = b43_nphy_get_rx_core_state(dev); @@ -1653,8 +1774,11 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) /* Grab RSSI results for every possible VCM */ for (vcm = 0; vcm < 8; vcm++) { - b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3, - vcm << 2); + if (dev->phy.rev >= 7) + ; + else + b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, + 0xE3, vcm << 2); b43_nphy_poll_rssi(dev, N_RSSI_NB, results[vcm], 8); } @@ -1681,8 +1805,11 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) } /* Select the best VCM */ - b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3, - vcm_final << 2); + if (dev->phy.rev >= 7) + ; + else + b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, + 0xE3, vcm_final << 2); for (i = 0; i < 4; i++) { if (core != i / 2) @@ -1735,9 +1862,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) b43_phy_set(dev, B43_NPHY_RFCTL_OVER, 0x1); b43_phy_set(dev, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_CMD_RXTX); - b43_phy_mask(dev, B43_NPHY_TXF_40CO_B1S1, ~0x1); + b43_phy_mask(dev, B43_NPHY_RFCTL_OVER, ~0x1); - for (i = 0; i < ARRAY_SIZE(regs_to_store); i++) + for (i = 0; i < regs_amount; i++) b43_phy_write(dev, regs_to_store[i], saved_regs_phy[i]); /* Store for future configuration */ @@ -2493,8 +2620,8 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) struct ssb_sprom *sprom = dev->dev->bus_sprom; /* TX to RX */ - u8 tx2rx_events[8] = { 0x4, 0x3, 0x6, 0x5, 0x2, 0x1, 0x8, 0x1F }; - u8 tx2rx_delays[8] = { 8, 4, 2, 2, 4, 4, 6, 1 }; + u8 tx2rx_events[7] = { 0x4, 0x3, 0x5, 0x2, 0x1, 0x8, 0x1F }; + u8 tx2rx_delays[7] = { 8, 4, 4, 4, 4, 6, 1 }; /* RX to TX */ u8 rx2tx_events_ipa[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0xF, 0x3, 0x1F }; @@ -2502,6 +2629,23 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) u8 rx2tx_events[9] = { 0x0, 0x1, 0x2, 0x8, 0x5, 0x6, 0x3, 0x4, 0x1F }; u8 rx2tx_delays[9] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 }; + u16 vmids[5][4] = { + { 0xa2, 0xb4, 0xb4, 0x89, }, /* 0 */ + { 0xb4, 0xb4, 0xb4, 0x24, }, /* 1 */ + { 0xa2, 0xb4, 0xb4, 0x74, }, /* 2 */ + { 0xa2, 0xb4, 0xb4, 0x270, }, /* 3 */ + { 0xa2, 0xb4, 0xb4, 0x00, }, /* 4 and 5 */ + }; + u16 gains[5][4] = { + { 0x02, 0x02, 0x02, 0x00, }, /* 0 */ + { 0x02, 0x02, 0x02, 0x02, }, /* 1 */ + { 0x02, 0x02, 0x02, 0x04, }, /* 2 */ + { 0x02, 0x02, 0x02, 0x00, }, /* 3 */ + { 0x02, 0x02, 0x02, 0x00, }, /* 4 and 5 */ + }; + u16 *vmid, *gain; + + u8 pdet_range; u16 tmp16; u32 tmp32; @@ -2560,7 +2704,71 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) b43_ntab_write(dev, B43_NTAB16(8, 0), 2); b43_ntab_write(dev, B43_NTAB16(8, 16), 2); - /* TODO */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + pdet_range = sprom->fem.ghz2.pdet_range; + else + pdet_range = sprom->fem.ghz5.pdet_range; + vmid = vmids[min_t(u16, pdet_range, 4)]; + gain = gains[min_t(u16, pdet_range, 4)]; + switch (pdet_range) { + case 3: + if (!(dev->phy.rev >= 4 && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) + break; + /* FALL THROUGH */ + case 0: + case 1: + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); + break; + case 2: + if (dev->phy.rev >= 6) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + vmid[3] = 0x94; + else + vmid[3] = 0x8e; + gain[3] = 3; + } else if (dev->phy.rev == 5) { + vmid[3] = 0x84; + gain[3] = 2; + } + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); + break; + case 4: + case 5: + if (b43_current_band(dev->wl) != IEEE80211_BAND_2GHZ) { + if (pdet_range == 4) { + vmid[3] = 0x8e; + tmp16 = 0x96; + gain[3] = 0x2; + } else { + vmid[3] = 0x89; + tmp16 = 0x89; + gain[3] = 0; + } + } else { + if (pdet_range == 4) { + vmid[3] = 0x89; + tmp16 = 0x8b; + gain[3] = 0x2; + } else { + vmid[3] = 0x74; + tmp16 = 0x70; + gain[3] = 0; + } + } + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x08), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x0c), 4, gain); + vmid[3] = tmp16; + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x18), 4, vmid); + b43_ntab_write_bulk(dev, B43_NTAB16(8, 0x1c), 4, gain); + break; + } b43_radio_write(dev, B2056_RX0 | B2056_RX_MIXA_MAST_BIAS, 0x00); b43_radio_write(dev, B2056_RX1 | B2056_RX_MIXA_MAST_BIAS, 0x00); @@ -2599,7 +2807,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev) /* Dropped probably-always-true condition */ b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH0, 0x03eb); b43_phy_write(dev, B43_NPHY_ED_CRS40ASSERTTHRESH1, 0x03eb); - b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341); + b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH0, 0x0341); b43_phy_write(dev, B43_NPHY_ED_CRS40DEASSERTTHRESH1, 0x0341); b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH0, 0x042b); b43_phy_write(dev, B43_NPHY_ED_CRS20LASSERTTHRESH1, 0x042b); @@ -3210,6 +3418,20 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev) u8 idx, delta; u8 i, stf_mode; + /* Array adj_pwr_tbl corresponds to the hardware table. It consists of + * 21 groups, each containing 4 entries. + * + * First group has entries for CCK modulation. + * The rest of groups has 1 entry per modulation (SISO, CDD, STBC, SDM). + * + * Group 0 is for CCK + * Groups 1..4 use BPSK (group per coding rate) + * Groups 5..8 use QPSK (group per coding rate) + * Groups 9..12 use 16-QAM (group per coding rate) + * Groups 13..16 use 64-QAM (group per coding rate) + * Groups 17..20 are unknown + */ + for (i = 0; i < 4; i++) nphy->adj_pwr_tbl[i] = nphy->tx_power_offset[i]; @@ -3408,10 +3630,8 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) } b43_nphy_tx_prepare_adjusted_power_table(dev); - /* b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84, nphy->adj_pwr_tbl); b43_ntab_write_bulk(dev, B43_NTAB16(27, 64), 84, nphy->adj_pwr_tbl); - */ if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, false); @@ -5123,7 +5343,7 @@ static int b43_phy_initn(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015); b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); if (phy->rev >= 3 && phy->rev <= 6) - b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014); + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032); b43_nphy_tx_lp_fbw(dev); if (phy->rev >= 3) b43_nphy_spur_workaround(dev); @@ -5175,22 +5395,22 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, int ch = new_channel->hw_value; u16 old_band_5ghz; - u32 tmp32; + u16 tmp16; old_band_5ghz = b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ; if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) { - tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4); + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ); } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) { b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); - tmp32 = b43_read32(dev, B43_MMIO_PSM_PHY_HDR); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32 | 4); + tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); b43_phy_mask(dev, B43_PHY_B_BBCFG, 0x3FFF); - b43_write32(dev, B43_MMIO_PSM_PHY_HDR, tmp32); + b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); } b43_chantab_phy_upload(dev, e); @@ -5337,7 +5557,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) nphy->hang_avoid = (phy->rev == 3 || phy->rev == 4); nphy->spur_avoid = (phy->rev >= 3) ? B43_SPUR_AVOID_AUTO : B43_SPUR_AVOID_DISABLE; - nphy->init_por = true; nphy->gain_boost = true; /* this way we follow wl, assume it is true */ nphy->txrx_chain = 2; /* sth different than 0 and 1 for now */ nphy->phyrxchain = 3; /* to avoid b43_nphy_set_rx_core_state like wl */ @@ -5378,8 +5597,6 @@ static void b43_nphy_op_prepare_structs(struct b43_wldev *dev) nphy->ipa2g_on = sprom->fem.ghz2.extpa_gain == 2; nphy->ipa5g_on = sprom->fem.ghz5.extpa_gain == 2; } - - nphy->init_por = true; } static void b43_nphy_op_free(struct b43_wldev *dev) @@ -5440,8 +5657,11 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ B43_WARN_ON(reg == 1); - /* N-PHY needs 0x100 for read access */ - reg |= 0x100; + + if (dev->phy.rev >= 7) + reg |= 0x200; /* Radio 0x2057 */ + else + reg |= 0x100; b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); return b43_read16(dev, B43_MMIO_RADIO_DATA_LOW); @@ -5487,10 +5707,12 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, } } else { if (dev->phy.rev >= 7) { - b43_radio_2057_init(dev); + if (!dev->phy.radio_on) + b43_radio_2057_init(dev); b43_switch_channel(dev, dev->phy.channel); } else if (dev->phy.rev >= 3) { - b43_radio_init2056(dev); + if (!dev->phy.radio_on) + b43_radio_init2056(dev); b43_switch_channel(dev, dev->phy.channel); } else { b43_radio_init2055(dev); diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 9a5b6bc27d2..ecfbf66dbc3 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -931,7 +931,6 @@ struct b43_phy_n { u16 papd_epsilon_offset[2]; s32 preamble_override; u32 bb_mult_save; - bool init_por; bool gain_boost; bool elna_gain_config; diff --git a/drivers/net/wireless/b43/pio.c b/drivers/net/wireless/b43/pio.c index a73ff8c9deb..a4ff5e2a42b 100644 --- a/drivers/net/wireless/b43/pio.c +++ b/drivers/net/wireless/b43/pio.c @@ -637,7 +637,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q) ctl = b43_piorx_read32(q, B43_PIO8_RXCTL); if (!(ctl & B43_PIO8_RXCTL_FRAMERDY)) - return 0; + return false; b43_piorx_write32(q, B43_PIO8_RXCTL, B43_PIO8_RXCTL_FRAMERDY); for (i = 0; i < 10; i++) { @@ -651,7 +651,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q) ctl = b43_piorx_read16(q, B43_PIO_RXCTL); if (!(ctl & B43_PIO_RXCTL_FRAMERDY)) - return 0; + return false; b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_FRAMERDY); for (i = 0; i < 10; i++) { @@ -662,7 +662,7 @@ static bool pio_rx_frame(struct b43_pio_rxqueue *q) } } b43dbg(q->dev->wl, "PIO RX timed out\n"); - return 1; + return true; data_ready: /* Get the preamble (RX header) */ @@ -759,7 +759,7 @@ data_ready: b43_rx(q->dev, skb, rxhdr); - return 1; + return true; rx_error: if (err_msg) @@ -769,7 +769,7 @@ rx_error: else b43_piorx_write16(q, B43_PIO_RXCTL, B43_PIO_RXCTL_DATARDY); - return 1; + return true; } void b43_pio_rx(struct b43_pio_rxqueue *q) diff --git a/drivers/net/wireless/b43/radio_2056.c b/drivers/net/wireless/b43/radio_2056.c index b4fd9345d67..2ce25607c60 100644 --- a/drivers/net/wireless/b43/radio_2056.c +++ b/drivers/net/wireless/b43/radio_2056.c @@ -48,7 +48,7 @@ struct b2056_inittabs_pts { unsigned int rx_length; }; -static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = { +static const struct b2056_inittab_entry b2056_inittab_phy_rev3_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -232,7 +232,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_syn[] = { [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = { +static const struct b2056_inittab_entry b2056_inittab_phy_rev3_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -380,7 +380,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_tx[] = { [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = { +static const struct b2056_inittab_entry b2056_inittab_phy_rev3_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -530,7 +530,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev3_rx[] = { [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = { +static const struct b2056_inittab_entry b2056_inittab_phy_rev4_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -714,7 +714,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_syn[] = { [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = { +static const struct b2056_inittab_entry b2056_inittab_phy_rev4_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -862,7 +862,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_tx[] = { [B2056_TX_STATUS_TXLPF_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = { +static const struct b2056_inittab_entry b2056_inittab_phy_rev4_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1012,7 +1012,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev4_rx[] = { [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev5_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1196,7 +1196,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_syn[] = { [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev5_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1352,7 +1352,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_tx[] = { [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev5_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1502,7 +1502,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev5_rx[] = { [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev6_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1686,7 +1686,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_syn[] = { [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev6_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1842,7 +1842,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_tx[] = { [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev6_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -1992,7 +1992,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev6_rx[] = { [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -2176,7 +2176,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_syn[] = { [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -2332,7 +2332,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_tx[] = { [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0075, .ghz2 = 0x0075, UPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev7_9_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -2482,7 +2482,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev7_rx[] = { [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev8_syn[] = { [B2056_SYN_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_SYN_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -2666,7 +2666,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_syn[] = { [B2056_SYN_LOGEN_TX_CMOS_VALID] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev8_tx[] = { [B2056_TX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_TX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -2822,7 +2822,7 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_tx[] = { [B2056_TX_GMBB_IDAC7] = { .ghz5 = 0x0070, .ghz2 = 0x0070, NOUPLOAD, }, }; -static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = { +static const struct b2056_inittab_entry b2056_inittab_radio_rev8_rx[] = { [B2056_RX_RESERVED_ADDR2] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR3] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, [B2056_RX_RESERVED_ADDR4] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, @@ -2972,24 +2972,69 @@ static const struct b2056_inittab_entry b2056_inittab_rev8_rx[] = { [B2056_RX_STATUS_HPC_RC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, NOUPLOAD, }, }; -#define INITTABSPTS(prefix) \ - .syn = prefix##_syn, \ - .syn_length = ARRAY_SIZE(prefix##_syn), \ - .tx = prefix##_tx, \ - .tx_length = ARRAY_SIZE(prefix##_tx), \ - .rx = prefix##_rx, \ - .rx_length = ARRAY_SIZE(prefix##_rx) +static const struct b2056_inittab_entry b2056_inittab_radio_rev11_syn[] = { + [B2056_SYN_PLL_PFD] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_CP2] = { .ghz5 = 0x003f, .ghz2 = 0x003f, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER1] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER2] = { .ghz5 = 0x0006, .ghz2 = 0x0006, UPLOAD, }, + [B2056_SYN_PLL_LOOPFILTER4] = { .ghz5 = 0x002b, .ghz2 = 0x002b, UPLOAD, }, + [B2056_SYN_PLL_VCO2] = { .ghz5 = 0x00f7, .ghz2 = 0x00f7, UPLOAD, }, + [B2056_SYN_PLL_VCOCAL12] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_SYN_LOGENBUF2] = { .ghz5 = 0x008f, .ghz2 = 0x008f, UPLOAD, }, +}; -static const struct b2056_inittabs_pts b2056_inittabs[] = { - [3] = { INITTABSPTS(b2056_inittab_rev3) }, - [4] = { INITTABSPTS(b2056_inittab_rev4) }, - [5] = { INITTABSPTS(b2056_inittab_rev5) }, - [6] = { INITTABSPTS(b2056_inittab_rev6) }, - [7] = { INITTABSPTS(b2056_inittab_rev7) }, - [8] = { INITTABSPTS(b2056_inittab_rev8) }, - [9] = { INITTABSPTS(b2056_inittab_rev7) }, +static const struct b2056_inittab_entry b2056_inittab_radio_rev11_tx[] = { + [B2056_TX_PA_SPARE2] = { .ghz5 = 0x00ee, .ghz2 = 0x00ee, UPLOAD, }, + [B2056_TX_INTPAA_IAUX_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_IMAIN_STAT] = { .ghz5 = 0x0050, .ghz2 = 0x0050, UPLOAD, }, + [B2056_TX_INTPAA_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_INTPAG_PASLOPE] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_TX_PADA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PADA_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PADG_SLOPE] = { .ghz5 = 0x0070, .ghz2 = 0x0070, UPLOAD, }, + [B2056_TX_PGAA_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_TX_PGAA_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_PGAG_SLOPE] = { .ghz5 = 0x0077, .ghz2 = 0x0077, UPLOAD, }, + [B2056_TX_GMBB_IDAC] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_TX_TXSPARE1] = { .ghz5 = 0x0030, .ghz2 = 0x0030, UPLOAD, }, +}; + +static const struct b2056_inittab_entry b2056_inittab_radio_rev11_rx[] = { + [B2056_RX_BIASPOLE_LNAA1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAA2_IDAC] = { .ghz5 = 0x00ff, .ghz2 = 0x00ff, UPLOAD, }, + [B2056_RX_BIASPOLE_LNAG1_IDAC] = { .ghz5 = 0x0017, .ghz2 = 0x0017, UPLOAD, }, + [B2056_RX_LNAG2_IDAC] = { .ghz5 = 0x00f0, .ghz2 = 0x00f0, UPLOAD, }, + [B2056_RX_MIXA_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_MIXA_LOB_BIAS] = { .ghz5 = 0x0088, .ghz2 = 0x0088, UPLOAD, }, + [B2056_RX_MIXA_BIAS_AUX] = { .ghz5 = 0x0007, .ghz2 = 0x0007, UPLOAD, }, + [B2056_RX_MIXG_VCM] = { .ghz5 = 0x0055, .ghz2 = 0x0055, UPLOAD, }, + [B2056_RX_TIA_IOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_QOPAMP] = { .ghz5 = 0x0026, .ghz2 = 0x0026, UPLOAD, }, + [B2056_RX_TIA_IMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_TIA_QMISC] = { .ghz5 = 0x000f, .ghz2 = 0x000f, UPLOAD, }, + [B2056_RX_RXLPF_OUTVCM] = { .ghz5 = 0x0004, .ghz2 = 0x0004, UPLOAD, }, + [B2056_RX_VGA_BIAS_DCCANCEL] = { .ghz5 = 0x0000, .ghz2 = 0x0000, UPLOAD, }, + [B2056_RX_RXSPARE3] = { .ghz5 = 0x0005, .ghz2 = 0x0005, UPLOAD, }, }; +#define INITTABSPTS(prefix) \ + static const struct b2056_inittabs_pts prefix = { \ + .syn = prefix##_syn, \ + .syn_length = ARRAY_SIZE(prefix##_syn), \ + .tx = prefix##_tx, \ + .tx_length = ARRAY_SIZE(prefix##_tx), \ + .rx = prefix##_rx, \ + .rx_length = ARRAY_SIZE(prefix##_rx), \ + } + +INITTABSPTS(b2056_inittab_phy_rev3); +INITTABSPTS(b2056_inittab_phy_rev4); +INITTABSPTS(b2056_inittab_radio_rev5); +INITTABSPTS(b2056_inittab_radio_rev6); +INITTABSPTS(b2056_inittab_radio_rev7_9); +INITTABSPTS(b2056_inittab_radio_rev8); +INITTABSPTS(b2056_inittab_radio_rev11); + #define RADIOREGS3(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ r20, r21, r22, r23, r24, r25, r26, r27, r28, r29, \ @@ -3041,7 +3086,7 @@ static const struct b2056_inittabs_pts b2056_inittabs[] = { .phy_regs.phy_bw6 = r5 /* http://bcm-v4.sipsolutions.net/802.11/Radio/2056/ChannelTable */ -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] = { +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev3[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, @@ -4036,7 +4081,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev3[] = }, }; -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] = { +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_phy_rev4[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, @@ -5031,7 +5076,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev4[] = }, }; -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] = { +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev5[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, @@ -6026,7 +6071,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev5[] = }, }; -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] = { +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev6[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, @@ -7021,7 +7066,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev6[] = }, }; -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[] = { +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev7_9[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, @@ -8016,7 +8061,7 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev7_9[] }, }; -static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] = { +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev8[] = { { .freq = 4920, RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x04, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, @@ -9011,6 +9056,1154 @@ static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_rev8[] = }, }; +static const struct b43_nphy_channeltab_entry_rev3 b43_nphy_channeltab_radio_rev11[] = { + { + .freq = 4920, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xec, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b4, 0x07b0, 0x07ac, 0x0214, 0x0215, 0x0216), + }, + { + .freq = 4930, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xed, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07b8, 0x07b4, 0x07b0, 0x0213, 0x0214, 0x0215), + }, + { + .freq = 4940, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xee, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07bc, 0x07b8, 0x07b4, 0x0212, 0x0213, 0x0214), + }, + { + .freq = 4950, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xef, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x00, 0x00, 0x00, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c0, 0x07bc, 0x07b8, 0x0211, 0x0212, 0x0213), + }, + { + .freq = 4960, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf0, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c4, 0x07c0, 0x07bc, 0x020f, 0x0211, 0x0212), + }, + { + .freq = 4970, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf1, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07c8, 0x07c4, 0x07c0, 0x020e, 0x020f, 0x0211), + }, + { + .freq = 4980, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf2, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07cc, 0x07c8, 0x07c4, 0x020d, 0x020e, 0x020f), + }, + { + .freq = 4990, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf3, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d0, 0x07cc, 0x07c8, 0x020c, 0x020d, 0x020e), + }, + { + .freq = 5000, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf4, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d4, 0x07d0, 0x07cc, 0x020b, 0x020c, 0x020d), + }, + { + .freq = 5010, + RADIOREGS3(0xff, 0x01, 0x01, 0x01, 0xf5, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07d8, 0x07d4, 0x07d0, 0x020a, 0x020b, 0x020c), + }, + { + .freq = 5020, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf6, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07dc, 0x07d8, 0x07d4, 0x0209, 0x020a, 0x020b), + }, + { + .freq = 5030, + RADIOREGS3(0xf7, 0x01, 0x01, 0x01, 0xf7, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e0, 0x07dc, 0x07d8, 0x0208, 0x0209, 0x020a), + }, + { + .freq = 5040, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf8, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e4, 0x07e0, 0x07dc, 0x0207, 0x0208, 0x0209), + }, + { + .freq = 5050, + RADIOREGS3(0xef, 0x01, 0x01, 0x01, 0xf9, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07e8, 0x07e4, 0x07e0, 0x0206, 0x0207, 0x0208), + }, + { + .freq = 5060, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfa, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfe, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfe, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07ec, 0x07e8, 0x07e4, 0x0205, 0x0206, 0x0207), + }, + { + .freq = 5070, + RADIOREGS3(0xe6, 0x01, 0x01, 0x01, 0xfb, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f0, 0x07ec, 0x07e8, 0x0204, 0x0205, 0x0206), + }, + { + .freq = 5080, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfc, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f4, 0x07f0, 0x07ec, 0x0203, 0x0204, 0x0205), + }, + { + .freq = 5090, + RADIOREGS3(0xde, 0x01, 0x01, 0x01, 0xfd, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x01, 0x01, 0x01, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x09, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x09, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07f8, 0x07f4, 0x07f0, 0x0202, 0x0203, 0x0204), + }, + { + .freq = 5100, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xfe, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfd, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfd, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x07fc, 0x07f8, 0x07f4, 0x0201, 0x0202, 0x0203), + }, + { + .freq = 5110, + RADIOREGS3(0xd6, 0x01, 0x01, 0x01, 0xff, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0800, 0x07fc, 0x07f8, 0x0200, 0x0201, 0x0202), + }, + { + .freq = 5120, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x00, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0804, 0x0800, 0x07fc, 0x01ff, 0x0200, 0x0201), + }, + { + .freq = 5130, + RADIOREGS3(0xce, 0x01, 0x01, 0x02, 0x01, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfc, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfc, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x0808, 0x0804, 0x0800, 0x01fe, 0x01ff, 0x0200), + }, + { + .freq = 5140, + RADIOREGS3(0xc6, 0x01, 0x01, 0x02, 0x02, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfb, 0x00, 0x08, 0x00, 0x77, 0x00, 0x0f, + 0x00, 0x6f, 0x00, 0xfb, 0x00, 0x08, 0x00, 0x77, + 0x00, 0x0f, 0x00, 0x6f, 0x00), + PHYREGS(0x080c, 0x0808, 0x0804, 0x01fd, 0x01fe, 0x01ff), + }, + { + .freq = 5160, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x04, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0814, 0x0810, 0x080c, 0x01fb, 0x01fc, 0x01fd), + }, + { + .freq = 5170, + RADIOREGS3(0xbe, 0x01, 0x01, 0x02, 0x05, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xfa, 0x00, 0x07, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xfa, 0x00, 0x07, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x0818, 0x0814, 0x0810, 0x01fa, 0x01fb, 0x01fc), + }, + { + .freq = 5180, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x06, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0e, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0e, 0x00, 0x6f, 0x00), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { + .freq = 5190, + RADIOREGS3(0xb6, 0x01, 0x01, 0x02, 0x07, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x06, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x06, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0820, 0x081c, 0x0818, 0x01f8, 0x01f9, 0x01fa), + }, + { + .freq = 5200, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x08, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { + .freq = 5210, + RADIOREGS3(0xaf, 0x01, 0x01, 0x02, 0x09, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8f, 0x0f, 0x00, + 0xff, 0xf9, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xf9, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0828, 0x0824, 0x0820, 0x01f6, 0x01f7, 0x01f8), + }, + { + .freq = 5220, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xfe, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { + .freq = 5230, + RADIOREGS3(0xa7, 0x01, 0x01, 0x02, 0x0b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xd8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xd8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0830, 0x082c, 0x0828, 0x01f4, 0x01f5, 0x01f6), + }, + { + .freq = 5240, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xee, 0xc8, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc8, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { + .freq = 5250, + RADIOREGS3(0xa0, 0x01, 0x01, 0x02, 0x0d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0f, 0x00, + 0xed, 0xc7, 0x00, 0x05, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x0838, 0x0834, 0x0830, 0x01f2, 0x01f3, 0x01f4), + }, + { + .freq = 5260, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x02, 0x02, 0x02, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0d, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0d, 0x00, 0x6f, 0x00), + PHYREGS(0x083c, 0x0838, 0x0834, 0x01f1, 0x01f2, 0x01f3), + }, + { + .freq = 5270, + RADIOREGS3(0x98, 0x01, 0x01, 0x02, 0x0f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8e, 0x0e, 0x00, + 0xed, 0xc7, 0x00, 0x04, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xc7, 0x00, 0x04, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0840, 0x083c, 0x0838, 0x01f0, 0x01f1, 0x01f2), + }, + { + .freq = 5280, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x10, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0844, 0x0840, 0x083c, 0x01f0, 0x01f0, 0x01f1), + }, + { + .freq = 5290, + RADIOREGS3(0x91, 0x01, 0x01, 0x02, 0x11, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0848, 0x0844, 0x0840, 0x01ef, 0x01f0, 0x01f0), + }, + { + .freq = 5300, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x12, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x084c, 0x0848, 0x0844, 0x01ee, 0x01ef, 0x01f0), + }, + { + .freq = 5310, + RADIOREGS3(0x8a, 0x01, 0x01, 0x02, 0x13, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdc, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0850, 0x084c, 0x0848, 0x01ed, 0x01ee, 0x01ef), + }, + { + .freq = 5320, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x14, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0e, 0x00, + 0xdb, 0xb7, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0c, + 0x00, 0x6f, 0x00, 0xb7, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0c, 0x00, 0x6f, 0x00), + PHYREGS(0x0854, 0x0850, 0x084c, 0x01ec, 0x01ed, 0x01ee), + }, + { + .freq = 5330, + RADIOREGS3(0x83, 0x01, 0x01, 0x02, 0x15, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xcb, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0858, 0x0854, 0x0850, 0x01eb, 0x01ec, 0x01ed), + }, + { + .freq = 5340, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x16, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8d, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x085c, 0x0858, 0x0854, 0x01ea, 0x01eb, 0x01ec), + }, + { + .freq = 5350, + RADIOREGS3(0x7c, 0x01, 0x01, 0x02, 0x17, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xca, 0xa6, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0b, + 0x00, 0x6f, 0x00, 0xa6, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0b, 0x00, 0x6f, 0x00), + PHYREGS(0x0860, 0x085c, 0x0858, 0x01e9, 0x01ea, 0x01eb), + }, + { + .freq = 5360, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x18, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0864, 0x0860, 0x085c, 0x01e8, 0x01e9, 0x01ea), + }, + { + .freq = 5370, + RADIOREGS3(0x75, 0x01, 0x01, 0x02, 0x19, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0d, 0x00, + 0xc9, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0868, 0x0864, 0x0860, 0x01e7, 0x01e8, 0x01e9), + }, + { + .freq = 5380, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x95, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x95, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x086c, 0x0868, 0x0864, 0x01e6, 0x01e7, 0x01e8), + }, + { + .freq = 5390, + RADIOREGS3(0x6e, 0x01, 0x01, 0x02, 0x1b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0870, 0x086c, 0x0868, 0x01e5, 0x01e6, 0x01e7), + }, + { + .freq = 5400, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb8, 0x84, 0x00, 0x03, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x03, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0874, 0x0870, 0x086c, 0x01e5, 0x01e5, 0x01e6), + }, + { + .freq = 5410, + RADIOREGS3(0x67, 0x01, 0x01, 0x02, 0x1d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xb7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0878, 0x0874, 0x0870, 0x01e4, 0x01e5, 0x01e5), + }, + { + .freq = 5420, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0c, 0x00, + 0xa7, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x087c, 0x0878, 0x0874, 0x01e3, 0x01e4, 0x01e5), + }, + { + .freq = 5430, + RADIOREGS3(0x61, 0x01, 0x01, 0x02, 0x1f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x03, 0x03, 0x03, 0x8c, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x0a, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x0a, 0x00, 0x6f, 0x00), + PHYREGS(0x0880, 0x087c, 0x0878, 0x01e2, 0x01e3, 0x01e4), + }, + { + .freq = 5440, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x20, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0xa6, 0x84, 0x00, 0x02, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x02, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0884, 0x0880, 0x087c, 0x01e1, 0x01e2, 0x01e3), + }, + { + .freq = 5450, + RADIOREGS3(0x5a, 0x01, 0x01, 0x02, 0x21, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0888, 0x0884, 0x0880, 0x01e0, 0x01e1, 0x01e2), + }, + { + .freq = 5460, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x22, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x95, 0x84, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x84, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x088c, 0x0888, 0x0884, 0x01df, 0x01e0, 0x01e1), + }, + { + .freq = 5470, + RADIOREGS3(0x53, 0x01, 0x01, 0x02, 0x23, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8b, 0x0b, 0x00, + 0x94, 0x73, 0x00, 0x01, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x01, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0890, 0x088c, 0x0888, 0x01de, 0x01df, 0x01e0), + }, + { + .freq = 5480, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x24, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x84, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0894, 0x0890, 0x088c, 0x01dd, 0x01de, 0x01df), + }, + { + .freq = 5490, + RADIOREGS3(0x4d, 0x01, 0x01, 0x02, 0x25, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x83, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x0898, 0x0894, 0x0890, 0x01dd, 0x01dd, 0x01de), + }, + { + .freq = 5500, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x26, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x089c, 0x0898, 0x0894, 0x01dc, 0x01dd, 0x01dd), + }, + { + .freq = 5510, + RADIOREGS3(0x47, 0x01, 0x01, 0x02, 0x27, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x82, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a0, 0x089c, 0x0898, 0x01db, 0x01dc, 0x01dd), + }, + { + .freq = 5520, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x28, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x0a, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a4, 0x08a0, 0x089c, 0x01da, 0x01db, 0x01dc), + }, + { + .freq = 5530, + RADIOREGS3(0x40, 0x01, 0x01, 0x02, 0x29, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x72, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08a8, 0x08a4, 0x08a0, 0x01d9, 0x01da, 0x01db), + }, + { + .freq = 5540, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x8a, 0x09, 0x00, + 0x71, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08ac, 0x08a8, 0x08a4, 0x01d8, 0x01d9, 0x01da), + }, + { + .freq = 5550, + RADIOREGS3(0x3a, 0x01, 0x01, 0x02, 0x2b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b0, 0x08ac, 0x08a8, 0x01d7, 0x01d8, 0x01d9), + }, + { + .freq = 5560, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x73, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x73, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b4, 0x08b0, 0x08ac, 0x01d7, 0x01d7, 0x01d8), + }, + { + .freq = 5570, + RADIOREGS3(0x34, 0x01, 0x01, 0x02, 0x2d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x09, 0x00, + 0x61, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x09, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x09, 0x00, 0x6f, 0x00), + PHYREGS(0x08b8, 0x08b4, 0x08b0, 0x01d6, 0x01d7, 0x01d7), + }, + { + .freq = 5580, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x60, 0x62, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x62, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08bc, 0x08b8, 0x08b4, 0x01d5, 0x01d6, 0x01d7), + }, + { + .freq = 5590, + RADIOREGS3(0x2e, 0x01, 0x01, 0x02, 0x2f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x04, 0x04, 0x04, 0x89, 0x08, 0x00, + 0x50, 0x61, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x61, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c0, 0x08bc, 0x08b8, 0x01d4, 0x01d5, 0x01d6), + }, + { + .freq = 5600, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x30, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c4, 0x08c0, 0x08bc, 0x01d3, 0x01d4, 0x01d5), + }, + { + .freq = 5610, + RADIOREGS3(0x28, 0x01, 0x01, 0x02, 0x31, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x51, 0x00, 0x00, 0x00, 0x77, 0x00, 0x08, + 0x00, 0x6f, 0x00, 0x51, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x08, 0x00, 0x6f, 0x00), + PHYREGS(0x08c8, 0x08c4, 0x08c0, 0x01d2, 0x01d3, 0x01d4), + }, + { + .freq = 5620, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x32, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x89, 0x08, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08cc, 0x08c8, 0x08c4, 0x01d2, 0x01d2, 0x01d3), + }, + { + .freq = 5630, + RADIOREGS3(0x21, 0x01, 0x01, 0x02, 0x33, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x50, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d0, 0x08cc, 0x08c8, 0x01d1, 0x01d2, 0x01d2), + }, + { + .freq = 5640, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x34, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x50, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x50, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d4, 0x08d0, 0x08cc, 0x01d0, 0x01d1, 0x01d2), + }, + { + .freq = 5650, + RADIOREGS3(0x1c, 0x01, 0x01, 0x02, 0x35, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x07, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x07, 0x00, 0x6f, 0x00), + PHYREGS(0x08d8, 0x08d4, 0x08d0, 0x01cf, 0x01d0, 0x01d1), + }, + { + .freq = 5660, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x36, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x40, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x40, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08dc, 0x08d8, 0x08d4, 0x01ce, 0x01cf, 0x01d0), + }, + { + .freq = 5670, + RADIOREGS3(0x16, 0x01, 0x01, 0x02, 0x37, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x88, 0x07, 0x00, + 0x40, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e0, 0x08dc, 0x08d8, 0x01ce, 0x01ce, 0x01cf), + }, + { + .freq = 5680, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x38, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e4, 0x08e0, 0x08dc, 0x01cd, 0x01ce, 0x01ce), + }, + { + .freq = 5690, + RADIOREGS3(0x10, 0x01, 0x01, 0x02, 0x39, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6f, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6f, 0x00), + PHYREGS(0x08e8, 0x08e4, 0x08e0, 0x01cc, 0x01cd, 0x01ce), + }, + { + .freq = 5700, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08ec, 0x08e8, 0x08e4, 0x01cb, 0x01cc, 0x01cd), + }, + { + .freq = 5710, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f0, 0x08ec, 0x08e8, 0x01ca, 0x01cb, 0x01cc), + }, + { + .freq = 5720, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f4, 0x08f0, 0x08ec, 0x01c9, 0x01ca, 0x01cb), + }, + { + .freq = 5725, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x79, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x06, 0x00, + 0x30, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f6, 0x08f2, 0x08ee, 0x01c9, 0x01ca, 0x01cb), + }, + { + .freq = 5730, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6e, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6e, 0x00), + PHYREGS(0x08f8, 0x08f4, 0x08f0, 0x01c9, 0x01c9, 0x01ca), + }, + { + .freq = 5735, + RADIOREGS3(0x03, 0x01, 0x02, 0x04, 0x7b, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fa, 0x08f6, 0x08f2, 0x01c8, 0x01c9, 0x01ca), + }, + { + .freq = 5740, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fc, 0x08f8, 0x08f4, 0x01c8, 0x01c9, 0x01c9), + }, + { + .freq = 5745, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7d, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x30, 0x00, 0x00, 0x00, 0x77, 0x00, 0x06, + 0x00, 0x6d, 0x00, 0x30, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x06, 0x00, 0x6d, 0x00), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { + .freq = 5750, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x3f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x20, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6d, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6d, 0x00), + PHYREGS(0x0900, 0x08fc, 0x08f8, 0x01c7, 0x01c8, 0x01c9), + }, + { + .freq = 5755, + RADIOREGS3(0xfe, 0x00, 0x02, 0x04, 0x7f, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x87, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0902, 0x08fe, 0x08fa, 0x01c7, 0x01c8, 0x01c8), + }, + { + .freq = 5760, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x40, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x20, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x20, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0904, 0x0900, 0x08fc, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5765, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x81, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x05, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6c, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5770, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x41, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0908, 0x0904, 0x0900, 0x01c6, 0x01c6, 0x01c7), + }, + { + .freq = 5775, + RADIOREGS3(0xf8, 0x00, 0x02, 0x04, 0x83, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090a, 0x0906, 0x0902, 0x01c5, 0x01c6, 0x01c7), + }, + { + .freq = 5780, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x42, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x05, 0x05, 0x05, 0x86, 0x04, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090c, 0x0908, 0x0904, 0x01c5, 0x01c6, 0x01c6), + }, + { + .freq = 5785, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x85, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5790, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x43, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0910, 0x090c, 0x0908, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5795, + RADIOREGS3(0xf2, 0x00, 0x02, 0x04, 0x87, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0912, 0x090e, 0x090a, 0x01c4, 0x01c4, 0x01c5), + }, + { + .freq = 5800, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x44, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6b, 0x00), + PHYREGS(0x0914, 0x0910, 0x090c, 0x01c3, 0x01c4, 0x01c5), + }, + { + .freq = 5805, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x89, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { + .freq = 5810, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x45, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x0918, 0x0914, 0x0910, 0x01c2, 0x01c3, 0x01c4), + }, + { + .freq = 5815, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8b, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091a, 0x0916, 0x0912, 0x01c2, 0x01c3, 0x01c4), + }, + { + .freq = 5820, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x46, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x6a, 0x00), + PHYREGS(0x091c, 0x0918, 0x0914, 0x01c2, 0x01c2, 0x01c3), + }, + { + .freq = 5825, + RADIOREGS3(0xed, 0x00, 0x02, 0x04, 0x8d, 0x05, 0x05, 0x02, + 0x15, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, + { + .freq = 5830, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x47, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x05, 0x00, 0x69, 0x00), + PHYREGS(0x0920, 0x091c, 0x0918, 0x01c1, 0x01c2, 0x01c2), + }, + { + .freq = 5840, + RADIOREGS3(0x0a, 0x01, 0x01, 0x02, 0x48, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x86, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0924, 0x0920, 0x091c, 0x01c0, 0x01c1, 0x01c2), + }, + { + .freq = 5850, + RADIOREGS3(0xe0, 0x00, 0x01, 0x02, 0x49, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x0928, 0x0924, 0x0920, 0x01bf, 0x01c0, 0x01c1), + }, + { + .freq = 5860, + RADIOREGS3(0xde, 0x00, 0x01, 0x02, 0x4a, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x69, 0x00), + PHYREGS(0x092c, 0x0928, 0x0924, 0x01bf, 0x01bf, 0x01c0), + }, + { + .freq = 5870, + RADIOREGS3(0xdb, 0x00, 0x01, 0x02, 0x4b, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0930, 0x092c, 0x0928, 0x01be, 0x01bf, 0x01bf), + }, + { + .freq = 5880, + RADIOREGS3(0xd8, 0x00, 0x01, 0x02, 0x4c, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0934, 0x0930, 0x092c, 0x01bd, 0x01be, 0x01bf), + }, + { + .freq = 5890, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4d, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0938, 0x0934, 0x0930, 0x01bc, 0x01bd, 0x01be), + }, + { + .freq = 5900, + RADIOREGS3(0xd3, 0x00, 0x01, 0x02, 0x4e, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x093c, 0x0938, 0x0934, 0x01bc, 0x01bc, 0x01bd), + }, + { + .freq = 5910, + RADIOREGS3(0xd6, 0x00, 0x01, 0x02, 0x4f, 0x05, 0x05, 0x02, + 0x0c, 0x01, 0x06, 0x06, 0x06, 0x85, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x04, + 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, + 0x00, 0x04, 0x00, 0x68, 0x00), + PHYREGS(0x0940, 0x093c, 0x0938, 0x01bb, 0x01bc, 0x01bc), + }, + { + .freq = 2412, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x6c, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x04, 0x04, 0x04, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x71, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x78, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x76, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x67, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0b, 0x00, 0x0a, 0x00, 0x89, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0b, 0x00, 0x0a), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x7b, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x57, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x78, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x80, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x56, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x77, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x85, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x46, 0x00, 0x03, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x76, 0x00, 0x03, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8a, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x05, 0x05, 0x05, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x45, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x0a, 0x00, 0x66, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x0a), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x8f, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x55, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x94, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x45, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x99, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x12, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x0a, 0x00, 0x09, 0x00, 0x34, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x0a, 0x00, 0x09), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0x9e, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x33, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 2467, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa3, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x06, 0x06, 0x06, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x22, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { + .freq = 2472, + RADIOREGS3(0x00, 0x01, 0x03, 0x09, 0xa8, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x30, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 2484, + RADIOREGS3(0xff, 0x01, 0x03, 0x09, 0xb4, 0x06, 0x06, 0x04, + 0x2b, 0x01, 0x07, 0x07, 0x07, 0x8f, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x70, 0x00, + 0x09, 0x00, 0x09, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x70, 0x00, 0x09, 0x00, 0x09), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + }, +}; + +static const struct b2056_inittabs_pts +*b43_nphy_get_inittabs_rev3(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + switch (dev->phy.rev) { + case 3: + return &b2056_inittab_phy_rev3; + case 4: + return &b2056_inittab_phy_rev4; + default: + switch (phy->radio_rev) { + case 5: + return &b2056_inittab_radio_rev5; + case 6: + return &b2056_inittab_radio_rev6; + case 7: + case 9: + return &b2056_inittab_radio_rev7_9; + case 8: + return &b2056_inittab_radio_rev8; + case 11: + return &b2056_inittab_radio_rev11; + } + } + + return NULL; +} + static void b2056_upload_inittab(struct b43_wldev *dev, bool ghz5, bool ignore_uploadflag, u16 routing, const struct b2056_inittab_entry *e, @@ -9037,11 +10230,11 @@ void b2056_upload_inittabs(struct b43_wldev *dev, { const struct b2056_inittabs_pts *pts; - if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) { + pts = b43_nphy_get_inittabs_rev3(dev); + if (!pts) { B43_WARN_ON(1); return; } - pts = &b2056_inittabs[dev->phy.rev]; b2056_upload_inittab(dev, ghz5, ignore_uploadflag, B2056_SYN, pts->syn, pts->syn_length); @@ -9060,11 +10253,12 @@ void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5) const struct b2056_inittabs_pts *pts; const struct b2056_inittab_entry *e; - if (dev->phy.rev >= ARRAY_SIZE(b2056_inittabs)) { + pts = b43_nphy_get_inittabs_rev3(dev); + if (!pts) { B43_WARN_ON(1); return; } - pts = &b2056_inittabs[dev->phy.rev]; + e = &pts->syn[B2056_SYN_PLL_CP2]; b43_radio_write(dev, B2056_SYN_PLL_CP2, ghz5 ? e->ghz5 : e->ghz2); @@ -9073,38 +10267,46 @@ void b2056_upload_syn_pll_cp2(struct b43_wldev *dev, bool ghz5) const struct b43_nphy_channeltab_entry_rev3 * b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq) { + struct b43_phy *phy = &dev->phy; const struct b43_nphy_channeltab_entry_rev3 *e; unsigned int length, i; - switch (dev->phy.rev) { + switch (phy->rev) { case 3: - e = b43_nphy_channeltab_rev3; - length = ARRAY_SIZE(b43_nphy_channeltab_rev3); + e = b43_nphy_channeltab_phy_rev3; + length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev3); break; case 4: - e = b43_nphy_channeltab_rev4; - length = ARRAY_SIZE(b43_nphy_channeltab_rev4); - break; - case 5: - e = b43_nphy_channeltab_rev5; - length = ARRAY_SIZE(b43_nphy_channeltab_rev5); - break; - case 6: - e = b43_nphy_channeltab_rev6; - length = ARRAY_SIZE(b43_nphy_channeltab_rev6); - break; - case 7: - case 9: - e = b43_nphy_channeltab_rev7_9; - length = ARRAY_SIZE(b43_nphy_channeltab_rev7_9); - break; - case 8: - e = b43_nphy_channeltab_rev8; - length = ARRAY_SIZE(b43_nphy_channeltab_rev8); + e = b43_nphy_channeltab_phy_rev4; + length = ARRAY_SIZE(b43_nphy_channeltab_phy_rev4); break; default: - B43_WARN_ON(1); - return NULL; + switch (phy->radio_rev) { + case 5: + e = b43_nphy_channeltab_radio_rev5; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev5); + break; + case 6: + e = b43_nphy_channeltab_radio_rev6; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev6); + break; + case 7: + case 9: + e = b43_nphy_channeltab_radio_rev7_9; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev7_9); + break; + case 8: + e = b43_nphy_channeltab_radio_rev8; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev8); + break; + case 11: + e = b43_nphy_channeltab_radio_rev11; + length = ARRAY_SIZE(b43_nphy_channeltab_radio_rev11); + break; + default: + B43_WARN_ON(1); + return NULL; + } } for (i = 0; i < length; i++, e++) { diff --git a/drivers/net/wireless/b43/sysfs.c b/drivers/net/wireless/b43/sysfs.c index 8e8431d4eb0..3190493bd07 100644 --- a/drivers/net/wireless/b43/sysfs.c +++ b/drivers/net/wireless/b43/sysfs.c @@ -40,7 +40,7 @@ static int get_integer(const char *buf, size_t count) if (count == 0) goto out; - count = min(count, (size_t) 10); + count = min_t(size_t, count, 10); memcpy(tmp, buf, count); ret = simple_strtol(tmp, NULL, 10); out: diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 94c755fdda1..4047c05e380 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -1627,74 +1627,7 @@ static const u32 b43_ntab_tdtrn_r3[] = { 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, }; -static const u32 b43_ntab_noisevar0_r3[] = { - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, - 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, -}; - -static const u32 b43_ntab_noisevar1_r3[] = { +static const u32 b43_ntab_noisevar_r3[] = { 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, @@ -3109,31 +3042,32 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) antswlut = sprom->fem.ghz2.antswlut; /* Static tables */ - ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); - ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); - ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); - ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); - ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); - ntab_upload(dev, B43_NTAB_NOISEVAR0_R3, b43_ntab_noisevar0_r3); - ntab_upload(dev, B43_NTAB_NOISEVAR1_R3, b43_ntab_noisevar1_r3); - ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); - ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); - ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); - ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); - ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); - ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); - ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); - ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); - ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); - ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); - ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); - ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); - ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); - ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); - ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); - ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); + ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload(dev, B43_NTAB_NOISEVAR_R3, b43_ntab_noisevar_r3); + ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); + ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); + ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); + ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); + ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); + ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); + ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); + ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); + } /* Volatile tables */ if (antswlut < ARRAY_SIZE(b43_ntab_antswctl_r3)) @@ -3146,20 +3080,22 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) static void b43_nphy_tables_init_rev0(struct b43_wldev *dev) { /* Static tables */ - ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); - ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); - ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); - ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); - ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); - ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); - ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); - ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); - ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); - ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); - ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); - ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); - ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); - ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); + ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); + ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); + ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); + ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); + ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); + ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); + ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); + ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); + ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); + ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); + ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); + ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); + ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + } /* Volatile tables */ ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 9ff33adcff8..3a58aee4c4c 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -143,8 +143,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( #define B43_NTAB_TMAP_R3 B43_NTAB32(12, 0) /* TM AP */ #define B43_NTAB_INTLEVEL_R3 B43_NTAB32(13, 0) /* INT LV */ #define B43_NTAB_TDTRN_R3 B43_NTAB32(14, 0) /* TD TRN */ -#define B43_NTAB_NOISEVAR0_R3 B43_NTAB32(16, 0) /* noise variance 0 */ -#define B43_NTAB_NOISEVAR1_R3 B43_NTAB32(16, 128) /* noise variance 1 */ +#define B43_NTAB_NOISEVAR_R3 B43_NTAB32(16, 0) /* noise variance */ #define B43_NTAB_MCS_R3 B43_NTAB16(18, 0) /* MCS */ #define B43_NTAB_TDI20A0_R3 B43_NTAB32(19, 128) /* TDI 20/0 */ #define B43_NTAB_TDI20A1_R3 B43_NTAB32(19, 256) /* TDI 20/1 */ diff --git a/drivers/net/wireless/b43/wa.c b/drivers/net/wireless/b43/wa.c index 9b1a038be08..c218c08fb2f 100644 --- a/drivers/net/wireless/b43/wa.c +++ b/drivers/net/wireless/b43/wa.c @@ -441,7 +441,7 @@ static void b43_wa_altagc(struct b43_wldev *dev) static void b43_wa_tr_ltov(struct b43_wldev *dev) /* TR Lookup Table Original Values */ { - b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0xC480); + b43_gtab_write(dev, B43_GTAB_ORIGTR, 0, 0x7654); } static void b43_wa_cpll_nonpilot(struct b43_wldev *dev) diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 8cb206a8908..6e6ef3fc224 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -278,7 +278,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, else txhdr->phy_rate = b43_plcp_get_ratecode_cck(rate); txhdr->mac_frame_ctl = wlhdr->frame_control; - memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); + memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); /* Calculate duration for fallback rate */ if ((rate_fb == rate) || @@ -337,7 +337,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, /* iv16 */ memcpy(txhdr->iv + 10, ((u8 *) wlhdr) + wlhdr_len, 3); } else { - iv_len = min((size_t) info->control.hw_key->iv_len, + iv_len = min_t(size_t, info->control.hw_key->iv_len, ARRAY_SIZE(txhdr->iv)); memcpy(txhdr->iv, ((u8 *) wlhdr) + wlhdr_len, iv_len); } @@ -408,7 +408,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, mac_ctl |= B43_TXH_MAC_HWSEQ; if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) mac_ctl |= B43_TXH_MAC_STMSDU; - if (phy->type == B43_PHYTYPE_A) + if (!phy->gmode) mac_ctl |= B43_TXH_MAC_5GHZ; /* Overwrite rates[0].count to make the retry calculation @@ -806,26 +806,30 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) B43_WARN_ON(1); /* FIXME: We don't really know which value the "chanid" contains. * So the following assignment might be wrong. */ - status.freq = b43_channel_to_freq_5ghz(chanid); + status.freq = + ieee80211_channel_to_frequency(chanid, status.band); break; case B43_PHYTYPE_G: status.band = IEEE80211_BAND_2GHZ; - /* chanid is the radio channel cookie value as used - * to tune the radio. */ - status.freq = chanid + 2400; + /* Somewhere between 478.104 and 508.1084 firmware for G-PHY + * has been modified to be compatible with N-PHY and others. + */ + if (dev->fw.rev >= 508) + status.freq = ieee80211_channel_to_frequency(chanid, status.band); + else + status.freq = chanid + 2400; break; case B43_PHYTYPE_N: case B43_PHYTYPE_LP: case B43_PHYTYPE_HT: /* chanid is the SHM channel cookie. Which is the plain * channel number in b43. */ - if (chanstat & B43_RX_CHAN_5GHZ) { + if (chanstat & B43_RX_CHAN_5GHZ) status.band = IEEE80211_BAND_5GHZ; - status.freq = b43_freq_to_channel_5ghz(chanid); - } else { + else status.band = IEEE80211_BAND_2GHZ; - status.freq = b43_freq_to_channel_2ghz(chanid); - } + status.freq = + ieee80211_channel_to_frequency(chanid, status.band); break; default: B43_WARN_ON(1); diff --git a/drivers/net/wireless/b43legacy/dma.c b/drivers/net/wireless/b43legacy/dma.c index 42eb26c99e1..b2ed1795130 100644 --- a/drivers/net/wireless/b43legacy/dma.c +++ b/drivers/net/wireless/b43legacy/dma.c @@ -806,12 +806,9 @@ static int b43legacy_dma_set_mask(struct b43legacy_wldev *dev, u64 mask) /* Try to set the DMA mask. If it fails, try falling back to a * lower mask, as we can always also support a lower one. */ while (1) { - err = dma_set_mask(dev->dev->dma_dev, mask); - if (!err) { - err = dma_set_coherent_mask(dev->dev->dma_dev, mask); - if (!err) - break; - } + err = dma_set_mask_and_coherent(dev->dev->dma_dev, mask); + if (!err) + break; if (mask == DMA_BIT_MASK(64)) { mask = DMA_BIT_MASK(32); fallback = true; diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 57266882186..1aec2146a2b 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -978,7 +978,7 @@ static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); - len = min((size_t)dev->wl->current_beacon->len, + len = min_t(size_t, dev->wl->current_beacon->len, 0x200 - sizeof(struct b43legacy_plcp_hdr6)); rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; @@ -1155,7 +1155,7 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev, b43legacy_write_probe_resp_plcp(dev, 0x350, size, &b43legacy_b_ratetable[3]); - size = min((size_t)size, + size = min_t(size_t, size, 0x200 - sizeof(struct b43legacy_plcp_hdr6)); b43legacy_write_template_common(dev, probe_resp_data, size, ram_offset, @@ -3919,6 +3919,7 @@ static void b43legacy_remove(struct ssb_device *dev) * as the ieee80211 unreg will destroy the workqueue. */ cancel_work_sync(&wldev->restart_work); cancel_work_sync(&wl->firmware_load); + complete(&wldev->fw_load_complete); B43legacy_WARN_ON(!wl); if (!wldev->fw.ucode) diff --git a/drivers/net/wireless/b43legacy/sysfs.c b/drivers/net/wireless/b43legacy/sysfs.c index 57f8b089767..2a1da15c913 100644 --- a/drivers/net/wireless/b43legacy/sysfs.c +++ b/drivers/net/wireless/b43legacy/sysfs.c @@ -42,7 +42,7 @@ static int get_integer(const char *buf, size_t count) if (count == 0) goto out; - count = min(count, (size_t)10); + count = min_t(size_t, count, 10); memcpy(tmp, buf, count); ret = simple_strtol(tmp, NULL, 10); out: diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index 849a28c8030..34bf3f0b729 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -215,7 +215,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); txhdr->mac_frame_ctl = wlhdr->frame_control; - memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); + memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); /* Calculate duration for fallback rate */ if ((rate_fb->hw_value == rate) || @@ -254,7 +254,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, B43legacy_TX4_MAC_KEYALG_SHIFT) & B43legacy_TX4_MAC_KEYALG; wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); - iv_len = min((size_t)info->control.hw_key->iv_len, + iv_len = min_t(size_t, info->control.hw_key->iv_len, ARRAY_SIZE(txhdr->iv)); memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); } else { diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index b00a7e92225..fcfed6b99a6 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig @@ -4,11 +4,12 @@ config BRCMUTIL config BRCMSMAC tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver" depends on MAC80211 - depends on BCMA + depends on BCMA_POSSIBLE + select BCMA + select NEW_LEDS if BCMA_DRIVER_GPIO + select LEDS_CLASS if BCMA_DRIVER_GPIO select BRCMUTIL select FW_LOADER - select CRC_CCITT - select CRC8 select CORDIC ---help--- This module adds support for PCIe wireless adapters based on Broadcom diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 8e9b1221b32..98e67c18f27 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -24,19 +24,20 @@ ccflags-y += -D__CHECK_ENDIAN__ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ wl_cfg80211.o \ + chip.o \ fwil.o \ fweh.o \ fwsignal.o \ p2p.o \ - dhd_cdc.o \ + proto.o \ + bcdc.o \ dhd_common.o \ dhd_linux.o \ + firmware.o \ btcoex.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ - bcmsdh.o \ - bcmsdh_sdmmc.o \ - sdio_chip.o + bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o brcmfmac-$(CONFIG_BRCMDBG) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c new file mode 100644 index 00000000000..c229210d50b --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * 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. + */ + +/******************************************************************************* + * Communicates with the dongle by using dcmd codes. + * For certain dcmd codes, the dongle interprets string data from the host. + ******************************************************************************/ + +#include <linux/types.h> +#include <linux/netdevice.h> + +#include <brcmu_utils.h> +#include <brcmu_wifi.h> + +#include "dhd.h" +#include "dhd_bus.h" +#include "fwsignal.h" +#include "dhd_dbg.h" +#include "tracepoint.h" +#include "proto.h" +#include "bcdc.h" + +struct brcmf_proto_bcdc_dcmd { + __le32 cmd; /* dongle command value */ + __le32 len; /* lower 16: output buflen; + * upper 16: input buflen (excludes header) */ + __le32 flags; /* flag defns given below */ + __le32 status; /* status code returned from the device */ +}; + +/* BCDC flag definitions */ +#define BCDC_DCMD_ERROR 0x01 /* 1=cmd failed */ +#define BCDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ +#define BCDC_DCMD_IF_MASK 0xF000 /* I/F index */ +#define BCDC_DCMD_IF_SHIFT 12 +#define BCDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ +#define BCDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ +#define BCDC_DCMD_ID(flags) \ + (((flags) & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT) + +/* + * BCDC header - Broadcom specific extension of CDC. + * Used on data packets to convey priority across USB. + */ +#define BCDC_HEADER_LEN 4 +#define BCDC_PROTO_VER 2 /* Protocol version */ +#define BCDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ +#define BCDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ +#define BCDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ +#define BCDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ +#define BCDC_PRIORITY_MASK 0x7 +#define BCDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ +#define BCDC_FLAG2_IF_SHIFT 0 + +#define BCDC_GET_IF_IDX(hdr) \ + ((int)((((hdr)->flags2) & BCDC_FLAG2_IF_MASK) >> BCDC_FLAG2_IF_SHIFT)) +#define BCDC_SET_IF_IDX(hdr, idx) \ + ((hdr)->flags2 = (((hdr)->flags2 & ~BCDC_FLAG2_IF_MASK) | \ + ((idx) << BCDC_FLAG2_IF_SHIFT))) + +/** + * struct brcmf_proto_bcdc_header - BCDC header format + * + * @flags: flags contain protocol and checksum info. + * @priority: 802.1d priority and USB flow control info (bit 4:7). + * @flags2: additional flags containing dongle interface index. + * @data_offset: start of packet data. header is following by firmware signals. + */ +struct brcmf_proto_bcdc_header { + u8 flags; + u8 priority; + u8 flags2; + u8 data_offset; +}; + +/* + * maximum length of firmware signal data between + * the BCDC header and packet data in the tx path. + */ +#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12 + +#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ +#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE + * (amount of header tha might be added) + * plus any space that might be needed + * for bus alignment padding. + */ +struct brcmf_bcdc { + u16 reqid; + u8 bus_header[BUS_HEADER_LEN]; + struct brcmf_proto_bcdc_dcmd msg; + unsigned char buf[BRCMF_DCMD_MAXLEN]; +}; + + +static int +brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, + uint len, bool set) +{ + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + u32 flags; + + brcmf_dbg(BCDC, "Enter\n"); + + memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd)); + + msg->cmd = cpu_to_le32(cmd); + msg->len = cpu_to_le32(len); + flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT); + if (set) + flags |= BCDC_DCMD_SET; + flags = (flags & ~BCDC_DCMD_IF_MASK) | + (ifidx << BCDC_DCMD_IF_SHIFT); + msg->flags = cpu_to_le32(flags); + + if (buf) + memcpy(bcdc->buf, buf, len); + + len += sizeof(*msg); + if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE) + len = BRCMF_TX_IOCTL_MAX_MSG_SIZE; + + /* Send request */ + return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len); +} + +static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) +{ + int ret; + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + + brcmf_dbg(BCDC, "Enter\n"); + len += sizeof(struct brcmf_proto_bcdc_dcmd); + do { + ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&bcdc->msg, + len); + if (ret < 0) + break; + } while (BCDC_DCMD_ID(le32_to_cpu(bcdc->msg.flags)) != id); + + return ret; +} + +static int +brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len) +{ + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + void *info; + int ret = 0, retries = 0; + u32 id, flags; + + brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); + + ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false); + if (ret < 0) { + brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n", + ret); + goto done; + } + +retry: + /* wait for interrupt and get first fragment */ + ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len); + if (ret < 0) + goto done; + + flags = le32_to_cpu(msg->flags); + id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT; + + if ((id < bcdc->reqid) && (++retries < RETRIES)) + goto retry; + if (id != bcdc->reqid) { + brcmf_err("%s: unexpected request id %d (expected %d)\n", + brcmf_ifname(drvr, ifidx), id, bcdc->reqid); + ret = -EINVAL; + goto done; + } + + /* Check info buffer */ + info = (void *)&msg[1]; + + /* Copy info buffer */ + if (buf) { + if (ret < (int)len) + len = ret; + memcpy(buf, info, len); + } + + /* Check the ERROR flag */ + if (flags & BCDC_DCMD_ERROR) + ret = le32_to_cpu(msg->status); + +done: + return ret; +} + +static int +brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len) +{ + struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + int ret = 0; + u32 flags, id; + + brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); + + ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true); + if (ret < 0) + goto done; + + ret = brcmf_proto_bcdc_cmplt(drvr, bcdc->reqid, len); + if (ret < 0) + goto done; + + flags = le32_to_cpu(msg->flags); + id = (flags & BCDC_DCMD_ID_MASK) >> BCDC_DCMD_ID_SHIFT; + + if (id != bcdc->reqid) { + brcmf_err("%s: unexpected request id %d (expected %d)\n", + brcmf_ifname(drvr, ifidx), id, bcdc->reqid); + ret = -EINVAL; + goto done; + } + + /* Check the ERROR flag */ + if (flags & BCDC_DCMD_ERROR) + ret = le32_to_cpu(msg->status); + +done: + return ret; +} + +static void +brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *pktbuf) +{ + struct brcmf_proto_bcdc_header *h; + + brcmf_dbg(BCDC, "Enter\n"); + + /* Push BDC header used to convey priority for buses that don't */ + skb_push(pktbuf, BCDC_HEADER_LEN); + + h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); + + h->flags = (BCDC_PROTO_VER << BCDC_FLAG_VER_SHIFT); + if (pktbuf->ip_summed == CHECKSUM_PARTIAL) + h->flags |= BCDC_FLAG_SUM_NEEDED; + + h->priority = (pktbuf->priority & BCDC_PRIORITY_MASK); + h->flags2 = 0; + h->data_offset = offset; + BCDC_SET_IF_IDX(h, ifidx); + trace_brcmf_bcdchdr(pktbuf->data); +} + +static int +brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, + struct sk_buff *pktbuf) +{ + struct brcmf_proto_bcdc_header *h; + + brcmf_dbg(BCDC, "Enter\n"); + + /* Pop BCDC header used to convey priority for buses that don't */ + if (pktbuf->len <= BCDC_HEADER_LEN) { + brcmf_dbg(INFO, "rx data too short (%d <= %d)\n", + pktbuf->len, BCDC_HEADER_LEN); + return -EBADE; + } + + trace_brcmf_bcdchdr(pktbuf->data); + h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); + + *ifidx = BCDC_GET_IF_IDX(h); + if (*ifidx >= BRCMF_MAX_IFS) { + brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); + return -EBADE; + } + /* The ifidx is the idx to map to matching netdev/ifp. When receiving + * events this is easy because it contains the bssidx which maps + * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. + * bssidx 1 is used for p2p0 and no data can be received or + * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 + */ + if (*ifidx) + (*ifidx)++; + + if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != + BCDC_PROTO_VER) { + brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", + brcmf_ifname(drvr, *ifidx), h->flags); + return -EBADE; + } + + if (h->flags & BCDC_FLAG_SUM_GOOD) { + brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", + brcmf_ifname(drvr, *ifidx), h->flags); + pktbuf->ip_summed = CHECKSUM_UNNECESSARY; + } + + pktbuf->priority = h->priority & BCDC_PRIORITY_MASK; + + skb_pull(pktbuf, BCDC_HEADER_LEN); + if (do_fws) + brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf); + else + skb_pull(pktbuf, h->data_offset << 2); + + if (pktbuf->len == 0) + return -ENODATA; + return 0; +} + +static int +brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *pktbuf) +{ + brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf); + return brcmf_bus_txdata(drvr->bus_if, pktbuf); +} + + +int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) +{ + struct brcmf_bcdc *bcdc; + + bcdc = kzalloc(sizeof(*bcdc), GFP_ATOMIC); + if (!bcdc) + goto fail; + + /* ensure that the msg buf directly follows the cdc msg struct */ + if ((unsigned long)(&bcdc->msg + 1) != (unsigned long)bcdc->buf) { + brcmf_err("struct brcmf_proto_bcdc is not correctly defined\n"); + goto fail; + } + + drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull; + drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; + drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; + drvr->proto->txdata = brcmf_proto_bcdc_txdata; + drvr->proto->pd = bcdc; + + drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; + drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + + sizeof(struct brcmf_proto_bcdc_dcmd); + return 0; + +fail: + kfree(bcdc); + return -ENOMEM; +} + +void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) +{ + kfree(drvr->proto->pd); + drvr->proto->pd = NULL; +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h new file mode 100644 index 00000000000..17e8c039ff3 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * 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 BRCMFMAC_BCDC_H +#define BRCMFMAC_BCDC_H + + +int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); +void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); + + +#endif /* BRCMFMAC_BCDC_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e13b1a65c65..a16e644e7c0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -17,17 +17,23 @@ #include <linux/types.h> #include <linux/netdevice.h> -#include <linux/export.h> #include <linux/pci.h> #include <linux/pci_ids.h> #include <linux/sched.h> #include <linux/completion.h> #include <linux/scatterlist.h> #include <linux/mmc/sdio.h> +#include <linux/mmc/core.h> #include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> +#include <linux/platform_device.h> #include <linux/platform_data/brcmfmac-sdio.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <net/cfg80211.h> #include <defs.h> #include <brcm_hw_ids.h> @@ -40,8 +46,21 @@ #define SDIOH_API_ACCESS_RETRY_LIMIT 2 +#define DMA_ALIGN_MASK 0x03 -static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id) +#define SDIO_FUNC1_BLOCKSIZE 64 +#define SDIO_FUNC2_BLOCKSIZE 512 +/* Maximum milliseconds to wait for F2 to come up */ +#define SDIO_WAIT_F2RDY 3000 + +#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ +#define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ + +static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; +module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); +MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); + +static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) { struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; @@ -56,27 +75,46 @@ static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id) sdiodev->irq_en = false; } - brcmf_sdbrcm_isr(sdiodev->bus); + brcmf_sdio_isr(sdiodev->bus); return IRQ_HANDLED; } -static void brcmf_sdio_ib_irqhandler(struct sdio_func *func) +static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func) { struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(INTR, "IB intr triggered\n"); - brcmf_sdbrcm_isr(sdiodev->bus); + brcmf_sdio_isr(sdiodev->bus); } /* dummy handler for SDIO function 2 interrupt */ -static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func) +static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) +{ +} + +static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev) +{ + bool is_err = false; +#ifdef CONFIG_PM_SLEEP + is_err = atomic_read(&sdiodev->suspend); +#endif + return is_err; +} + +static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, + wait_queue_head_t *wq) { +#ifdef CONFIG_PM_SLEEP + int retry = 0; + while (atomic_read(&sdiodev->suspend) && retry++ != 30) + wait_event_timeout(*wq, false, HZ/100); +#endif } -int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; u8 data; @@ -86,7 +124,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n", sdiodev->pdata->oob_irq_nr); ret = request_irq(sdiodev->pdata->oob_irq_nr, - brcmf_sdio_oob_irqhandler, + brcmf_sdiod_oob_irqhandler, sdiodev->pdata->oob_irq_flags, "brcmf_oob_intr", &sdiodev->func[1]->dev); @@ -110,36 +148,36 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) sdio_claim_host(sdiodev->func[1]); /* must configure SDIO_CCCR_IENx to enable irq */ - data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret); + data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret); data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); /* redirect, configure and enable io for interrupt signal */ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) data |= SDIO_SEPINT_ACT_HI; - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); sdio_release_host(sdiodev->func[1]); } else { brcmf_dbg(SDIO, "Entering\n"); sdio_claim_host(sdiodev->func[1]); - sdio_claim_irq(sdiodev->func[1], brcmf_sdio_ib_irqhandler); - sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler); + sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler); + sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler); sdio_release_host(sdiodev->func[1]); } return 0; } -int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(SDIO, "Entering\n"); if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { sdio_claim_host(sdiodev->func[1]); - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); sdio_release_host(sdiodev->func[1]); if (sdiodev->oob_irq_requested) { @@ -162,29 +200,151 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) return 0; } +static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func, + uint regaddr, u8 byte) +{ + int err_ret; + + /* + * Can only directly write to some F0 registers. + * Handle CCCR_IENx and CCCR_ABORT command + * as a special case. + */ + if ((regaddr == SDIO_CCCR_ABORT) || + (regaddr == SDIO_CCCR_IENx)) + sdio_writeb(func, byte, regaddr, &err_ret); + else + sdio_f0_writeb(func, byte, regaddr, &err_ret); + + return err_ret; +} + +static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, + u32 addr, u8 regsz, void *data, bool write) +{ + struct sdio_func *func; + int ret; + + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + write, fn, addr, regsz); + + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) + return -EIO; + + /* only allow byte access on F0 */ + if (WARN_ON(regsz > 1 && !fn)) + return -EINVAL; + func = sdiodev->func[fn]; + + switch (regsz) { + case sizeof(u8): + if (write) { + if (fn) + sdio_writeb(func, *(u8 *)data, addr, &ret); + else + ret = brcmf_sdiod_f0_writeb(func, addr, + *(u8 *)data); + } else { + if (fn) + *(u8 *)data = sdio_readb(func, addr, &ret); + else + *(u8 *)data = sdio_f0_readb(func, addr, &ret); + } + break; + case sizeof(u16): + if (write) + sdio_writew(func, *(u16 *)data, addr, &ret); + else + *(u16 *)data = sdio_readw(func, addr, &ret); + break; + case sizeof(u32): + if (write) + sdio_writel(func, *(u32 *)data, addr, &ret); + else + *(u32 *)data = sdio_readl(func, addr, &ret); + break; + default: + brcmf_err("invalid size: %d\n", regsz); + break; + } + + 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; + s32 retry = 0; + int ret; + + if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + return -ENOMEDIUM; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~REG_F0_REG_MASK) == 0) + func = SDIO_FUNC_0; + else + func = SDIO_FUNC_1; + + do { + if (!write) + memset(data, 0, regsz); + /* for retry wait for 1 ms till bus get settled down */ + if (retry) + usleep_range(1000, 2000); + 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) { + /* + * 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; +} + static int -brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) +brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) { int err = 0, i; u8 addr[3]; - s32 retry; + + if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + return -ENOMEDIUM; addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK; addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK; for (i = 0; i < 3; i++) { - retry = 0; - do { - if (retry) - usleep_range(1000, 2000); - err = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, - SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW + i, - &addr[i]); - } while (err != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); - + err = brcmf_sdiod_regrw_helper(sdiodev, + SBSDIO_FUNC1_SBADDRLOW + i, + sizeof(u8), &addr[i], true); if (err) { - brcmf_err("failed at addr:0x%0x\n", + brcmf_err("failed at addr: 0x%0x\n", SBSDIO_FUNC1_SBADDRLOW + i); break; } @@ -194,13 +354,13 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) } static int -brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) +brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) { uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; if (bar0 != sdiodev->sbwad) { - err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); + err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0); if (err) return err; @@ -215,59 +375,14 @@ brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) return 0; } -int -brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, - void *data, bool write) -{ - u8 func_num, reg_size; - s32 retry = 0; - int ret; - - /* - * figure out how to read the register based on address range - * 0x00 ~ 0x7FF: function 0 CCCR and FBR - * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers - * The rest: function 1 silicon backplane core registers - */ - if ((addr & ~REG_F0_REG_MASK) == 0) { - func_num = SDIO_FUNC_0; - reg_size = 1; - } else if ((addr & ~REG_F1_MISC_MASK) == 0) { - func_num = SDIO_FUNC_1; - reg_size = 1; - } else { - func_num = SDIO_FUNC_1; - reg_size = 4; - - brcmf_sdio_addrprep(sdiodev, reg_size, &addr); - } - - do { - if (!write) - memset(data, 0, reg_size); - if (retry) /* wait for 1 ms till bus get settled down */ - usleep_range(1000, 2000); - if (reg_size == 1) - ret = brcmf_sdioh_request_byte(sdiodev, write, - func_num, addr, data); - else - ret = brcmf_sdioh_request_word(sdiodev, write, - func_num, addr, data, 4); - } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); - - if (ret != 0) - brcmf_err("failed with %d\n", ret); - - return ret; -} - -u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) { u8 data; int retval; brcmf_dbg(SDIO, "addr:0x%08x\n", addr); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + false); brcmf_dbg(SDIO, "data:0x%02x\n", data); if (ret) @@ -276,47 +391,86 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) return data; } -u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) { u32 data; int retval; brcmf_dbg(SDIO, "addr:0x%08x\n", addr); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); + retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); + if (retval) + goto done; + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + false); brcmf_dbg(SDIO, "data:0x%08x\n", data); +done: if (ret) *ret = retval; return data; } -void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, int *ret) { int retval; brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); - + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + true); if (ret) *ret = retval; } -void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, int *ret) { int retval; brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); + retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); + if (retval) + goto done; + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + true); +done: if (ret) *ret = retval; } +static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, struct sk_buff *pkt) +{ + unsigned int req_sz; + int err; + + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) + return -EIO; + + /* Single skb use the standard mmc interface */ + req_sz = pkt->len + 3; + req_sz &= (uint)~3; + + if (write) + err = sdio_memcpy_toio(sdiodev->func[fn], addr, + ((u8 *)(pkt->data)), req_sz); + else if (fn == 1) + err = sdio_memcpy_fromio(sdiodev->func[fn], ((u8 *)(pkt->data)), + addr, req_sz); + else + /* function 2 read is FIFO operation */ + err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, + req_sz); + if (err == -ENOMEDIUM) + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + return err; +} + /** - * brcmf_sdio_buffrw - SDIO interface function for block data access + * brcmf_sdiod_sglist_rw - SDIO interface function for block data access * @sdiodev: brcmfmac sdio device * @fn: SDIO function number * @write: direction flag @@ -327,51 +481,29 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, * stack for block data access. It assumes that the skb passed down by the * caller has already been padded and aligned. */ -static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, - bool write, u32 addr, struct sk_buff_head *pktlist) +static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, + struct sk_buff_head *pktlist) { unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; - unsigned int max_blks, max_req_sz, orig_offset, dst_offset; - unsigned short max_seg_sz, seg_sz; + unsigned int max_req_sz, orig_offset, dst_offset; + unsigned short max_seg_cnt, seg_sz; unsigned char *pkt_data, *orig_data, *dst_data; struct sk_buff *pkt_next = NULL, *local_pkt_next; struct sk_buff_head local_list, *target_list; struct mmc_request mmc_req; struct mmc_command mmc_cmd; struct mmc_data mmc_dat; - struct sg_table st; struct scatterlist *sgl; - struct mmc_host *host; int ret = 0; if (!pktlist->qlen) return -EINVAL; - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_pm_resume_error(sdiodev)) + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) return -EIO; - /* Single skb use the standard mmc interface */ - if (pktlist->qlen == 1) { - pkt_next = pktlist->next; - req_sz = pkt_next->len + 3; - req_sz &= (uint)~3; - - if (write) - return sdio_memcpy_toio(sdiodev->func[fn], addr, - ((u8 *)(pkt_next->data)), - req_sz); - else if (fn == 1) - return sdio_memcpy_fromio(sdiodev->func[fn], - ((u8 *)(pkt_next->data)), - addr, req_sz); - else - /* function 2 read is FIFO operation */ - return sdio_readsb(sdiodev->func[fn], - ((u8 *)(pkt_next->data)), addr, - req_sz); - } - target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); @@ -398,38 +530,41 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, target_list = &local_list; } - host = sdiodev->func[fn]->card->host; func_blk_sz = sdiodev->func[fn]->cur_blksize; - /* Blocks per command is limited by host count, host transfer - * size and the maximum for IO_RW_EXTENDED of 511 blocks. - */ - max_blks = min_t(unsigned int, host->max_blk_count, 511u); - max_req_sz = min_t(unsigned int, host->max_req_size, - max_blks * func_blk_sz); - max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC); - max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen); + max_req_sz = sdiodev->max_request_size; + max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, + target_list->qlen); seg_sz = target_list->qlen; pkt_offset = 0; pkt_next = target_list->next; - if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) { - ret = -ENOMEM; - goto exit; - } + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = sdiodev->sgtable.sgl; + mmc_dat.blksz = func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1<<27; /* block mode */ + /* for function 1 the addr will be incremented */ + mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; while (seg_sz) { req_sz = 0; sg_cnt = 0; - memset(&mmc_req, 0, sizeof(struct mmc_request)); - memset(&mmc_cmd, 0, sizeof(struct mmc_command)); - memset(&mmc_dat, 0, sizeof(struct mmc_data)); - sgl = st.sgl; + sgl = sdiodev->sgtable.sgl; /* prep sg table */ while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; sg_data_sz = pkt_next->len - pkt_offset; - if (sg_data_sz > host->max_seg_size) - sg_data_sz = host->max_seg_size; + if (sg_data_sz > sdiodev->max_segment_size) + sg_data_sz = sdiodev->max_segment_size; if (sg_data_sz > max_req_sz - req_sz) sg_data_sz = max_req_sz - req_sz; @@ -444,7 +579,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, pkt_next = pkt_next->next; } - if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz) + if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) break; } seg_sz -= sg_cnt; @@ -455,30 +590,24 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, ret = -ENOTBLK; goto exit; } - mmc_dat.sg = st.sgl; + mmc_dat.sg_len = sg_cnt; - mmc_dat.blksz = func_blk_sz; mmc_dat.blocks = req_sz / func_blk_sz; - mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; - mmc_cmd.opcode = SD_IO_RW_EXTENDED; - mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ - mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ - mmc_cmd.arg |= 1<<27; /* block mode */ - /* incrementing addr for function 1 */ - mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ - mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; - mmc_req.cmd = &mmc_cmd; - mmc_req.data = &mmc_dat; + /* incrementing addr for function 1 */ if (fn == 1) addr += req_sz; mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card); - mmc_wait_for_req(host, &mmc_req); + mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req); ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; - if (ret != 0) { + if (ret == -ENOMEDIUM) { + brcmf_bus_change_state(sdiodev->bus_if, + BRCMF_BUS_NOMEDIUM); + break; + } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", write ? "write" : "read", ret); ret = -EIO; @@ -511,16 +640,14 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, } exit: - sg_free_table(&st); + sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents); while ((pkt_next = __skb_dequeue(&local_list)) != NULL) brcmu_pkt_buf_free_skb(pkt_next); return ret; } -int -brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes) +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) { struct sk_buff *mypkt; int err; @@ -532,7 +659,7 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, return -EIO; } - err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt); + err = brcmf_sdiod_recv_pkt(sdiodev, mypkt); if (!err) memcpy(buf, mypkt->data, nbytes); @@ -540,59 +667,66 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, return err; } -int -brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt) +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt) { - uint width; + u32 addr = sdiodev->sbwad; int err = 0; - struct sk_buff_head pkt_list; - brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pkt->len); + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); if (err) goto done; - skb_queue_head_init(&pkt_list); - skb_queue_tail(&pkt_list, pkt); - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list); - skb_dequeue_tail(&pkt_list); + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt); done: return err; } -int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq) +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen) { - uint incr_fix; - uint width; + struct sk_buff *glom_skb; + struct sk_buff *skb; + u32 addr = sdiodev->sbwad; int err = 0; - brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pktq->qlen); + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", + addr, pktq->qlen); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); if (err) goto done; - incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq); + if (pktq->qlen == 1) + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, + pktq->next); + else if (!sdiodev->sg_support) { + glom_skb = brcmu_pkt_buf_get_skb(totlen); + if (!glom_skb) + return -ENOMEM; + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, + glom_skb); + if (err) + goto done; + + skb_queue_walk(pktq, skb) { + memcpy(skb->data, glom_skb->data, skb->len); + skb_pull(glom_skb, skb->len); + } + } else + err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr, + pktq); done: return err; } -int -brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes) +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) { struct sk_buff *mypkt; - struct sk_buff_head pktq; + u32 addr = sdiodev->sbwad; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -603,43 +737,53 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, } memcpy(mypkt->data, buf, nbytes); - __skb_queue_head_init(&pktq); - __skb_queue_tail(&pktq, mypkt); - err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq); - __skb_dequeue_tail(&pktq); + + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); + + if (!err) + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr, + mypkt); brcmu_pkt_buf_free_skb(mypkt); return err; } -int -brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq) +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq) { - uint width; - int err = 0; + struct sk_buff *skb; + u32 addr = sdiodev->sbwad; + int err; - brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pktq->qlen); + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); + if (err) + return err; - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq); + if (pktq->qlen == 1 || !sdiodev->sg_support) + skb_queue_walk(pktq, skb) { + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, + addr, skb); + if (err) + break; + } + else + err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr, + pktq); return err; } int -brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, - u8 *data, uint size) +brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size) { int bcmerror = 0; struct sk_buff *pkt; u32 sdaddr; uint dsize; - struct sk_buff_head pkt_list; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); pkt = dev_alloc_skb(dsize); @@ -648,7 +792,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, return -EIO; } pkt->priority = 0; - skb_queue_head_init(&pkt_list); /* Determine initial transfer parameters */ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; @@ -662,7 +805,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, /* Do the transfer(s) */ while (size) { /* Set the backplane window to include the start address */ - bcmerror = brcmf_sdcard_set_sbaddr_window(sdiodev, address); + bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address); if (bcmerror) break; @@ -676,17 +819,15 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, skb_put(pkt, dsize); if (write) memcpy(pkt->data, data, dsize); - skb_queue_tail(&pkt_list, pkt); - bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write, - sdaddr, &pkt_list); - skb_dequeue_tail(&pkt_list); + bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write, + sdaddr, pkt); if (bcmerror) { brcmf_err("membytes transfer failed\n"); break; } if (!write) memcpy(data, pkt->data, dsize); - skb_trim(pkt, dsize); + skb_trim(pkt, 0); /* Adjust for next transfer (if any) */ size -= dsize; @@ -701,7 +842,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, dev_kfree_skb(pkt); /* Return the window to backplane enumeration space for core access */ - if (brcmf_sdcard_set_sbaddr_window(sdiodev, sdiodev->sbwad)) + if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad)) brcmf_err("FAILED to set window back to 0x%x\n", sdiodev->sbwad); @@ -710,67 +851,368 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, return bcmerror; } -int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) { char t_func = (char)fn; brcmf_dbg(SDIO, "Enter\n"); /* issue abort cmd52 command through F0 */ - brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, - SDIO_CCCR_ABORT, &t_func); + brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT, + sizeof(t_func), &t_func, true); brcmf_dbg(SDIO, "Exit\n"); return 0; } -int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) +static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) +{ + uint nents; + int err; + + if (!sdiodev->sg_support) + return; + + nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); + nents += (nents >> 4) + 1; + + WARN_ON(nents > sdiodev->max_segment_count); + + brcmf_dbg(TRACE, "nents=%d\n", nents); + err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL); + if (err < 0) { + brcmf_err("allocation failed: disable scatter-gather"); + sdiodev->sg_support = false; + } + + sdiodev->txglomsz = brcmf_sdiod_txglomsz; +} + +static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) { - u32 regs = 0; + if (sdiodev->bus) { + brcmf_sdio_remove(sdiodev->bus); + sdiodev->bus = NULL; + } + + /* Disable Function 2 */ + sdio_claim_host(sdiodev->func[2]); + sdio_disable_func(sdiodev->func[2]); + sdio_release_host(sdiodev->func[2]); + + /* Disable Function 1 */ + sdio_claim_host(sdiodev->func[1]); + sdio_disable_func(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + + sg_free_table(&sdiodev->sgtable); + sdiodev->sbwad = 0; + + return 0; +} + +static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) +{ + struct sdio_func *func; + struct mmc_host *host; + uint max_blocks; int ret = 0; - ret = brcmf_sdioh_attach(sdiodev); - if (ret) + sdiodev->num_funcs = 2; + + sdio_claim_host(sdiodev->func[1]); + + ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); + if (ret) { + brcmf_err("Failed to set F1 blocksize\n"); + sdio_release_host(sdiodev->func[1]); + goto out; + } + ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); + if (ret) { + brcmf_err("Failed to set F2 blocksize\n"); + sdio_release_host(sdiodev->func[1]); + goto out; + } + + /* increase F2 timeout */ + sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY; + + /* Enable Function 1 */ + ret = sdio_enable_func(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + if (ret) { + brcmf_err("Failed to enable F1: err=%d\n", ret); goto out; + } - regs = SI_ENUM_BASE; + /* + * determine host related variables after brcmf_sdiod_probe() + * as func->cur_blksize is properly set and F2 init has been + * completed successfully. + */ + func = sdiodev->func[2]; + host = func->card->host; + sdiodev->sg_support = host->max_segs > 1; + max_blocks = min_t(uint, host->max_blk_count, 511u); + sdiodev->max_request_size = min_t(uint, host->max_req_size, + max_blocks * func->cur_blksize); + sdiodev->max_segment_count = min_t(uint, host->max_segs, + SG_MAX_SINGLE_ALLOC); + sdiodev->max_segment_size = host->max_seg_size; + + /* allocate scatter-gather table. sg support + * will be disabled upon allocation failure. + */ + brcmf_sdiod_sgtable_alloc(sdiodev); /* try to attach to the target device */ - sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev); + sdiodev->bus = brcmf_sdio_probe(sdiodev); if (!sdiodev->bus) { - brcmf_err("device attach failed\n"); ret = -ENODEV; goto out; } out: if (ret) - brcmf_sdio_remove(sdiodev); + brcmf_sdiod_remove(sdiodev); return ret; } -EXPORT_SYMBOL(brcmf_sdio_probe); -int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) +/* devices we support, null terminated */ +static const struct sdio_device_id brcmf_sdmmc_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, + {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); + +static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; + + +static int brcmf_ops_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) { - sdiodev->bus_if->state = BRCMF_BUS_DOWN; + int err; + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bus *bus_if; - if (sdiodev->bus) { - brcmf_sdbrcm_disconnect(sdiodev->bus); - sdiodev->bus = NULL; + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "Class=%x\n", func->class); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function#: %d\n", func->num); + + /* Consume func num 1 but dont do anything with it. */ + if (func->num == 1) + return 0; + + /* Ignore anything but func 2 */ + if (func->num != 2) + return -ENODEV; + + bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); + if (!bus_if) + return -ENOMEM; + sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); + if (!sdiodev) { + kfree(bus_if); + return -ENOMEM; } - brcmf_sdioh_detach(sdiodev); + /* store refs to functions used. mmc_card does + * not hold the F0 function pointer. + */ + sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL); + sdiodev->func[0]->num = 0; + sdiodev->func[1] = func->card->sdio_func[0]; + sdiodev->func[2] = func; + + sdiodev->bus_if = bus_if; + bus_if->bus_priv.sdio = sdiodev; + bus_if->proto_type = BRCMF_PROTO_BCDC; + dev_set_drvdata(&func->dev, bus_if); + dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); + sdiodev->dev = &sdiodev->func[1]->dev; + sdiodev->pdata = brcmfmac_sdio_pdata; + + atomic_set(&sdiodev->suspend, false); + init_waitqueue_head(&sdiodev->request_word_wait); + init_waitqueue_head(&sdiodev->request_buffer_wait); + + brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); + err = brcmf_sdiod_probe(sdiodev); + if (err) { + brcmf_err("F2 error, probe failed %d...\n", err); + goto fail; + } - sdiodev->sbwad = 0; + brcmf_dbg(SDIO, "F2 init completed...\n"); + return 0; + +fail: + dev_set_drvdata(&func->dev, NULL); + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + kfree(sdiodev->func[0]); + kfree(sdiodev); + kfree(bus_if); + return err; +} + +static void brcmf_ops_sdio_remove(struct sdio_func *func) +{ + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; + + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function: %d\n", func->num); + if (func->num != 1 && func->num != 2) + return; + + bus_if = dev_get_drvdata(&func->dev); + if (bus_if) { + sdiodev = bus_if->bus_priv.sdio; + brcmf_sdiod_remove(sdiodev); + + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + dev_set_drvdata(&sdiodev->func[2]->dev, NULL); + + kfree(bus_if); + kfree(sdiodev->func[0]); + kfree(sdiodev); + } + + brcmf_dbg(SDIO, "Exit\n"); +} + +#ifdef CONFIG_PM_SLEEP +static int brcmf_ops_sdio_suspend(struct device *dev) +{ + mmc_pm_flag_t sdio_flags; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + int ret = 0; + + brcmf_dbg(SDIO, "Enter\n"); + + sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + brcmf_err("Host can't keep power while suspended\n"); + return -EINVAL; + } + + atomic_set(&sdiodev->suspend, true); + + ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); + if (ret) { + brcmf_err("Failed to set pm_flags\n"); + atomic_set(&sdiodev->suspend, false); + return ret; + } + + brcmf_sdio_wd_timer(sdiodev->bus, 0); + + return ret; +} + +static int brcmf_ops_sdio_resume(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_dbg(SDIO, "Enter\n"); + brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); + atomic_set(&sdiodev->suspend, false); return 0; } -EXPORT_SYMBOL(brcmf_sdio_remove); -void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable) +static const struct dev_pm_ops brcmf_sdio_pm_ops = { + .suspend = brcmf_ops_sdio_suspend, + .resume = brcmf_ops_sdio_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +static struct sdio_driver brcmf_sdmmc_driver = { + .probe = brcmf_ops_sdio_probe, + .remove = brcmf_ops_sdio_remove, + .name = BRCMFMAC_SDIO_PDATA_NAME, + .id_table = brcmf_sdmmc_ids, + .drv = { + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &brcmf_sdio_pm_ops, +#endif /* CONFIG_PM_SLEEP */ + }, +}; + +static int __init brcmf_sdio_pd_probe(struct platform_device *pdev) { - if (enable) - brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); + brcmf_dbg(SDIO, "Enter\n"); + + brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); + + if (brcmfmac_sdio_pdata->power_on) + brcmfmac_sdio_pdata->power_on(); + + return 0; +} + +static int brcmf_sdio_pd_remove(struct platform_device *pdev) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (brcmfmac_sdio_pdata->power_off) + brcmfmac_sdio_pdata->power_off(); + + sdio_unregister_driver(&brcmf_sdmmc_driver); + + return 0; +} + +static struct platform_driver brcmf_sdio_pd = { + .remove = brcmf_sdio_pd_remove, + .driver = { + .name = BRCMFMAC_SDIO_PDATA_NAME, + .owner = THIS_MODULE, + } +}; + +void brcmf_sdio_register(void) +{ + int ret; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); +} + +void brcmf_sdio_exit(void) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (brcmfmac_sdio_pdata) + platform_driver_unregister(&brcmf_sdio_pd); else - brcmf_sdbrcm_wd_timer(sdiodev->bus, 0); + sdio_unregister_driver(&brcmf_sdmmc_driver); +} + +void __init brcmf_sdio_init(void) +{ + int ret; + + brcmf_dbg(SDIO, "Enter\n"); + + ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); + if (ret == -ENODEV) + brcmf_dbg(SDIO, "No platform data available.\n"); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c deleted file mode 100644 index c3462b75bd0..00000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * 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/types.h> -#include <linux/netdevice.h> -#include <linux/mmc/sdio.h> -#include <linux/mmc/core.h> -#include <linux/mmc/sdio_func.h> -#include <linux/mmc/sdio_ids.h> -#include <linux/mmc/card.h> -#include <linux/suspend.h> -#include <linux/errno.h> -#include <linux/sched.h> /* request_irq() */ -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/platform_data/brcmfmac-sdio.h> -#include <net/cfg80211.h> - -#include <defs.h> -#include <brcm_hw_ids.h> -#include <brcmu_utils.h> -#include <brcmu_wifi.h> -#include "sdio_host.h" -#include "dhd_dbg.h" -#include "dhd_bus.h" - -#define SDIO_VENDOR_ID_BROADCOM 0x02d0 - -#define DMA_ALIGN_MASK 0x03 - -#define SDIO_DEVICE_ID_BROADCOM_43143 43143 -#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324 -#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 -#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 -#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 -#define SDIO_DEVICE_ID_BROADCOM_4335 0x4335 - -#define SDIO_FUNC1_BLOCKSIZE 64 -#define SDIO_FUNC2_BLOCKSIZE 512 - -/* devices we support, null terminated */ -static const struct sdio_device_id brcmf_sdmmc_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335)}, - { /* end: all zeroes */ }, -}; -MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); - -static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; - - -bool -brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) -{ - bool is_err = false; -#ifdef CONFIG_PM_SLEEP - is_err = atomic_read(&sdiodev->suspend); -#endif - return is_err; -} - -void -brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq) -{ -#ifdef CONFIG_PM_SLEEP - int retry = 0; - while (atomic_read(&sdiodev->suspend) && retry++ != 30) - wait_event_timeout(*wq, false, HZ/100); -#endif -} - -static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, - uint regaddr, u8 *byte) -{ - struct sdio_func *sdfunc = sdiodev->func[0]; - int err_ret; - - /* - * Can only directly write to some F0 registers. - * Handle F2 enable/disable and Abort command - * as a special case. - */ - if (regaddr == SDIO_CCCR_IOEx) { - sdfunc = sdiodev->func[2]; - if (sdfunc) { - if (*byte & SDIO_FUNC_ENABLE_2) { - /* Enable Function 2 */ - err_ret = sdio_enable_func(sdfunc); - if (err_ret) - brcmf_err("enable F2 failed:%d\n", - err_ret); - } else { - /* Disable Function 2 */ - err_ret = sdio_disable_func(sdfunc); - if (err_ret) - brcmf_err("Disable F2 failed:%d\n", - err_ret); - } - } - } else if ((regaddr == SDIO_CCCR_ABORT) || - (regaddr == SDIO_CCCR_IENx)) { - sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func), - GFP_KERNEL); - if (!sdfunc) - return -ENOMEM; - sdfunc->num = 0; - sdio_writeb(sdfunc, *byte, regaddr, &err_ret); - kfree(sdfunc); - } else if (regaddr < 0xF0) { - brcmf_err("F0 Wr:0x%02x: write disallowed\n", regaddr); - err_ret = -EPERM; - } else { - sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret); - } - - return err_ret; -} - -int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, - uint regaddr, u8 *byte) -{ - int err_ret; - - brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); - - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait); - if (brcmf_pm_resume_error(sdiodev)) - return -EIO; - - if (rw && func == 0) { - /* handle F0 separately */ - err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte); - } else { - if (rw) /* CMD52 Write */ - sdio_writeb(sdiodev->func[func], *byte, regaddr, - &err_ret); - else if (func == 0) { - *byte = sdio_f0_readb(sdiodev->func[func], regaddr, - &err_ret); - } else { - *byte = sdio_readb(sdiodev->func[func], regaddr, - &err_ret); - } - } - - if (err_ret) - brcmf_err("Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", - rw ? "write" : "read", func, regaddr, *byte, err_ret); - - return err_ret; -} - -int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, - uint rw, uint func, uint addr, u32 *word, - uint nbytes) -{ - int err_ret = -EIO; - - if (func == 0) { - brcmf_err("Only CMD52 allowed to F0\n"); - return -EINVAL; - } - - brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", - rw, func, addr, nbytes); - - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); - if (brcmf_pm_resume_error(sdiodev)) - return -EIO; - - if (rw) { /* CMD52 Write */ - if (nbytes == 4) - sdio_writel(sdiodev->func[func], *word, addr, - &err_ret); - else if (nbytes == 2) - sdio_writew(sdiodev->func[func], (*word & 0xFFFF), - addr, &err_ret); - else - brcmf_err("Invalid nbytes: %d\n", nbytes); - } else { /* CMD52 Read */ - if (nbytes == 4) - *word = sdio_readl(sdiodev->func[func], addr, &err_ret); - else if (nbytes == 2) - *word = sdio_readw(sdiodev->func[func], addr, - &err_ret) & 0xFFFF; - else - brcmf_err("Invalid nbytes: %d\n", nbytes); - } - - if (err_ret) - brcmf_err("Failed to %s word, Err: 0x%08x\n", - rw ? "write" : "read", err_ret); - - return err_ret; -} - -static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr) -{ - /* read 24 bits and return valid 17 bit addr */ - int i, ret; - u32 scratch, regdata; - __le32 scratch_le; - u8 *ptr = (u8 *)&scratch_le; - - for (i = 0; i < 3; i++) { - regdata = brcmf_sdio_regrl(sdiodev, regaddr, &ret); - if (ret != 0) - brcmf_err("Can't read!\n"); - - *ptr++ = (u8) regdata; - regaddr++; - } - - /* Only the lower 17-bits are valid */ - scratch = le32_to_cpu(scratch_le); - scratch &= 0x0001FFFF; - return scratch; -} - -static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) -{ - int err_ret; - u32 fbraddr; - u8 func; - - brcmf_dbg(SDIO, "\n"); - - /* Get the Card's common CIS address */ - sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev, - SDIO_CCCR_CIS); - brcmf_dbg(SDIO, "Card's Common CIS Ptr = 0x%x\n", - sdiodev->func_cis_ptr[0]); - - /* Get the Card's function CIS (for each function) */ - for (fbraddr = SDIO_FBR_BASE(1), func = 1; - func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { - sdiodev->func_cis_ptr[func] = - brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr); - brcmf_dbg(SDIO, "Function %d CIS Ptr = 0x%x\n", - func, sdiodev->func_cis_ptr[func]); - } - - /* Enable Function 1 */ - err_ret = sdio_enable_func(sdiodev->func[1]); - if (err_ret) - brcmf_err("Failed to enable F1 Err: 0x%08x\n", err_ret); - - return false; -} - -/* - * Public entry points & extern's - */ -int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) -{ - int err_ret = 0; - - brcmf_dbg(SDIO, "\n"); - - sdiodev->num_funcs = 2; - - sdio_claim_host(sdiodev->func[1]); - - err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); - if (err_ret) { - brcmf_err("Failed to set F1 blocksize\n"); - goto out; - } - - err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); - if (err_ret) { - brcmf_err("Failed to set F2 blocksize\n"); - goto out; - } - - brcmf_sdioh_enablefuncs(sdiodev); - -out: - sdio_release_host(sdiodev->func[1]); - brcmf_dbg(SDIO, "Done\n"); - return err_ret; -} - -void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev) -{ - brcmf_dbg(SDIO, "\n"); - - /* Disable Function 2 */ - sdio_claim_host(sdiodev->func[2]); - sdio_disable_func(sdiodev->func[2]); - sdio_release_host(sdiodev->func[2]); - - /* Disable Function 1 */ - sdio_claim_host(sdiodev->func[1]); - sdio_disable_func(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); - -} - -static int brcmf_ops_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - int err; - struct brcmf_sdio_dev *sdiodev; - struct brcmf_bus *bus_if; - - brcmf_dbg(SDIO, "Enter\n"); - brcmf_dbg(SDIO, "Class=%x\n", func->class); - brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(SDIO, "Function#: %d\n", func->num); - - /* Consume func num 1 but dont do anything with it. */ - if (func->num == 1) - return 0; - - /* Ignore anything but func 2 */ - if (func->num != 2) - return -ENODEV; - - bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); - if (!bus_if) - return -ENOMEM; - sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); - if (!sdiodev) { - kfree(bus_if); - return -ENOMEM; - } - - sdiodev->func[0] = func->card->sdio_func[0]; - sdiodev->func[1] = func->card->sdio_func[0]; - sdiodev->func[2] = func; - - sdiodev->bus_if = bus_if; - bus_if->bus_priv.sdio = sdiodev; - dev_set_drvdata(&func->dev, bus_if); - dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); - sdiodev->dev = &sdiodev->func[1]->dev; - sdiodev->pdata = brcmfmac_sdio_pdata; - - atomic_set(&sdiodev->suspend, false); - init_waitqueue_head(&sdiodev->request_byte_wait); - init_waitqueue_head(&sdiodev->request_word_wait); - init_waitqueue_head(&sdiodev->request_buffer_wait); - - brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n"); - err = brcmf_sdio_probe(sdiodev); - if (err) { - brcmf_err("F2 error, probe failed %d...\n", err); - goto fail; - } - brcmf_dbg(SDIO, "F2 init completed...\n"); - return 0; - -fail: - dev_set_drvdata(&func->dev, NULL); - dev_set_drvdata(&sdiodev->func[1]->dev, NULL); - kfree(sdiodev); - kfree(bus_if); - return err; -} - -static void brcmf_ops_sdio_remove(struct sdio_func *func) -{ - struct brcmf_bus *bus_if; - struct brcmf_sdio_dev *sdiodev; - - brcmf_dbg(SDIO, "Enter\n"); - brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(SDIO, "Function: %d\n", func->num); - - if (func->num != 1 && func->num != 2) - return; - - bus_if = dev_get_drvdata(&func->dev); - if (bus_if) { - sdiodev = bus_if->bus_priv.sdio; - brcmf_sdio_remove(sdiodev); - - dev_set_drvdata(&sdiodev->func[1]->dev, NULL); - dev_set_drvdata(&sdiodev->func[2]->dev, NULL); - - kfree(bus_if); - kfree(sdiodev); - } - - brcmf_dbg(SDIO, "Exit\n"); -} - -#ifdef CONFIG_PM_SLEEP -static int brcmf_sdio_suspend(struct device *dev) -{ - mmc_pm_flag_t sdio_flags; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - int ret = 0; - - brcmf_dbg(SDIO, "\n"); - - atomic_set(&sdiodev->suspend, true); - - sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); - if (!(sdio_flags & MMC_PM_KEEP_POWER)) { - brcmf_err("Host can't keep power while suspended\n"); - return -EINVAL; - } - - ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); - if (ret) { - brcmf_err("Failed to set pm_flags\n"); - return ret; - } - - brcmf_sdio_wdtmr_enable(sdiodev, false); - - return ret; -} - -static int brcmf_sdio_resume(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - - brcmf_sdio_wdtmr_enable(sdiodev, true); - atomic_set(&sdiodev->suspend, false); - return 0; -} - -static const struct dev_pm_ops brcmf_sdio_pm_ops = { - .suspend = brcmf_sdio_suspend, - .resume = brcmf_sdio_resume, -}; -#endif /* CONFIG_PM_SLEEP */ - -static struct sdio_driver brcmf_sdmmc_driver = { - .probe = brcmf_ops_sdio_probe, - .remove = brcmf_ops_sdio_remove, - .name = BRCMFMAC_SDIO_PDATA_NAME, - .id_table = brcmf_sdmmc_ids, -#ifdef CONFIG_PM_SLEEP - .drv = { - .pm = &brcmf_sdio_pm_ops, - }, -#endif /* CONFIG_PM_SLEEP */ -}; - -static int brcmf_sdio_pd_probe(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - brcmfmac_sdio_pdata = pdev->dev.platform_data; - - if (brcmfmac_sdio_pdata->power_on) - brcmfmac_sdio_pdata->power_on(); - - return 0; -} - -static int brcmf_sdio_pd_remove(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata->power_off) - brcmfmac_sdio_pdata->power_off(); - - sdio_unregister_driver(&brcmf_sdmmc_driver); - - return 0; -} - -static struct platform_driver brcmf_sdio_pd = { - .remove = brcmf_sdio_pd_remove, - .driver = { - .name = BRCMFMAC_SDIO_PDATA_NAME, - .owner = THIS_MODULE, - } -}; - -void brcmf_sdio_register(void) -{ - int ret; - - ret = sdio_register_driver(&brcmf_sdmmc_driver); - if (ret) - brcmf_err("sdio_register_driver failed: %d\n", ret); -} - -void brcmf_sdio_exit(void) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata) - platform_driver_unregister(&brcmf_sdio_pd); - else - sdio_unregister_driver(&brcmf_sdmmc_driver); -} - -void __init brcmf_sdio_init(void) -{ - int ret; - - brcmf_dbg(SDIO, "Enter\n"); - - ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); - if (ret == -ENODEV) - brcmf_dbg(SDIO, "No platform data available.\n"); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c new file mode 100644 index 00000000000..c7c9f15c0fe --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -0,0 +1,1035 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * 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/kernel.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/ssb/ssb_regs.h> +#include <linux/bcma/bcma.h> +#include <linux/bcma/bcma_regs.h> + +#include <defs.h> +#include <soc.h> +#include <brcm_hw_ids.h> +#include <brcmu_utils.h> +#include <chipcommon.h> +#include "dhd_dbg.h" +#include "chip.h" + +/* SOC Interconnect types (aka chip types) */ +#define SOCI_SB 0 +#define SOCI_AI 1 + +/* PL-368 DMP definitions */ +#define DMP_DESC_TYPE_MSK 0x0000000F +#define DMP_DESC_EMPTY 0x00000000 +#define DMP_DESC_VALID 0x00000001 +#define DMP_DESC_COMPONENT 0x00000001 +#define DMP_DESC_MASTER_PORT 0x00000003 +#define DMP_DESC_ADDRESS 0x00000005 +#define DMP_DESC_ADDRSIZE_GT32 0x00000008 +#define DMP_DESC_EOT 0x0000000F + +#define DMP_COMP_DESIGNER 0xFFF00000 +#define DMP_COMP_DESIGNER_S 20 +#define DMP_COMP_PARTNUM 0x000FFF00 +#define DMP_COMP_PARTNUM_S 8 +#define DMP_COMP_CLASS 0x000000F0 +#define DMP_COMP_CLASS_S 4 +#define DMP_COMP_REVISION 0xFF000000 +#define DMP_COMP_REVISION_S 24 +#define DMP_COMP_NUM_SWRAP 0x00F80000 +#define DMP_COMP_NUM_SWRAP_S 19 +#define DMP_COMP_NUM_MWRAP 0x0007C000 +#define DMP_COMP_NUM_MWRAP_S 14 +#define DMP_COMP_NUM_SPORT 0x00003E00 +#define DMP_COMP_NUM_SPORT_S 9 +#define DMP_COMP_NUM_MPORT 0x000001F0 +#define DMP_COMP_NUM_MPORT_S 4 + +#define DMP_MASTER_PORT_UID 0x0000FF00 +#define DMP_MASTER_PORT_UID_S 8 +#define DMP_MASTER_PORT_NUM 0x000000F0 +#define DMP_MASTER_PORT_NUM_S 4 + +#define DMP_SLAVE_ADDR_BASE 0xFFFFF000 +#define DMP_SLAVE_ADDR_BASE_S 12 +#define DMP_SLAVE_PORT_NUM 0x00000F00 +#define DMP_SLAVE_PORT_NUM_S 8 +#define DMP_SLAVE_TYPE 0x000000C0 +#define DMP_SLAVE_TYPE_S 6 +#define DMP_SLAVE_TYPE_SLAVE 0 +#define DMP_SLAVE_TYPE_BRIDGE 1 +#define DMP_SLAVE_TYPE_SWRAP 2 +#define DMP_SLAVE_TYPE_MWRAP 3 +#define DMP_SLAVE_SIZE_TYPE 0x00000030 +#define DMP_SLAVE_SIZE_TYPE_S 4 +#define DMP_SLAVE_SIZE_4K 0 +#define DMP_SLAVE_SIZE_8K 1 +#define DMP_SLAVE_SIZE_16K 2 +#define DMP_SLAVE_SIZE_DESC 3 + +/* EROM CompIdentB */ +#define CIB_REV_MASK 0xff000000 +#define CIB_REV_SHIFT 24 + +/* ARM CR4 core specific control flag bits */ +#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 + +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + +/* chip core base & ramsize */ +/* bcm4329 */ +/* SDIO device core, ID 0x829 */ +#define BCM4329_CORE_BUS_BASE 0x18011000 +/* internal memory core, ID 0x80e */ +#define BCM4329_CORE_SOCRAM_BASE 0x18003000 +/* ARM Cortex M3 core, ID 0x82a */ +#define BCM4329_CORE_ARM_BASE 0x18002000 +#define BCM4329_RAMSIZE 0x48000 + +/* bcm43143 */ +/* SDIO device core */ +#define BCM43143_CORE_BUS_BASE 0x18002000 +/* internal memory core */ +#define BCM43143_CORE_SOCRAM_BASE 0x18004000 +/* ARM Cortex M3 core, ID 0x82a */ +#define BCM43143_CORE_ARM_BASE 0x18003000 +#define BCM43143_RAMSIZE 0x70000 + +#define CORE_SB(base, field) \ + (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) +#define SBCOREREV(sbidh) \ + ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ + ((sbidh) & SSB_IDHIGH_RCLO)) + +struct sbconfig { + u32 PAD[2]; + u32 sbipsflag; /* initiator port ocp slave flag */ + u32 PAD[3]; + u32 sbtpsflag; /* target port ocp slave flag */ + u32 PAD[11]; + u32 sbtmerrloga; /* (sonics >= 2.3) */ + u32 PAD; + u32 sbtmerrlog; /* (sonics >= 2.3) */ + u32 PAD[3]; + u32 sbadmatch3; /* address match3 */ + u32 PAD; + u32 sbadmatch2; /* address match2 */ + u32 PAD; + u32 sbadmatch1; /* address match1 */ + u32 PAD[7]; + u32 sbimstate; /* initiator agent state */ + u32 sbintvec; /* interrupt mask */ + u32 sbtmstatelow; /* target state */ + u32 sbtmstatehigh; /* target state */ + u32 sbbwa0; /* bandwidth allocation table0 */ + u32 PAD; + u32 sbimconfiglow; /* initiator configuration */ + u32 sbimconfighigh; /* initiator configuration */ + u32 sbadmatch0; /* address match0 */ + u32 PAD; + u32 sbtmconfiglow; /* target configuration */ + u32 sbtmconfighigh; /* target configuration */ + u32 sbbconfig; /* broadcast configuration */ + u32 PAD; + u32 sbbstate; /* broadcast state */ + u32 PAD[3]; + u32 sbactcnfg; /* activate configuration */ + u32 PAD[3]; + u32 sbflagst; /* current sbflags */ + u32 PAD[3]; + u32 sbidlow; /* identification */ + u32 sbidhigh; /* identification */ +}; + +struct brcmf_core_priv { + struct brcmf_core pub; + u32 wrapbase; + struct list_head list; + struct brcmf_chip_priv *chip; +}; + +/* ARM CR4 core specific control flag bits */ +#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 + +/* D11 core specific control flag bits */ +#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 +#define D11_BCMA_IOCTL_PHYRESET 0x0008 + +struct brcmf_chip_priv { + struct brcmf_chip pub; + const struct brcmf_buscore_ops *ops; + void *ctx; + /* assured first core is chipcommon, second core is buscore */ + struct list_head cores; + u16 num_cores; + + bool (*iscoreup)(struct brcmf_core_priv *core); + void (*coredisable)(struct brcmf_core_priv *core, u32 prereset, + u32 reset); + void (*resetcore)(struct brcmf_core_priv *core, u32 prereset, u32 reset, + u32 postreset); +}; + +static void brcmf_chip_sb_corerev(struct brcmf_chip_priv *ci, + struct brcmf_core *core) +{ + u32 regdata; + + regdata = ci->ops->read32(ci->ctx, CORE_SB(core->base, sbidhigh)); + core->rev = SBCOREREV(regdata); +} + +static bool brcmf_chip_sb_iscoreup(struct brcmf_core_priv *core) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + u32 address; + + ci = core->chip; + address = CORE_SB(core->pub.base, sbtmstatelow); + regdata = ci->ops->read32(ci->ctx, address); + regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | + SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); + return SSB_TMSLOW_CLOCK == regdata; +} + +static bool brcmf_chip_ai_iscoreup(struct brcmf_core_priv *core) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + bool ret; + + ci = core->chip; + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; + + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); + ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); + + return ret; +} + +static void brcmf_chip_sb_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) +{ + struct brcmf_chip_priv *ci; + u32 val, base; + + ci = core->chip; + base = core->pub.base; + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if (val & SSB_TMSLOW_RESET) + return; + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + if ((val & SSB_TMSLOW_CLOCK) != 0) { + /* + * set target reject and spin until busy is clear + * (preserve core-specific bits) + */ + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + val | SSB_TMSLOW_REJECT); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + SPINWAIT((ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)) + & SSB_TMSHIGH_BUSY), 100000); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (val & SSB_TMSHIGH_BUSY) + brcmf_err("core state still busy\n"); + + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val |= SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + udelay(1); + SPINWAIT((ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)) & + SSB_IMSTATE_BUSY), 100000); + } + + /* set reset and reject while enabling the clocks */ + val = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), val); + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(10); + + /* clear the initiator reject bit */ + val = ci->ops->read32(ci->ctx, CORE_SB(base, sbidlow)); + if (val & SSB_IDLOW_INITIATOR) { + val = ci->ops->read32(ci->ctx, + CORE_SB(base, sbimstate)); + val &= ~SSB_IMSTATE_REJECT; + ci->ops->write32(ci->ctx, + CORE_SB(base, sbimstate), val); + } + } + + /* leave reset and reject asserted */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET)); + udelay(1); +} + +static void brcmf_chip_ai_coredisable(struct brcmf_core_priv *core, + u32 prereset, u32 reset) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + + ci = core->chip; + + /* if core is already in reset, skip reset */ + regdata = ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL); + if ((regdata & BCMA_RESET_CTL_RESET) != 0) + goto in_reset_configure; + + /* configure reset */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + prereset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); + + /* put in reset */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, + BCMA_RESET_CTL_RESET); + usleep_range(10, 20); + + /* wait till reset is 1 */ + SPINWAIT(ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) != + BCMA_RESET_CTL_RESET, 300); + +in_reset_configure: + /* in-reset configure */ + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + reset | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static void brcmf_chip_sb_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) +{ + struct brcmf_chip_priv *ci; + u32 regdata; + u32 base; + + ci = core->chip; + base = core->pub.base; + /* + * Must do the disable sequence first to work for + * arbitrary current core state. + */ + brcmf_chip_sb_coredisable(core, 0, 0); + + /* + * Now do the initialization sequence. + * set reset while enabling the clock and + * forcing them on throughout the core + */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | + SSB_TMSLOW_RESET); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + + /* clear any serror */ + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatehigh)); + if (regdata & SSB_TMSHIGH_SERR) + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatehigh), 0); + + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbimstate)); + if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) { + regdata &= ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO); + ci->ops->write32(ci->ctx, CORE_SB(base, sbimstate), regdata); + } + + /* clear reset and allow it to propagate throughout the core */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); + + /* leave clock enabled */ + ci->ops->write32(ci->ctx, CORE_SB(base, sbtmstatelow), + SSB_TMSLOW_CLOCK); + regdata = ci->ops->read32(ci->ctx, CORE_SB(base, sbtmstatelow)); + udelay(1); +} + +static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset, + u32 reset, u32 postreset) +{ + struct brcmf_chip_priv *ci; + int count; + + ci = core->chip; + + /* must disable first to work for arbitrary current core state */ + brcmf_chip_ai_coredisable(core, prereset, reset); + + count = 0; + while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) & + BCMA_RESET_CTL_RESET) { + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_RESET_CTL, 0); + count++; + if (count > 50) + break; + usleep_range(40, 60); + } + + ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL, + postreset | BCMA_IOCTL_CLK); + ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL); +} + +static char *brcmf_chip_name(uint chipid, char *buf, uint len) +{ + const char *fmt; + + fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; + snprintf(buf, len, fmt, chipid); + return buf; +} + +static struct brcmf_core *brcmf_chip_add_core(struct brcmf_chip_priv *ci, + u16 coreid, u32 base, + u32 wrapbase) +{ + struct brcmf_core_priv *core; + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) + return ERR_PTR(-ENOMEM); + + core->pub.id = coreid; + core->pub.base = base; + core->chip = ci; + core->wrapbase = wrapbase; + + list_add_tail(&core->list, &ci->cores); + return &core->pub; +} + +#ifdef DEBUG +/* safety check for chipinfo */ +static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) +{ + struct brcmf_core_priv *core; + bool need_socram = false; + bool has_socram = false; + int idx = 1; + + list_for_each_entry(core, &ci->cores, list) { + brcmf_dbg(INFO, " [%-2d] core 0x%x:%-2d base 0x%08x wrap 0x%08x\n", + idx++, core->pub.id, core->pub.rev, core->pub.base, + core->wrapbase); + + switch (core->pub.id) { + case BCMA_CORE_ARM_CM3: + need_socram = true; + break; + case BCMA_CORE_INTERNAL_MEM: + has_socram = true; + break; + case BCMA_CORE_ARM_CR4: + if (ci->pub.rambase == 0) { + brcmf_err("RAM base not provided with ARM CR4 core\n"); + return -ENOMEM; + } + break; + default: + break; + } + } + + /* check RAM core presence for ARM CM3 core */ + if (need_socram && !has_socram) { + brcmf_err("RAM core not provided with ARM CM3 core\n"); + return -ENODEV; + } + return 0; +} +#else /* DEBUG */ +static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) +{ + return 0; +} +#endif + +static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) +{ + switch (ci->pub.chip) { + case BCM4329_CHIP_ID: + ci->pub.ramsize = BCM4329_RAMSIZE; + break; + case BCM43143_CHIP_ID: + ci->pub.ramsize = BCM43143_RAMSIZE; + break; + case BCM43241_CHIP_ID: + ci->pub.ramsize = 0x90000; + break; + case BCM4330_CHIP_ID: + ci->pub.ramsize = 0x48000; + break; + case BCM4334_CHIP_ID: + ci->pub.ramsize = 0x80000; + break; + case BCM4335_CHIP_ID: + ci->pub.ramsize = 0xc0000; + ci->pub.rambase = 0x180000; + break; + case BCM43362_CHIP_ID: + ci->pub.ramsize = 0x3c000; + break; + case BCM4339_CHIP_ID: + case BCM4354_CHIP_ID: + ci->pub.ramsize = 0xc0000; + ci->pub.rambase = 0x180000; + break; + default: + brcmf_err("unknown chip: %s\n", ci->pub.name); + break; + } +} + +static u32 brcmf_chip_dmp_get_desc(struct brcmf_chip_priv *ci, u32 *eromaddr, + u8 *type) +{ + u32 val; + + /* read next descriptor */ + val = ci->ops->read32(ci->ctx, *eromaddr); + *eromaddr += 4; + + if (!type) + return val; + + /* determine descriptor type */ + *type = (val & DMP_DESC_TYPE_MSK); + if ((*type & ~DMP_DESC_ADDRSIZE_GT32) == DMP_DESC_ADDRESS) + *type = DMP_DESC_ADDRESS; + + return val; +} + +static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr, + u32 *regbase, u32 *wrapbase) +{ + u8 desc; + u32 val; + u8 mpnum = 0; + u8 stype, sztype, wraptype; + + *regbase = 0; + *wrapbase = 0; + + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + if (desc == DMP_DESC_MASTER_PORT) { + mpnum = (val & DMP_MASTER_PORT_NUM) >> DMP_MASTER_PORT_NUM_S; + wraptype = DMP_SLAVE_TYPE_MWRAP; + } else if (desc == DMP_DESC_ADDRESS) { + /* revert erom address */ + *eromaddr -= 4; + wraptype = DMP_SLAVE_TYPE_SWRAP; + } else { + *eromaddr -= 4; + return -EILSEQ; + } + + do { + /* locate address descriptor */ + do { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, &desc); + /* unexpected table end */ + if (desc == DMP_DESC_EOT) { + *eromaddr -= 4; + return -EFAULT; + } + } while (desc != DMP_DESC_ADDRESS); + + /* skip upper 32-bit address descriptor */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + + sztype = (val & DMP_SLAVE_SIZE_TYPE) >> DMP_SLAVE_SIZE_TYPE_S; + + /* next size descriptor can be skipped */ + if (sztype == DMP_SLAVE_SIZE_DESC) { + val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + /* skip upper size descriptor if present */ + if (val & DMP_DESC_ADDRSIZE_GT32) + brcmf_chip_dmp_get_desc(ci, eromaddr, NULL); + } + + /* only look for 4K register regions */ + if (sztype != DMP_SLAVE_SIZE_4K) + continue; + + stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S; + + /* only regular slave and wrapper */ + if (*regbase == 0 && stype == DMP_SLAVE_TYPE_SLAVE) + *regbase = val & DMP_SLAVE_ADDR_BASE; + if (*wrapbase == 0 && stype == wraptype) + *wrapbase = val & DMP_SLAVE_ADDR_BASE; + } while (*regbase == 0 || *wrapbase == 0); + + return 0; +} + +static +int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 eromaddr; + u8 desc_type = 0; + u32 val; + u16 id; + u8 nmp, nsp, nmw, nsw, rev; + u32 base, wrap; + int err; + + eromaddr = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, eromptr)); + + while (desc_type != DMP_DESC_EOT) { + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (!(val & DMP_DESC_VALID)) + continue; + + if (desc_type == DMP_DESC_EMPTY) + continue; + + /* need a component descriptor */ + if (desc_type != DMP_DESC_COMPONENT) + continue; + + id = (val & DMP_COMP_PARTNUM) >> DMP_COMP_PARTNUM_S; + + /* next descriptor must be component as well */ + val = brcmf_chip_dmp_get_desc(ci, &eromaddr, &desc_type); + if (WARN_ON((val & DMP_DESC_TYPE_MSK) != DMP_DESC_COMPONENT)) + return -EFAULT; + + /* only look at cores with master port(s) */ + nmp = (val & DMP_COMP_NUM_MPORT) >> DMP_COMP_NUM_MPORT_S; + nsp = (val & DMP_COMP_NUM_SPORT) >> DMP_COMP_NUM_SPORT_S; + nmw = (val & DMP_COMP_NUM_MWRAP) >> DMP_COMP_NUM_MWRAP_S; + nsw = (val & DMP_COMP_NUM_SWRAP) >> DMP_COMP_NUM_SWRAP_S; + rev = (val & DMP_COMP_REVISION) >> DMP_COMP_REVISION_S; + + /* need core with ports */ + if (nmw + nsw == 0) + continue; + + /* try to obtain register address info */ + err = brcmf_chip_dmp_get_regaddr(ci, &eromaddr, &base, &wrap); + if (err) + continue; + + /* finally a core to be added */ + core = brcmf_chip_add_core(ci, id, base, wrap); + if (IS_ERR(core)) + return PTR_ERR(core); + + core->rev = rev; + } + + return 0; +} + +static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) +{ + struct brcmf_core *core; + u32 regdata; + u32 socitype; + + /* Get CC core rev + * Chipid is assume to be at offset 0 from SI_ENUM_BASE + * For different chiptypes or old sdio hosts w/o chipcommon, + * other ways of recognition should be added here. + */ + regdata = ci->ops->read32(ci->ctx, CORE_CC_REG(SI_ENUM_BASE, chipid)); + ci->pub.chip = regdata & CID_ID_MASK; + ci->pub.chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; + socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; + + brcmf_chip_name(ci->pub.chip, ci->pub.name, sizeof(ci->pub.name)); + brcmf_dbg(INFO, "found %s chip: BCM%s, rev=%d\n", + socitype == SOCI_SB ? "SB" : "AXI", ci->pub.name, + ci->pub.chiprev); + + if (socitype == SOCI_SB) { + if (ci->pub.chip != BCM4329_CHIP_ID) { + brcmf_err("SB chip is not supported\n"); + return -ENODEV; + } + ci->iscoreup = brcmf_chip_sb_iscoreup; + ci->coredisable = brcmf_chip_sb_coredisable; + ci->resetcore = brcmf_chip_sb_resetcore; + + core = brcmf_chip_add_core(ci, BCMA_CORE_CHIPCOMMON, + SI_ENUM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_SDIO_DEV, + BCM4329_CORE_BUS_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_INTERNAL_MEM, + BCM4329_CORE_SOCRAM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + core = brcmf_chip_add_core(ci, BCMA_CORE_ARM_CM3, + BCM4329_CORE_ARM_BASE, 0); + brcmf_chip_sb_corerev(ci, core); + + core = brcmf_chip_add_core(ci, BCMA_CORE_80211, 0x18001000, 0); + brcmf_chip_sb_corerev(ci, core); + } else if (socitype == SOCI_AI) { + ci->iscoreup = brcmf_chip_ai_iscoreup; + ci->coredisable = brcmf_chip_ai_coredisable; + ci->resetcore = brcmf_chip_ai_resetcore; + + brcmf_chip_dmp_erom_scan(ci); + } else { + brcmf_err("chip backplane type %u is not supported\n", + socitype); + return -ENODEV; + } + + brcmf_chip_get_raminfo(ci); + + return brcmf_chip_cores_check(ci); +} + +static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) +{ + struct brcmf_core *core; + struct brcmf_core_priv *cr4; + u32 val; + + + core = brcmf_chip_get_core(&chip->pub, id); + if (!core) + return; + + switch (id) { + case BCMA_CORE_ARM_CM3: + brcmf_chip_coredisable(core, 0, 0); + break; + case BCMA_CORE_ARM_CR4: + cr4 = container_of(core, struct brcmf_core_priv, pub); + + /* clear all IOCTL bits except HALT bit */ + val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL); + val &= ARMCR4_BCMA_IOCTL_CPUHALT; + brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, + ARMCR4_BCMA_IOCTL_CPUHALT); + break; + default: + brcmf_err("unknown id: %u\n", id); + break; + } +} + +static int brcmf_chip_setup(struct brcmf_chip_priv *chip) +{ + struct brcmf_chip *pub; + struct brcmf_core_priv *cc; + u32 base; + u32 val; + int ret = 0; + + pub = &chip->pub; + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + base = cc->pub.base; + + /* get chipcommon capabilites */ + pub->cc_caps = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, capabilities)); + + /* get pmu caps & rev */ + if (pub->cc_caps & CC_CAP_PMU) { + val = chip->ops->read32(chip->ctx, + CORE_CC_REG(base, pmucapabilities)); + pub->pmurev = val & PCAP_REV_MASK; + pub->pmucaps = val; + } + + brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, pmucaps=0x%x\n", + cc->pub.rev, pub->pmurev, pub->pmucaps); + + /* execute bus core specific setup */ + if (chip->ops->setup) + ret = chip->ops->setup(chip->ctx, pub); + + /* + * Make sure any on-chip ARM is off (in case strapping is wrong), + * or downloaded code was already running. + */ + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + return ret; +} + +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops) +{ + struct brcmf_chip_priv *chip; + int err = 0; + + if (WARN_ON(!ops->read32)) + err = -EINVAL; + if (WARN_ON(!ops->write32)) + err = -EINVAL; + if (WARN_ON(!ops->prepare)) + err = -EINVAL; + if (WARN_ON(!ops->exit_dl)) + err = -EINVAL; + if (err < 0) + return ERR_PTR(-EINVAL); + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&chip->cores); + chip->num_cores = 0; + chip->ops = ops; + chip->ctx = ctx; + + err = ops->prepare(ctx); + if (err < 0) + goto fail; + + err = brcmf_chip_recognition(chip); + if (err < 0) + goto fail; + + err = brcmf_chip_setup(chip); + if (err < 0) + goto fail; + + return &chip->pub; + +fail: + brcmf_chip_detach(&chip->pub); + return ERR_PTR(err); +} + +void brcmf_chip_detach(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + struct brcmf_core_priv *tmp; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry_safe(core, tmp, &chip->cores, list) { + list_del(&core->list); + kfree(core); + } + kfree(chip); +} + +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *core; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + list_for_each_entry(core, &chip->cores, list) + if (core->pub.id == coreid) + return &core->pub; + + return NULL; +} + +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core_priv *cc; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + cc = list_first_entry(&chip->cores, struct brcmf_core_priv, list); + if (WARN_ON(!cc || cc->pub.id != BCMA_CORE_CHIPCOMMON)) + return brcmf_chip_get_core(pub, BCMA_CORE_CHIPCOMMON); + return &cc->pub; +} + +bool brcmf_chip_iscoreup(struct brcmf_core *pub) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + return core->chip->iscoreup(core); +} + +void brcmf_chip_coredisable(struct brcmf_core *pub, u32 prereset, u32 reset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->coredisable(core, prereset, reset); +} + +void brcmf_chip_resetcore(struct brcmf_core *pub, u32 prereset, u32 reset, + u32 postreset) +{ + struct brcmf_core_priv *core; + + core = container_of(pub, struct brcmf_core_priv, pub); + core->chip->resetcore(core, prereset, reset, postreset); +} + +static void +brcmf_chip_cm3_enterdl(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CM3); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + brcmf_chip_resetcore(core, 0, 0, 0); +} + +static bool brcmf_chip_cm3_exitdl(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_INTERNAL_MEM); + if (!brcmf_chip_iscoreup(core)) { + brcmf_err("SOCRAM core is down after reset?\n"); + return false; + } + + chip->ops->exit_dl(chip->ctx, &chip->pub, 0); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CM3); + brcmf_chip_resetcore(core, 0, 0, 0); + + return true; +} + +static inline void +brcmf_chip_cr4_enterdl(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_cr4_exitdl(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->exit_dl(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CR4); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + +void brcmf_chip_enter_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); + if (arm) { + brcmf_chip_cr4_enterdl(chip); + return; + } + + brcmf_chip_cm3_enterdl(chip); +} + +bool brcmf_chip_exit_download(struct brcmf_chip *pub, u32 rstvec) +{ + struct brcmf_chip_priv *chip; + struct brcmf_core *arm; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); + if (arm) + return brcmf_chip_cr4_exitdl(chip, rstvec); + + return brcmf_chip_cm3_exitdl(chip); +} + +bool brcmf_chip_sr_capable(struct brcmf_chip *pub) +{ + u32 base, addr, reg, pmu_cc3_mask = ~0; + struct brcmf_chip_priv *chip; + + brcmf_dbg(TRACE, "Enter\n"); + + /* old chips with PMU version less than 17 don't support save restore */ + if (pub->pmurev < 17) + return false; + + base = brcmf_chip_get_chipcommon(pub)->base; + 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: + /* read PMU chipcontrol register 3 */ + addr = CORE_CC_REG(base, chipcontrol_addr); + chip->ops->write32(chip->ctx, addr, 3); + addr = CORE_CC_REG(base, chipcontrol_data); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & pmu_cc3_mask) != 0; + default: + addr = CORE_CC_REG(base, pmucapabilities_ext); + reg = chip->ops->read32(chip->ctx, addr); + if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0) + return false; + + addr = CORE_CC_REG(base, retention_ctl); + reg = chip->ops->read32(chip->ctx, addr); + return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK | + PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/brcm80211/brcmfmac/chip.h new file mode 100644 index 00000000000..c32908da90c --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * 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 BRCMF_CHIP_H +#define BRCMF_CHIP_H + +#include <linux/types.h> + +#define CORE_CC_REG(base, field) \ + (base + offsetof(struct chipcregs, field)) + +/** + * struct brcmf_chip - chip level information. + * + * @chip: chip identifier. + * @chiprev: chip revision. + * @cc_caps: chipcommon core capabilities. + * @pmucaps: PMU capabilities. + * @pmurev: PMU revision. + * @rambase: RAM base address (only applicable for ARM CR4 chips). + * @ramsize: amount of RAM on chip. + * @name: string representation of the chip identifier. + */ +struct brcmf_chip { + u32 chip; + u32 chiprev; + u32 cc_caps; + u32 pmucaps; + u32 pmurev; + u32 rambase; + u32 ramsize; + char name[8]; +}; + +/** + * struct brcmf_core - core related information. + * + * @id: core identifier. + * @rev: core revision. + * @base: base address of core register space. + */ +struct brcmf_core { + u16 id; + u16 rev; + u32 base; +}; + +/** + * struct brcmf_buscore_ops - buscore specific callbacks. + * + * @read32: read 32-bit value over bus. + * @write32: write 32-bit value over bus. + * @prepare: prepare bus for core configuration. + * @setup: bus-specific core setup. + * @exit_dl: exit download state. + * The callback should use the provided @rstvec when non-zero. + */ +struct brcmf_buscore_ops { + u32 (*read32)(void *ctx, u32 addr); + void (*write32)(void *ctx, u32 addr, u32 value); + int (*prepare)(void *ctx); + int (*setup)(void *ctx, struct brcmf_chip *chip); + void (*exit_dl)(void *ctx, struct brcmf_chip *chip, u32 rstvec); +}; + +struct brcmf_chip *brcmf_chip_attach(void *ctx, + const struct brcmf_buscore_ops *ops); +void brcmf_chip_detach(struct brcmf_chip *chip); +struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *chip, u16 coreid); +struct brcmf_core *brcmf_chip_get_chipcommon(struct brcmf_chip *chip); +bool brcmf_chip_iscoreup(struct brcmf_core *core); +void brcmf_chip_coredisable(struct brcmf_core *core, u32 prereset, u32 reset); +void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset, + u32 postreset); +void brcmf_chip_enter_download(struct brcmf_chip *ci); +bool brcmf_chip_exit_download(struct brcmf_chip *ci, u32 rstvec); +bool brcmf_chip_sr_capable(struct brcmf_chip *pub); + +#endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 2eb9e642c9b..16f9ab2568a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -21,483 +21,33 @@ #ifndef _BRCMF_H_ #define _BRCMF_H_ -#define BRCMF_VERSION_STR "4.218.248.5" - #include "fweh.h" -/******************************************************************************* - * IO codes that are interpreted by dongle firmware - ******************************************************************************/ -#define BRCMF_C_GET_VERSION 1 -#define BRCMF_C_UP 2 -#define BRCMF_C_DOWN 3 -#define BRCMF_C_SET_PROMISC 10 -#define BRCMF_C_GET_RATE 12 -#define BRCMF_C_GET_INFRA 19 -#define BRCMF_C_SET_INFRA 20 -#define BRCMF_C_GET_AUTH 21 -#define BRCMF_C_SET_AUTH 22 -#define BRCMF_C_GET_BSSID 23 -#define BRCMF_C_GET_SSID 25 -#define BRCMF_C_SET_SSID 26 -#define BRCMF_C_TERMINATED 28 -#define BRCMF_C_GET_CHANNEL 29 -#define BRCMF_C_SET_CHANNEL 30 -#define BRCMF_C_GET_SRL 31 -#define BRCMF_C_SET_SRL 32 -#define BRCMF_C_GET_LRL 33 -#define BRCMF_C_SET_LRL 34 -#define BRCMF_C_GET_RADIO 37 -#define BRCMF_C_SET_RADIO 38 -#define BRCMF_C_GET_PHYTYPE 39 -#define BRCMF_C_SET_KEY 45 -#define BRCMF_C_SET_PASSIVE_SCAN 49 -#define BRCMF_C_SCAN 50 -#define BRCMF_C_SCAN_RESULTS 51 -#define BRCMF_C_DISASSOC 52 -#define BRCMF_C_REASSOC 53 -#define BRCMF_C_SET_ROAM_TRIGGER 55 -#define BRCMF_C_SET_ROAM_DELTA 57 -#define BRCMF_C_GET_BCNPRD 75 -#define BRCMF_C_SET_BCNPRD 76 -#define BRCMF_C_GET_DTIMPRD 77 -#define BRCMF_C_SET_DTIMPRD 78 -#define BRCMF_C_SET_COUNTRY 84 -#define BRCMF_C_GET_PM 85 -#define BRCMF_C_SET_PM 86 -#define BRCMF_C_GET_CURR_RATESET 114 -#define BRCMF_C_GET_AP 117 -#define BRCMF_C_SET_AP 118 -#define BRCMF_C_GET_RSSI 127 -#define BRCMF_C_GET_WSEC 133 -#define BRCMF_C_SET_WSEC 134 -#define BRCMF_C_GET_PHY_NOISE 135 -#define BRCMF_C_GET_BSS_INFO 136 -#define BRCMF_C_GET_BANDLIST 140 -#define BRCMF_C_SET_SCB_TIMEOUT 158 -#define BRCMF_C_GET_PHYLIST 180 -#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 -#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 -#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 -#define BRCMF_C_GET_VALID_CHANNELS 217 -#define BRCMF_C_GET_KEY_PRIMARY 235 -#define BRCMF_C_SET_KEY_PRIMARY 236 -#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258 -#define BRCMF_C_GET_VAR 262 -#define BRCMF_C_SET_VAR 263 - -/* phy types (returned by WLC_GET_PHYTPE) */ -#define WLC_PHY_TYPE_A 0 -#define WLC_PHY_TYPE_B 1 -#define WLC_PHY_TYPE_G 2 -#define WLC_PHY_TYPE_N 4 -#define WLC_PHY_TYPE_LP 5 -#define WLC_PHY_TYPE_SSN 6 -#define WLC_PHY_TYPE_HT 7 -#define WLC_PHY_TYPE_LCN 8 -#define WLC_PHY_TYPE_NULL 0xf - -#define BRCMF_EVENTING_MASK_LEN 16 - #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 -#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */ - -/* size of brcmf_scan_params not including variable length array */ -#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 - -/* masks for channel and ssid count */ -#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff -#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16 - -/* primary (ie tx) key */ -#define BRCMF_PRIMARY_KEY (1 << 1) - /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 -#define DOT11_BSSTYPE_ANY 2 #define DOT11_MAX_DEFAULT_KEYS 4 -#define BRCMF_ESCAN_REQ_VERSION 1 - -#define WLC_BSS_RSSI_ON_CHANNEL 0x0002 - -#define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ -#define BRCMF_STA_ASSOC 0x10 /* Associated */ - -#define BRCMF_E_STATUS_SUCCESS 0 -#define BRCMF_E_STATUS_FAIL 1 -#define BRCMF_E_STATUS_TIMEOUT 2 -#define BRCMF_E_STATUS_NO_NETWORKS 3 -#define BRCMF_E_STATUS_ABORT 4 -#define BRCMF_E_STATUS_NO_ACK 5 -#define BRCMF_E_STATUS_UNSOLICITED 6 -#define BRCMF_E_STATUS_ATTEMPT 7 -#define BRCMF_E_STATUS_PARTIAL 8 -#define BRCMF_E_STATUS_NEWSCAN 9 -#define BRCMF_E_STATUS_NEWASSOC 10 -#define BRCMF_E_STATUS_11HQUIET 11 -#define BRCMF_E_STATUS_SUPPRESS 12 -#define BRCMF_E_STATUS_NOCHANS 13 -#define BRCMF_E_STATUS_CS_ABORT 15 -#define BRCMF_E_STATUS_ERROR 16 - -#define BRCMF_E_REASON_INITIAL_ASSOC 0 -#define BRCMF_E_REASON_LOW_RSSI 1 -#define BRCMF_E_REASON_DEAUTH 2 -#define BRCMF_E_REASON_DISASSOC 3 -#define BRCMF_E_REASON_BCNS_LOST 4 -#define BRCMF_E_REASON_MINTXRATE 9 -#define BRCMF_E_REASON_TXFAIL 10 - -#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4 -#define BRCMF_E_REASON_FAST_ROAM_FAILED 5 -#define BRCMF_E_REASON_DIRECTED_ROAM 6 -#define BRCMF_E_REASON_TSPEC_REJECTED 7 -#define BRCMF_E_REASON_BETTER_AP 8 - -#define BRCMF_E_PRUNE_ENCR_MISMATCH 1 -#define BRCMF_E_PRUNE_BCAST_BSSID 2 -#define BRCMF_E_PRUNE_MAC_DENY 3 -#define BRCMF_E_PRUNE_MAC_NA 4 -#define BRCMF_E_PRUNE_REG_PASSV 5 -#define BRCMF_E_PRUNE_SPCT_MGMT 6 -#define BRCMF_E_PRUNE_RADAR 7 -#define BRCMF_E_RSN_MISMATCH 8 -#define BRCMF_E_PRUNE_NO_COMMON_RATES 9 -#define BRCMF_E_PRUNE_BASIC_RATES 10 -#define BRCMF_E_PRUNE_CIPHER_NA 12 -#define BRCMF_E_PRUNE_KNOWN_STA 13 -#define BRCMF_E_PRUNE_WDS_PEER 15 -#define BRCMF_E_PRUNE_QBSS_LOAD 16 -#define BRCMF_E_PRUNE_HOME_AP 17 - -#define BRCMF_E_SUP_OTHER 0 -#define BRCMF_E_SUP_DECRYPT_KEY_DATA 1 -#define BRCMF_E_SUP_BAD_UCAST_WEP128 2 -#define BRCMF_E_SUP_BAD_UCAST_WEP40 3 -#define BRCMF_E_SUP_UNSUP_KEY_LEN 4 -#define BRCMF_E_SUP_PW_KEY_CIPHER 5 -#define BRCMF_E_SUP_MSG3_TOO_MANY_IE 6 -#define BRCMF_E_SUP_MSG3_IE_MISMATCH 7 -#define BRCMF_E_SUP_NO_INSTALL_FLAG 8 -#define BRCMF_E_SUP_MSG3_NO_GTK 9 -#define BRCMF_E_SUP_GRP_KEY_CIPHER 10 -#define BRCMF_E_SUP_GRP_MSG1_NO_GTK 11 -#define BRCMF_E_SUP_GTK_DECRYPT_FAIL 12 -#define BRCMF_E_SUP_SEND_FAIL 13 -#define BRCMF_E_SUP_DEAUTH 14 - -#define BRCMF_E_IF_ADD 1 -#define BRCMF_E_IF_DEL 2 -#define BRCMF_E_IF_CHANGE 3 - -#define BRCMF_E_IF_FLAG_NOIF 1 - -#define BRCMF_E_IF_ROLE_STA 0 -#define BRCMF_E_IF_ROLE_AP 1 -#define BRCMF_E_IF_ROLE_WDS 2 - -#define BRCMF_E_LINK_BCN_LOSS 1 -#define BRCMF_E_LINK_DISASSOC 2 -#define BRCMF_E_LINK_ASSOC_REC 3 -#define BRCMF_E_LINK_BSSCFG_DIS 4 - /* Small, medium and maximum buffer size for dcmd */ #define BRCMF_DCMD_SMLEN 256 #define BRCMF_DCMD_MEDLEN 1536 #define BRCMF_DCMD_MAXLEN 8192 -#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 - -/* Pattern matching filter. Specifies an offset within received packets to - * start matching, the pattern to match, the size of the pattern, and a bitmask - * that indicates which bits within the pattern should be matched. - */ -struct brcmf_pkt_filter_pattern_le { - /* - * Offset within received packet to start pattern matching. - * Offset '0' is the first byte of the ethernet header. - */ - __le32 offset; - /* Size of the pattern. Bitmask must be the same size.*/ - __le32 size_bytes; - /* - * Variable length mask and pattern data. mask starts at offset 0. - * Pattern immediately follows mask. - */ - u8 mask_and_pattern[1]; -}; - -/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ -struct brcmf_pkt_filter_le { - __le32 id; /* Unique filter id, specified by app. */ - __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ - __le32 negate_match; /* Negate the result of filter matches */ - union { /* Filter definitions */ - struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */ - } u; -}; - -/* IOVAR "pkt_filter_enable" parameter. */ -struct brcmf_pkt_filter_enable_le { - __le32 id; /* Unique filter id */ - __le32 enable; /* Enable/disable bool */ -}; - -/* BSS info structure - * Applications MUST CHECK ie_offset field and length field to access IEs and - * next bss_info structure in a vector (in struct brcmf_scan_results) - */ -struct brcmf_bss_info_le { - __le32 version; /* version field */ - __le32 length; /* byte length of data in this record, - * starting at version and including IEs - */ - u8 BSSID[ETH_ALEN]; - __le16 beacon_period; /* units are Kusec */ - __le16 capability; /* Capability information */ - u8 SSID_len; - u8 SSID[32]; - struct { - __le32 count; /* # rates in this set */ - u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ - } rateset; /* supported rates */ - __le16 chanspec; /* chanspec for bss */ - __le16 atim_window; /* units are Kusec */ - u8 dtim_period; /* DTIM period */ - __le16 RSSI; /* receive signal strength (in dBm) */ - s8 phy_noise; /* noise (in dBm) */ - - u8 n_cap; /* BSS is 802.11N Capable */ - /* 802.11N BSS Capabilities (based on HT_CAP_*): */ - __le32 nbss_cap; - u8 ctl_ch; /* 802.11N BSS control channel number */ - __le32 reserved32[1]; /* Reserved for expansion of BSS properties */ - u8 flags; /* flags */ - u8 reserved[3]; /* Reserved for expansion of BSS properties */ - u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ - - __le16 ie_offset; /* offset at which IEs start, from beginning */ - __le32 ie_length; /* byte length of Information Elements */ - __le16 SNR; /* average SNR of during frame reception */ - /* Add new fields here */ - /* variable length Information Elements */ -}; - -struct brcm_rateset_le { - /* # rates in this set */ - __le32 count; - /* rates in 500kbps units w/hi bit set if basic */ - u8 rates[BRCMF_MAXRATES_IN_SET]; -}; - -struct brcmf_ssid { - u32 SSID_len; - unsigned char SSID[32]; -}; - -struct brcmf_ssid_le { - __le32 SSID_len; - unsigned char SSID[32]; -}; - -struct brcmf_scan_params_le { - struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ - u8 bssid[ETH_ALEN]; /* default: bcast */ - s8 bss_type; /* default: any, - * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT - */ - u8 scan_type; /* flags, 0 use default */ - __le32 nprobes; /* -1 use default, number of probes per channel */ - __le32 active_time; /* -1 use default, dwell time per channel for - * active scanning - */ - __le32 passive_time; /* -1 use default, dwell time per channel - * for passive scanning - */ - __le32 home_time; /* -1 use default, dwell time for the - * home channel between channel scans - */ - __le32 channel_num; /* count of channels and ssids that follow - * - * low half is count of channels in - * channel_list, 0 means default (use all - * available channels) - * - * high half is entries in struct brcmf_ssid - * array that follows channel_list, aligned for - * s32 (4 bytes) meaning an odd channel count - * implies a 2-byte pad between end of - * channel_list and first ssid - * - * if ssid count is zero, single ssid in the - * fixed parameter portion is assumed, otherwise - * ssid in the fixed portion is ignored - */ - __le16 channel_list[1]; /* list of chanspecs */ -}; - -struct brcmf_scan_results { - u32 buflen; - u32 version; - u32 count; - struct brcmf_bss_info_le bss_info_le[]; -}; - -struct brcmf_escan_params_le { - __le32 version; - __le16 action; - __le16 sync_id; - struct brcmf_scan_params_le params_le; -}; - -struct brcmf_escan_result_le { - __le32 buflen; - __le32 version; - __le16 sync_id; - __le16 bss_count; - struct brcmf_bss_info_le bss_info_le; -}; - -#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \ - sizeof(struct brcmf_bss_info_le)) - -/* used for association with a specific BSSID and chanspec list */ -struct brcmf_assoc_params_le { - /* 00:00:00:00:00:00: broadcast scan */ - u8 bssid[ETH_ALEN]; - /* 0: all available channels, otherwise count of chanspecs in - * chanspec_list */ - __le32 chanspec_num; - /* list of chanspecs */ - __le16 chanspec_list[1]; -}; - -/* used for join with or without a specific bssid and channel list */ -struct brcmf_join_params { - struct brcmf_ssid_le ssid_le; - struct brcmf_assoc_params_le params_le; -}; - -/* scan params for extended join */ -struct brcmf_join_scan_params_le { - u8 scan_type; /* 0 use default, active or passive scan */ - __le32 nprobes; /* -1 use default, nr of probes per channel */ - __le32 active_time; /* -1 use default, dwell time per channel for - * active scanning - */ - __le32 passive_time; /* -1 use default, dwell time per channel - * for passive scanning - */ - __le32 home_time; /* -1 use default, dwell time for the home - * channel between channel scans - */ -}; - -/* extended join params */ -struct brcmf_ext_join_params_le { - struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */ - struct brcmf_join_scan_params_le scan_le; - struct brcmf_assoc_params_le assoc_le; -}; - -struct brcmf_wsec_key { - u32 index; /* key index */ - u32 len; /* key length */ - u8 data[WLAN_MAX_KEY_LEN]; /* key data */ - u32 pad_1[18]; - u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ - u32 flags; /* misc flags */ - u32 pad_2[3]; - u32 iv_initialized; /* has IV been initialized already? */ - u32 pad_3; - /* Rx IV */ - struct { - u32 hi; /* upper 32 bits of IV */ - u16 lo; /* lower 16 bits of IV */ - } rxiv; - u32 pad_4[2]; - u8 ea[ETH_ALEN]; /* per station */ -}; - -/* - * dongle requires same struct as above but with fields in little endian order +/* IOCTL from host to device are limited in lenght. A device can only handle + * ethernet frame size. This limitation is to be applied by protocol layer. */ -struct brcmf_wsec_key_le { - __le32 index; /* key index */ - __le32 len; /* key length */ - u8 data[WLAN_MAX_KEY_LEN]; /* key data */ - __le32 pad_1[18]; - __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ - __le32 flags; /* misc flags */ - __le32 pad_2[3]; - __le32 iv_initialized; /* has IV been initialized already? */ - __le32 pad_3; - /* Rx IV */ - struct { - __le32 hi; /* upper 32 bits of IV */ - __le16 lo; /* lower 16 bits of IV */ - } rxiv; - __le32 pad_4[2]; - u8 ea[ETH_ALEN]; /* per station */ -}; - -/* Used to get specific STA parameters */ -struct brcmf_scb_val_le { - __le32 val; - u8 ea[ETH_ALEN]; -}; - -/* channel encoding */ -struct brcmf_channel_info_le { - __le32 hw_channel; - __le32 target_channel; - __le32 scan_channel; -}; - -struct brcmf_sta_info_le { - __le16 ver; /* version of this struct */ - __le16 len; /* length in bytes of this structure */ - __le16 cap; /* sta's advertised capabilities */ - __le32 flags; /* flags defined below */ - __le32 idle; /* time since data pkt rx'd from sta */ - u8 ea[ETH_ALEN]; /* Station address */ - __le32 count; /* # rates in this set */ - u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */ - /* w/hi bit set if basic */ - __le32 in; /* seconds elapsed since associated */ - __le32 listen_interval_inms; /* Min Listen interval in ms for STA */ - __le32 tx_pkts; /* # of packets transmitted */ - __le32 tx_failures; /* # of packets failed */ - __le32 rx_ucast_pkts; /* # of unicast packets received */ - __le32 rx_mcast_pkts; /* # of multicast packets received */ - __le32 tx_rate; /* Rate of last successful tx frame */ - __le32 rx_rate; /* Rate of last successful rx frame */ - __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */ - __le32 rx_decrypt_failures; /* # of packet decrypted failed */ -}; +#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) -struct brcmf_chanspec_list { - __le32 count; /* # of entries */ - __le32 element[1]; /* variable length uint32 list */ -}; +#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 -/* - * WLC_E_PROBRESP_MSG - * WLC_E_P2P_PROBREQ_MSG - * WLC_E_ACTION_FRAME_RX +/* Length of firmware version string stored for + * ethtool driver info which uses 32 bytes as well. */ -struct brcmf_rx_mgmt_data { - __be16 version; - __be16 chanspec; - __be32 rssi; - __be32 mactime; - __be32 rate; -}; +#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 /* Bus independent dongle command */ struct brcmf_dcmd { @@ -537,7 +87,7 @@ struct brcmf_fws_info; /* firmware signalling info */ struct brcmf_pub { /* Linkage ponters */ struct brcmf_bus *bus_if; - struct brcmf_proto *prot; + struct brcmf_proto *proto; struct brcmf_cfg80211_info *config; /* Internal brcmf items */ @@ -546,7 +96,7 @@ struct brcmf_pub { u8 wme_dp; /* wme discard priority */ /* Dongle media info */ - unsigned long drv_version; /* Version of dongle-resident driver */ + char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ /* Multicast data packets sent to dongle */ @@ -568,14 +118,6 @@ struct brcmf_pub { #endif }; -struct brcmf_if_event { - u8 ifidx; - u8 action; - u8 flags; - u8 bssidx; - u8 role; -}; - /* forward declarations */ struct brcmf_cfg80211_vif; struct brcmf_fws_mac_descriptor; @@ -632,29 +174,22 @@ struct brcmf_skb_reorder_data { u8 *reorder; }; -extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); +int brcmf_netdev_wait_pend8021x(struct net_device *ndev); /* Return pointer to interface name */ -extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx); - -/* Query dongle */ -extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len); -extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len); +char *brcmf_ifname(struct brcmf_pub *drvr, int idx); -/* Remove any protocol-specific data header. */ -extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *rxp); - -extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); -extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, - s32 ifidx, char *name, u8 *mac_addr); -extern void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); +int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, + char *name, u8 *mac_addr); +void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -extern u32 brcmf_get_chip_info(struct brcmf_if *ifp); -extern void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, - bool success); +u32 brcmf_get_chip_info(struct brcmf_if *ifp); +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, + bool success); + +/* Sets dongle media info (drv_version, mac address). */ +int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); #endif /* _BRCMF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 74156f84180..7735328fff2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -17,13 +17,23 @@ #ifndef _BRCMF_BUS_H_ #define _BRCMF_BUS_H_ +#include "dhd_dbg.h" + /* The level of bus communication with the dongle */ enum brcmf_bus_state { + BRCMF_BUS_UNKNOWN, /* Not determined yet */ + BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */ BRCMF_BUS_DOWN, /* Not ready for frame transfers */ BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ BRCMF_BUS_DATA /* Ready for frame transfers */ }; +/* The level of bus communication with the dongle */ +enum brcmf_bus_protocol_type { + BRCMF_PROTO_BCDC, + BRCMF_PROTO_MSGBUF +}; + struct brcmf_bus_dcmd { char *name; char *param; @@ -34,6 +44,7 @@ struct brcmf_bus_dcmd { /** * struct brcmf_bus_ops - bus callback operations. * + * @preinit: execute bus/device specific dongle init commands (optional). * @init: prepare for communication with dongle. * @stop: clear pending frames, disable data flow. * @txdata: send a data frame to the dongle. When the data @@ -51,7 +62,7 @@ struct brcmf_bus_dcmd { * indicated otherwise these callbacks are mandatory. */ struct brcmf_bus_ops { - int (*init)(struct device *dev); + int (*preinit)(struct device *dev); void (*stop)(struct device *dev); int (*txdata)(struct device *dev, struct sk_buff *skb); int (*txctl)(struct device *dev, unsigned char *msg, uint len); @@ -63,6 +74,7 @@ struct brcmf_bus_ops { * struct brcmf_bus - interface structure between common and bus layer * * @bus_priv: pointer to private bus device. + * @proto_type: protocol type, bcdc or msgbuf * @dev: device pointer of bus device. * @drvr: public driver information. * @state: operational state of the bus interface. @@ -78,6 +90,7 @@ struct brcmf_bus { struct brcmf_sdio_dev *sdio; struct brcmf_usbdev *usb; } bus_priv; + enum brcmf_bus_protocol_type proto_type; struct device *dev; struct brcmf_pub *drvr; enum brcmf_bus_state state; @@ -85,7 +98,7 @@ struct brcmf_bus { unsigned long tx_realloc; u32 chip; u32 chiprev; - struct list_head dcmd_list; + bool always_use_fws_queue; struct brcmf_bus_ops *ops; }; @@ -93,9 +106,11 @@ struct brcmf_bus { /* * callback wrappers */ -static inline int brcmf_bus_init(struct brcmf_bus *bus) +static inline int brcmf_bus_preinit(struct brcmf_bus *bus) { - return bus->ops->init(bus->dev); + if (!bus->ops->preinit) + return 0; + return bus->ops->preinit(bus->dev); } static inline void brcmf_bus_stop(struct brcmf_bus *bus) @@ -128,39 +143,58 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) return bus->ops->gettxq(bus->dev); } + +static inline bool brcmf_bus_ready(struct brcmf_bus *bus) +{ + return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; +} + +static inline void brcmf_bus_change_state(struct brcmf_bus *bus, + enum brcmf_bus_state new_state) +{ + /* NOMEDIUM is permanent */ + if (bus->state == BRCMF_BUS_NOMEDIUM) + return; + + brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); + bus->state = new_state; +} + /* * interface functions from common layer */ -extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, - struct sk_buff *pkt, int prec); +bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, + int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ -extern void brcmf_rx_frames(struct device *dev, struct sk_buff_head *rxlist); +void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); /* Indication from bus module regarding presence/insertion of dongle. */ -extern int brcmf_attach(uint bus_hdrlen, struct device *dev); +int brcmf_attach(struct device *dev); /* Indication from bus module regarding removal/absence of dongle */ -extern void brcmf_detach(struct device *dev); +void brcmf_detach(struct device *dev); /* Indication from bus module that dongle should be reset */ -extern void brcmf_dev_reset(struct device *dev); +void brcmf_dev_reset(struct device *dev); /* Indication from bus module to change flow-control state */ -extern void brcmf_txflowblock(struct device *dev, bool state); +void brcmf_txflowblock(struct device *dev, bool state); /* Notify the bus has transferred the tx packet to firmware */ -extern void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, - bool success); +void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); -extern int brcmf_bus_start(struct device *dev); +int brcmf_bus_start(struct device *dev); +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, + u32 len); +void brcmf_bus_add_txhdrlen(struct device *dev, uint len); #ifdef CONFIG_BRCMFMAC_SDIO -extern void brcmf_sdio_exit(void); -extern void brcmf_sdio_init(void); -extern void brcmf_sdio_register(void); +void brcmf_sdio_exit(void); +void brcmf_sdio_init(void); +void brcmf_sdio_register(void); #endif #ifdef CONFIG_BRCMFMAC_USB -extern void brcmf_usb_exit(void); -extern void brcmf_usb_register(void); +void brcmf_usb_exit(void); +void brcmf_usb_register(void); #endif #endif /* _BRCMF_BUS_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c deleted file mode 100644 index dd85401063c..00000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * 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. - */ - -/******************************************************************************* - * Communicates with the dongle by using dcmd codes. - * For certain dcmd codes, the dongle interprets string data from the host. - ******************************************************************************/ - -#include <linux/types.h> -#include <linux/netdevice.h> - -#include <brcmu_utils.h> -#include <brcmu_wifi.h> - -#include "dhd.h" -#include "dhd_proto.h" -#include "dhd_bus.h" -#include "fwsignal.h" -#include "dhd_dbg.h" -#include "tracepoint.h" - -struct brcmf_proto_cdc_dcmd { - __le32 cmd; /* dongle command value */ - __le32 len; /* lower 16: output buflen; - * upper 16: input buflen (excludes header) */ - __le32 flags; /* flag defns given below */ - __le32 status; /* status code returned from the device */ -}; - -/* Max valid buffer size that can be sent to the dongle */ -#define CDC_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) - -/* CDC flag definitions */ -#define CDC_DCMD_ERROR 0x01 /* 1=cmd failed */ -#define CDC_DCMD_SET 0x02 /* 0=get, 1=set cmd */ -#define CDC_DCMD_IF_MASK 0xF000 /* I/F index */ -#define CDC_DCMD_IF_SHIFT 12 -#define CDC_DCMD_ID_MASK 0xFFFF0000 /* id an cmd pairing */ -#define CDC_DCMD_ID_SHIFT 16 /* ID Mask shift bits */ -#define CDC_DCMD_ID(flags) \ - (((flags) & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT) - -/* - * BDC header - Broadcom specific extension of CDC. - * Used on data packets to convey priority across USB. - */ -#define BDC_HEADER_LEN 4 -#define BDC_PROTO_VER 2 /* Protocol version */ -#define BDC_FLAG_VER_MASK 0xf0 /* Protocol version mask */ -#define BDC_FLAG_VER_SHIFT 4 /* Protocol version shift */ -#define BDC_FLAG_SUM_GOOD 0x04 /* Good RX checksums */ -#define BDC_FLAG_SUM_NEEDED 0x08 /* Dongle needs to do TX checksums */ -#define BDC_PRIORITY_MASK 0x7 -#define BDC_FLAG2_IF_MASK 0x0f /* packet rx interface in APSTA */ -#define BDC_FLAG2_IF_SHIFT 0 - -#define BDC_GET_IF_IDX(hdr) \ - ((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT)) -#define BDC_SET_IF_IDX(hdr, idx) \ - ((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \ - ((idx) << BDC_FLAG2_IF_SHIFT))) - -/** - * struct brcmf_proto_bdc_header - BDC header format - * - * @flags: flags contain protocol and checksum info. - * @priority: 802.1d priority and USB flow control info (bit 4:7). - * @flags2: additional flags containing dongle interface index. - * @data_offset: start of packet data. header is following by firmware signals. - */ -struct brcmf_proto_bdc_header { - u8 flags; - u8 priority; - u8 flags2; - u8 data_offset; -}; - -/* - * maximum length of firmware signal data between - * the BDC header and packet data in the tx path. - */ -#define BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES 12 - -#define RETRIES 2 /* # of retries to retrieve matching dcmd response */ -#define BUS_HEADER_LEN (16+64) /* Must be atleast SDPCM_RESERVE - * (amount of header tha might be added) - * plus any space that might be needed - * for bus alignment padding. - */ -#define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for - * round off at the end of buffer - * Currently is SDIO - */ - -struct brcmf_proto { - u16 reqid; - u8 bus_header[BUS_HEADER_LEN]; - struct brcmf_proto_cdc_dcmd msg; - unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; -}; - -static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr) -{ - struct brcmf_proto *prot = drvr->prot; - int len = le32_to_cpu(prot->msg.len) + - sizeof(struct brcmf_proto_cdc_dcmd); - - brcmf_dbg(CDC, "Enter\n"); - - /* NOTE : cdc->msg.len holds the desired length of the buffer to be - * returned. Only up to CDC_MAX_MSG_SIZE of this buffer area - * is actually sent to the dongle - */ - if (len > CDC_MAX_MSG_SIZE) - len = CDC_MAX_MSG_SIZE; - - /* Send request */ - return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&prot->msg, len); -} - -static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) -{ - int ret; - struct brcmf_proto *prot = drvr->prot; - - brcmf_dbg(CDC, "Enter\n"); - len += sizeof(struct brcmf_proto_cdc_dcmd); - do { - ret = brcmf_bus_rxctl(drvr->bus_if, (unsigned char *)&prot->msg, - len); - if (ret < 0) - break; - } while (CDC_DCMD_ID(le32_to_cpu(prot->msg.flags)) != id); - - return ret; -} - -int -brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len) -{ - struct brcmf_proto *prot = drvr->prot; - struct brcmf_proto_cdc_dcmd *msg = &prot->msg; - void *info; - int ret = 0, retries = 0; - u32 id, flags; - - brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); - - memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); - - msg->cmd = cpu_to_le32(cmd); - msg->len = cpu_to_le32(len); - flags = (++prot->reqid << CDC_DCMD_ID_SHIFT); - flags = (flags & ~CDC_DCMD_IF_MASK) | - (ifidx << CDC_DCMD_IF_SHIFT); - msg->flags = cpu_to_le32(flags); - - if (buf) - memcpy(prot->buf, buf, len); - - ret = brcmf_proto_cdc_msg(drvr); - if (ret < 0) { - brcmf_err("brcmf_proto_cdc_msg failed w/status %d\n", - ret); - goto done; - } - -retry: - /* wait for interrupt and get first fragment */ - ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); - if (ret < 0) - goto done; - - flags = le32_to_cpu(msg->flags); - id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; - - if ((id < prot->reqid) && (++retries < RETRIES)) - goto retry; - if (id != prot->reqid) { - brcmf_err("%s: unexpected request id %d (expected %d)\n", - brcmf_ifname(drvr, ifidx), id, prot->reqid); - ret = -EINVAL; - goto done; - } - - /* Check info buffer */ - info = (void *)&msg[1]; - - /* Copy info buffer */ - if (buf) { - if (ret < (int)len) - len = ret; - memcpy(buf, info, len); - } - - /* Check the ERROR flag */ - if (flags & CDC_DCMD_ERROR) - ret = le32_to_cpu(msg->status); - -done: - return ret; -} - -int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len) -{ - struct brcmf_proto *prot = drvr->prot; - struct brcmf_proto_cdc_dcmd *msg = &prot->msg; - int ret = 0; - u32 flags, id; - - brcmf_dbg(CDC, "Enter, cmd %d len %d\n", cmd, len); - - memset(msg, 0, sizeof(struct brcmf_proto_cdc_dcmd)); - - msg->cmd = cpu_to_le32(cmd); - msg->len = cpu_to_le32(len); - flags = (++prot->reqid << CDC_DCMD_ID_SHIFT) | CDC_DCMD_SET; - flags = (flags & ~CDC_DCMD_IF_MASK) | - (ifidx << CDC_DCMD_IF_SHIFT); - msg->flags = cpu_to_le32(flags); - - if (buf) - memcpy(prot->buf, buf, len); - - ret = brcmf_proto_cdc_msg(drvr); - if (ret < 0) - goto done; - - ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len); - if (ret < 0) - goto done; - - flags = le32_to_cpu(msg->flags); - id = (flags & CDC_DCMD_ID_MASK) >> CDC_DCMD_ID_SHIFT; - - if (id != prot->reqid) { - brcmf_err("%s: unexpected request id %d (expected %d)\n", - brcmf_ifname(drvr, ifidx), id, prot->reqid); - ret = -EINVAL; - goto done; - } - - /* Check the ERROR flag */ - if (flags & CDC_DCMD_ERROR) - ret = le32_to_cpu(msg->status); - -done: - return ret; -} - -static bool pkt_sum_needed(struct sk_buff *skb) -{ - return skb->ip_summed == CHECKSUM_PARTIAL; -} - -static void pkt_set_sum_good(struct sk_buff *skb, bool x) -{ - skb->ip_summed = (x ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE); -} - -void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, - struct sk_buff *pktbuf) -{ - struct brcmf_proto_bdc_header *h; - - brcmf_dbg(CDC, "Enter\n"); - - /* Push BDC header used to convey priority for buses that don't */ - skb_push(pktbuf, BDC_HEADER_LEN); - - h = (struct brcmf_proto_bdc_header *)(pktbuf->data); - - h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT); - if (pkt_sum_needed(pktbuf)) - h->flags |= BDC_FLAG_SUM_NEEDED; - - h->priority = (pktbuf->priority & BDC_PRIORITY_MASK); - h->flags2 = 0; - h->data_offset = offset; - BDC_SET_IF_IDX(h, ifidx); - trace_brcmf_bdchdr(pktbuf->data); -} - -int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *pktbuf) -{ - struct brcmf_proto_bdc_header *h; - - brcmf_dbg(CDC, "Enter\n"); - - /* Pop BDC header used to convey priority for buses that don't */ - - if (pktbuf->len <= BDC_HEADER_LEN) { - brcmf_dbg(INFO, "rx data too short (%d <= %d)\n", - pktbuf->len, BDC_HEADER_LEN); - return -EBADE; - } - - trace_brcmf_bdchdr(pktbuf->data); - h = (struct brcmf_proto_bdc_header *)(pktbuf->data); - - *ifidx = BDC_GET_IF_IDX(h); - if (*ifidx >= BRCMF_MAX_IFS) { - brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); - return -EBADE; - } - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (*ifidx) - (*ifidx)++; - - if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) != - BDC_PROTO_VER) { - brcmf_err("%s: non-BDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); - return -EBADE; - } - - if (h->flags & BDC_FLAG_SUM_GOOD) { - brcmf_dbg(CDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); - pkt_set_sum_good(pktbuf, true); - } - - pktbuf->priority = h->priority & BDC_PRIORITY_MASK; - - skb_pull(pktbuf, BDC_HEADER_LEN); - if (do_fws) - brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf); - else - skb_pull(pktbuf, h->data_offset << 2); - - if (pktbuf->len == 0) - return -ENODATA; - return 0; -} - -int brcmf_proto_attach(struct brcmf_pub *drvr) -{ - struct brcmf_proto *cdc; - - cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC); - if (!cdc) - goto fail; - - /* ensure that the msg buf directly follows the cdc msg struct */ - if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) { - brcmf_err("struct brcmf_proto is not correctly defined\n"); - goto fail; - } - - drvr->prot = cdc; - drvr->hdrlen += BDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; - drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + - sizeof(struct brcmf_proto_cdc_dcmd) + ROUND_UP_MARGIN; - return 0; - -fail: - kfree(cdc); - return -ENOMEM; -} - -/* ~NOTE~ What if another thread is waiting on the semaphore? Holding it? */ -void brcmf_proto_detach(struct brcmf_pub *drvr) -{ - kfree(drvr->prot); - drvr->prot = NULL; -} - -void brcmf_proto_stop(struct brcmf_pub *drvr) -{ - /* Nothing to do for CDC */ -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 9431af2465f..ed3e32ce8c2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -21,9 +21,9 @@ #include <brcmu_utils.h> #include "dhd.h" #include "dhd_bus.h" -#include "dhd_proto.h" #include "dhd_dbg.h" #include "fwil.h" +#include "fwil_types.h" #include "tracepoint.h" #define PKTFILTER_BUF_SIZE 128 @@ -32,14 +32,8 @@ #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 #define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" -#ifdef DEBUG -static const char brcmf_version[] = - "Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on " - __DATE__ " at " __TIME__; -#else -static const char brcmf_version[] = - "Dongle Host Driver, version " BRCMF_VERSION_STR; -#endif +/* boost value for RSSI_DELTA in preferred join selection */ +#define BRCMF_JOIN_PREF_RSSI_BOOST 8 bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, @@ -255,10 +249,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; u8 buf[BRCMF_DCMD_SMLEN]; + struct brcmf_join_pref_params join_pref_params[2]; char *ptr; s32 err; - struct brcmf_bus_dcmd *cmdlst; - struct list_head *cur, *q; /* retreive mac address */ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, @@ -281,9 +274,14 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) } ptr = (char *)buf; strsep(&ptr, "\n"); + /* Print fw version info */ brcmf_err("Firmware version = %s\n", buf); + /* locate firmware version number for ethtool */ + ptr = strrchr(buf, ' ') + 1; + strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); + /* * Setup timeout if Beacons are lost and roam is off to report * link down @@ -304,6 +302,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ + join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; + join_pref_params[0].len = 2; + join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; + join_pref_params[0].band = WLC_BAND_5G; + join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; + join_pref_params[1].len = 2; + join_pref_params[1].rssi_gain = 0; + join_pref_params[1].band = 0; + err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, + sizeof(join_pref_params)); + if (err) + brcmf_err("Set join_pref error (%d)\n", err); + /* Setup event_msgs, enable E_IF */ err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN); @@ -342,17 +354,8 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) brcmf_c_pktfilter_offload_enable(ifp, BRCMF_DEFAULT_PACKET_FILTER, 0, true); - /* set bus specific command if there is any */ - list_for_each_safe(cur, q, &ifp->drvr->bus_if->dcmd_list) { - cmdlst = list_entry(cur, struct brcmf_bus_dcmd, list); - if (cmdlst->name && cmdlst->param && cmdlst->param_len) { - brcmf_fil_iovar_data_set(ifp, cmdlst->name, - cmdlst->param, - cmdlst->param_len); - } - list_del(cur); - kfree(cmdlst); - } + /* do bus specific preinit here */ + err = brcmf_bus_preinit(ifp->drvr->bus_if); done: return err; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 0f9e9057e7d..03fe8aca4d3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -22,7 +22,6 @@ #include "dhd.h" #include "dhd_bus.h" #include "dhd_dbg.h" -#include "tracepoint.h" static struct dentry *root_folder; @@ -42,6 +41,40 @@ void brcmf_debugfs_exit(void) root_folder = NULL; } +static +ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data, + size_t count, loff_t *ppos) +{ + struct brcmf_pub *drvr = f->private_data; + struct brcmf_bus *bus = drvr->bus_if; + char buf[40]; + int res; + + /* only allow read from start */ + if (*ppos > 0) + return 0; + + res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n", + bus->chip, bus->chip, bus->chiprev); + return simple_read_from_buffer(data, count, ppos, buf, res); +} + +static const struct file_operations brcmf_debugfs_chipinfo_ops = { + .owner = THIS_MODULE, + .open = simple_open, + .read = brcmf_debugfs_chipinfo_read +}; + +static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr) +{ + struct dentry *dentry = drvr->dbgfs_dir; + + if (!IS_ERR_OR_NULL(dentry)) + debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr, + &brcmf_debugfs_chipinfo_ops); + return 0; +} + int brcmf_debugfs_attach(struct brcmf_pub *drvr) { struct device *dev = drvr->bus_if->dev; @@ -50,6 +83,7 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr) return -ENODEV; drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); + brcmf_debugfs_create_chipinfo(drvr); return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 0af1f5dc583..ef52ed7abc6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -33,7 +33,7 @@ #define BRCMF_USB_VAL 0x00002000 #define BRCMF_SCAN_VAL 0x00004000 #define BRCMF_CONN_VAL 0x00008000 -#define BRCMF_CDC_VAL 0x00010000 +#define BRCMF_BCDC_VAL 0x00010000 #define BRCMF_SDIO_VAL 0x00020000 /* set default print format */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 40e7f854e10..09dd8c13d84 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -24,13 +24,13 @@ #include "dhd.h" #include "dhd_bus.h" -#include "dhd_proto.h" #include "dhd_dbg.h" #include "fwil_types.h" #include "p2p.h" #include "wl_cfg80211.h" #include "fwil.h" #include "fwsignal.h" +#include "proto.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -190,7 +190,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, int ret; struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - struct ethhdr *eh; + struct ethhdr *eh = (struct ethhdr *)(skb->data); brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); @@ -236,6 +236,9 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } + if (eh->h_proto == htons(ETH_P_PAE)) + atomic_inc(&ifp->pend_8021x_cnt); + ret = brcmf_fws_process_skb(ifp, skb); done: @@ -509,9 +512,8 @@ netif_rx: } } -void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) +void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) { - struct sk_buff *skb, *pnext; struct brcmf_if *ifp; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; @@ -519,56 +521,46 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) u8 ifidx; int ret; - brcmf_dbg(DATA, "Enter: %s: count=%u\n", dev_name(dev), - skb_queue_len(skb_list)); - - skb_queue_walk_safe(skb_list, skb, pnext) { - skb_unlink(skb, skb_list); + brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); - /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); - ifp = drvr->iflist[ifidx]; - - if (ret || !ifp || !ifp->ndev) { - if ((ret != -ENODATA) && ifp) - ifp->stats.rx_errors++; - brcmu_pkt_buf_free_skb(skb); - continue; - } + /* process and remove protocol-specific header */ + ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); + ifp = drvr->iflist[ifidx]; - rd = (struct brcmf_skb_reorder_data *)skb->cb; - if (rd->reorder) - brcmf_rxreorder_process_info(ifp, rd->reorder, skb); - else - brcmf_netif_rx(ifp, skb); + if (ret || !ifp || !ifp->ndev) { + if ((ret != -ENODATA) && ifp) + ifp->stats.rx_errors++; + brcmu_pkt_buf_free_skb(skb); + return; } + + rd = (struct brcmf_skb_reorder_data *)skb->cb; + if (rd->reorder) + brcmf_rxreorder_process_info(ifp, rd->reorder, skb); + else + brcmf_netif_rx(ifp, skb); } -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success) { struct brcmf_if *ifp; struct ethhdr *eh; - u8 ifidx; u16 type; - int res; - - res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp); ifp = drvr->iflist[ifidx]; if (!ifp) goto done; - if (res == 0) { - eh = (struct ethhdr *)(txp->data); - type = ntohs(eh->h_proto); + eh = (struct ethhdr *)(txp->data); + type = ntohs(eh->h_proto); - if (type == ETH_P_PAE) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } + if (type == ETH_P_PAE) { + atomic_dec(&ifp->pend_8021x_cnt); + if (waitqueue_active(&ifp->pend_8021x_wait)) + wake_up(&ifp->pend_8021x_wait); } + if (!success) ifp->stats.tx_errors++; done: @@ -579,13 +571,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; + u8 ifidx; /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(drvr->fws)) { if (!success) brcmf_fws_bustxfail(drvr->fws, txp); } else { - brcmf_txfinalize(drvr, txp, success); + if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp)) + brcmu_pkt_buf_free_skb(txp); + else + brcmf_txfinalize(drvr, txp, ifidx, success); } } @@ -598,28 +594,6 @@ static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) return &ifp->stats; } -/* - * Set current toe component enables in toe_ol iovar, - * and set toe global enable iovar - */ -static int brcmf_toe_set(struct brcmf_if *ifp, u32 toe_ol) -{ - s32 err; - - err = brcmf_fil_iovar_int_set(ifp, "toe_ol", toe_ol); - if (err < 0) { - brcmf_err("Setting toe_ol failed, %d\n", err); - return err; - } - - err = brcmf_fil_iovar_int_set(ifp, "toe", (toe_ol != 0)); - if (err < 0) - brcmf_err("Setting toe failed, %d\n", err); - - return err; - -} - static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info) { @@ -627,8 +601,8 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, struct brcmf_pub *drvr = ifp->drvr; strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - snprintf(info->version, sizeof(info->version), "%lu", - drvr->drv_version); + snprintf(info->version, sizeof(info->version), "n/a"); + strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), sizeof(info->bus_info)); } @@ -637,124 +611,6 @@ static const struct ethtool_ops brcmf_ethtool_ops = { .get_drvinfo = brcmf_ethtool_get_drvinfo, }; -static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr) -{ - struct brcmf_pub *drvr = ifp->drvr; - struct ethtool_drvinfo info; - char drvname[sizeof(info.driver)]; - u32 cmd; - struct ethtool_value edata; - u32 toe_cmpnt, csum_dir; - int ret; - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - /* all ethtool calls start with a cmd word */ - if (copy_from_user(&cmd, uaddr, sizeof(u32))) - return -EFAULT; - - switch (cmd) { - case ETHTOOL_GDRVINFO: - /* Copy out any request driver name */ - if (copy_from_user(&info, uaddr, sizeof(info))) - return -EFAULT; - strncpy(drvname, info.driver, sizeof(info.driver)); - drvname[sizeof(info.driver) - 1] = '\0'; - - /* clear struct for return */ - memset(&info, 0, sizeof(info)); - info.cmd = cmd; - - /* if requested, identify ourselves */ - if (strcmp(drvname, "?dhd") == 0) { - sprintf(info.driver, "dhd"); - strcpy(info.version, BRCMF_VERSION_STR); - } - /* report dongle driver type */ - else - sprintf(info.driver, "wl"); - - sprintf(info.version, "%lu", drvr->drv_version); - if (copy_to_user(uaddr, &info, sizeof(info))) - return -EFAULT; - brcmf_dbg(TRACE, "given %*s, returning %s\n", - (int)sizeof(drvname), drvname, info.driver); - break; - - /* Get toe offload components from dongle */ - case ETHTOOL_GRXCSUM: - case ETHTOOL_GTXCSUM: - ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt); - if (ret < 0) - return ret; - - csum_dir = - (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; - - edata.cmd = cmd; - edata.data = (toe_cmpnt & csum_dir) ? 1 : 0; - - if (copy_to_user(uaddr, &edata, sizeof(edata))) - return -EFAULT; - break; - - /* Set toe offload components in dongle */ - case ETHTOOL_SRXCSUM: - case ETHTOOL_STXCSUM: - if (copy_from_user(&edata, uaddr, sizeof(edata))) - return -EFAULT; - - /* Read the current settings, update and write back */ - ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt); - if (ret < 0) - return ret; - - csum_dir = - (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL; - - if (edata.data != 0) - toe_cmpnt |= csum_dir; - else - toe_cmpnt &= ~csum_dir; - - ret = brcmf_toe_set(ifp, toe_cmpnt); - if (ret < 0) - return ret; - - /* If setting TX checksum mode, tell Linux the new mode */ - if (cmd == ETHTOOL_STXCSUM) { - if (edata.data) - ifp->ndev->features |= NETIF_F_IP_CSUM; - else - ifp->ndev->features &= ~NETIF_F_IP_CSUM; - } - - break; - - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr, - int cmd) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - - brcmf_dbg(TRACE, "Enter, idx=%d, cmd=0x%04x\n", ifp->bssidx, cmd); - - if (!drvr->iflist[ifp->bssidx]) - return -1; - - if (cmd == SIOCETHTOOL) - return brcmf_ethtool(ifp, ifr->ifr_data); - - return -EOPNOTSUPP; -} - static int brcmf_netdev_stop(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); @@ -775,7 +631,6 @@ static int brcmf_netdev_open(struct net_device *ndev) struct brcmf_pub *drvr = ifp->drvr; struct brcmf_bus *bus_if = drvr->bus_if; u32 toe_ol; - s32 ret = 0; brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); @@ -794,21 +649,20 @@ static int brcmf_netdev_open(struct net_device *ndev) else ndev->features &= ~NETIF_F_IP_CSUM; - /* Allow transmit calls */ - netif_start_queue(ndev); if (brcmf_cfg80211_up(ndev)) { brcmf_err("failed to bring up cfg80211\n"); - return -1; + return -EIO; } - return ret; + /* Allow transmit calls */ + netif_start_queue(ndev); + return 0; } static const struct net_device_ops brcmf_netdev_ops_pri = { .ndo_open = brcmf_netdev_open, .ndo_stop = brcmf_netdev_stop, .ndo_get_stats = brcmf_netdev_get_stats, - .ndo_do_ioctl = brcmf_netdev_ioctl_entry, .ndo_start_xmit = brcmf_netdev_start_xmit, .ndo_set_mac_address = brcmf_netdev_set_mac_address, .ndo_set_rx_mode = brcmf_netdev_set_multicast_list @@ -850,7 +704,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - ndev->destructor = free_netdev; + ndev->destructor = brcmf_cfg80211_free_netdev; return 0; fail: @@ -874,13 +728,6 @@ static int brcmf_net_p2p_stop(struct net_device *ndev) return brcmf_cfg80211_down(ndev); } -static int brcmf_net_p2p_do_ioctl(struct net_device *ndev, - struct ifreq *ifr, int cmd) -{ - brcmf_dbg(TRACE, "Enter\n"); - return 0; -} - static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb, struct net_device *ndev) { @@ -893,7 +740,6 @@ static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb, static const struct net_device_ops brcmf_netdev_ops_p2p = { .ndo_open = brcmf_net_p2p_open, .ndo_stop = brcmf_net_p2p_stop, - .ndo_do_ioctl = brcmf_net_p2p_do_ioctl, .ndo_start_xmit = brcmf_net_p2p_start_xmit }; @@ -1015,14 +861,12 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) } /* unregister will take care of freeing it */ unregister_netdev(ifp->ndev); - if (bssidx == 0) - brcmf_cfg80211_detach(drvr->config); } else { kfree(ifp); } } -int brcmf_attach(uint bus_hdrlen, struct device *dev) +int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; int ret = 0; @@ -1037,7 +881,7 @@ int brcmf_attach(uint bus_hdrlen, struct device *dev) mutex_init(&drvr->proto_block); /* Link to bus module */ - drvr->hdrlen = bus_hdrlen; + drvr->hdrlen = 0; drvr->bus_if = dev_get_drvdata(dev); drvr->bus_if->drvr = drvr; @@ -1054,8 +898,6 @@ int brcmf_attach(uint bus_hdrlen, struct device *dev) /* attach firmware event handler */ brcmf_fweh_attach(drvr); - INIT_LIST_HEAD(&drvr->bus_if->dcmd_list); - return ret; fail: @@ -1074,13 +916,6 @@ int brcmf_bus_start(struct device *dev) brcmf_dbg(TRACE, "\n"); - /* Bring up the bus */ - ret = brcmf_bus_init(bus_if); - if (ret != 0) { - brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret); - return ret; - } - /* add primary networking interface */ ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); if (IS_ERR(ifp)) @@ -1094,7 +929,7 @@ int brcmf_bus_start(struct device *dev) p2p_ifp = NULL; /* signal bus ready */ - bus_if->state = BRCMF_BUS_DATA; + brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA); /* Bus is ready, do any initialization */ ret = brcmf_c_preinit_dcmds(ifp); @@ -1121,8 +956,7 @@ int brcmf_bus_start(struct device *dev) fail: if (ret < 0) { brcmf_err("failed: %d\n", ret); - if (drvr->config) - brcmf_cfg80211_detach(drvr->config); + brcmf_cfg80211_detach(drvr->config); if (drvr->fws) { brcmf_fws_del_interface(ifp); brcmf_fws_deinit(drvr); @@ -1144,14 +978,21 @@ fail: return 0; } +void brcmf_bus_add_txhdrlen(struct device *dev, uint len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + if (drvr) { + drvr->hdrlen += len; + } +} + static void brcmf_bus_detach(struct brcmf_pub *drvr) { brcmf_dbg(TRACE, "Enter\n"); if (drvr) { - /* Stop the protocol module */ - brcmf_proto_stop(drvr); - /* Stop the bus module */ brcmf_bus_stop(drvr->bus_if); } @@ -1183,6 +1024,8 @@ void brcmf_detach(struct device *dev) /* stop firmware event handling */ brcmf_fweh_detach(drvr); + brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); + /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) if (drvr->iflist[i]) { @@ -1190,18 +1033,27 @@ void brcmf_detach(struct device *dev) brcmf_del_if(drvr, i); } - brcmf_bus_detach(drvr); - - if (drvr->prot) - brcmf_proto_detach(drvr); + brcmf_cfg80211_detach(drvr->config); brcmf_fws_deinit(drvr); + brcmf_bus_detach(drvr); + + brcmf_proto_detach(drvr); + brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; kfree(drvr); } +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_if *ifp = bus_if->drvr->iflist[0]; + + return brcmf_fil_iovar_data_set(ifp, name, data, len); +} + static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) { return atomic_read(&ifp->pend_8021x_cnt); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h deleted file mode 100644 index ef917988374..00000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * 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 _BRCMF_PROTO_H_ -#define _BRCMF_PROTO_H_ - -/* - * Exported from the brcmf protocol module (brcmf_cdc) - */ - -/* Linkage, sets prot link and updates hdrlen in pub */ -extern int brcmf_proto_attach(struct brcmf_pub *drvr); - -/* Unlink, frees allocated protocol memory (including brcmf_proto) */ -extern void brcmf_proto_detach(struct brcmf_pub *drvr); - -/* Stop protocol: sync w/dongle state. */ -extern void brcmf_proto_stop(struct brcmf_pub *drvr); - -/* Add any protocol-specific data header. - * Caller must reserve prot_hdrlen prepend space. - */ -extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, u8 offset, - struct sk_buff *txp); - -/* Sets dongle media info (drv_version, mac address). */ -extern int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); - -#endif /* _BRCMF_PROTO_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1aa75d5951b..8fa0dbbbda7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> #include <linux/semaphore.h> @@ -32,6 +33,7 @@ #include <linux/debugfs.h> #include <linux/vmalloc.h> #include <linux/platform_data/brcmfmac-sdio.h> +#include <linux/moduleparam.h> #include <asm/unaligned.h> #include <defs.h> #include <brcmu_wifi.h> @@ -39,7 +41,8 @@ #include <brcm_hw_ids.h> #include <soc.h> #include "sdio_host.h" -#include "sdio_chip.h" +#include "chip.h" +#include "firmware.h" #define DCMD_RESP_TIMEOUT 2000 /* In milli second */ @@ -152,6 +155,34 @@ struct rte_console { /* manfid tuple length, include tuple, link bytes */ #define SBSDIO_CIS_MANFID_TUPLE_LEN 6 +#define CORE_BUS_REG(base, field) \ + (base + offsetof(struct sdpcmd_regs, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP 0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT 0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 +/* Status: ALP is ready */ +#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) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ + (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) + /* intstatus */ #define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ #define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ @@ -257,9 +288,6 @@ struct rte_console { #define MAX_HDR_READ (1 << 6) #define MAX_RX_DATASZ 2048 -/* Maximum milliseconds to wait for F2 to come up */ -#define BRCMF_WAIT_F2RDY 3000 - /* Bump up limit on waiting for HT to account for first startup; * if the image is doing a CRC calculation before programming the PMU * for HT availability, it could take a couple hundred ms more, so @@ -275,12 +303,6 @@ struct rte_console { /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -#define BRCMF_SDIO_FW_NAME "brcm/brcmfmac-sdio.bin" -#define BRCMF_SDIO_NV_NAME "brcm/brcmfmac-sdio.txt" -MODULE_FIRMWARE(BRCMF_SDIO_FW_NAME); -MODULE_FIRMWARE(BRCMF_SDIO_NV_NAME); - -#define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */ #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle */ @@ -365,15 +387,15 @@ struct brcmf_sdio_hdrinfo { u16 len_left; u16 len_nxtfrm; u8 dat_offset; + bool lastfrm; + u16 tail_pad; }; /* misc chip info needed by some of the routines */ /* Private data for SDIO bus interaction */ struct brcmf_sdio { struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ - struct chip_info *ci; /* Chip info struct */ - char *vars; /* Variables (from CIS and/or other) */ - uint varsz; /* Size of variables buffer */ + struct brcmf_chip *ci; /* Chip info struct */ u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ @@ -389,7 +411,7 @@ struct brcmf_sdio { u8 tx_seq; /* Transmit sequence number (next) */ u8 tx_max; /* Maximum transmit sequence allowed */ - u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN]; + u8 *hdrbuf; /* buffer for handling rx frame */ u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ u8 rx_seq; /* Receive sequence number (expected) */ struct brcmf_sdio_hdrinfo cur_read; @@ -437,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; @@ -454,15 +477,15 @@ struct brcmf_sdio { struct work_struct datawork; atomic_t dpc_tskcnt; - const struct firmware *firmware; - u32 fw_ptr; - bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ bool sleeping; /* SDIO bus sleeping */ u8 tx_hdrlen; /* sdio bus header length for tx packet */ + bool txglom; /* host tx glomming enable flag */ + u16 head_align; /* buffer pointer alignment */ + u16 sgentry_align; /* scatter-gather buffer alignment */ }; /* clkstate */ @@ -493,6 +516,146 @@ enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_SUB, }; +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { + u8 strength; /* Pad Drive Strength in mA */ + u8 sel; /* Chip-specific select value */ +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { + {16, 0x7}, + {12, 0x5}, + {8, 0x3}, + {4, 0x1} +}; + +#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" +#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" +#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" +#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" +#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" +#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" +#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" +#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" +#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" +#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" +#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" +#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" +#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" +#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" +#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); +MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4329_NVRAM_NAME); +MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4330_NVRAM_NAME); +MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4335_NVRAM_NAME); +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; + u32 revmsk; + const char *bin; + const char *nv; +}; + +enum brcmf_firmware_type { + BRCMF_FIRMWARE_BIN, + BRCMF_FIRMWARE_NVRAM +}; + +#define BRCMF_FIRMWARE_NVRAM(name) \ + name ## _FIRMWARE_NAME, name ## _NVRAM_NAME + +static const struct brcmf_firmware_names brcmf_fwname_data[] = { + { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, + { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, + { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, + { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, + { 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) }, + { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } +}; + +static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, + enum brcmf_firmware_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { + if (brcmf_fwname_data[i].chipid == ci->chip && + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { + switch (type) { + case BRCMF_FIRMWARE_BIN: + return brcmf_fwname_data[i].bin; + case BRCMF_FIRMWARE_NVRAM: + return brcmf_fwname_data[i].nv; + default: + brcmf_err("invalid firmware type (%d)\n", type); + return NULL; + } + } + } + brcmf_err("Unknown chipid %d [%d]\n", + ci->chip, ci->chiprev); + return NULL; +} + static void pkt_align(struct sk_buff *p, int len, int align) { uint datalign; @@ -514,48 +677,41 @@ static bool data_ok(struct brcmf_sdio *bus) * Reads a register in the SDIO hardware block. This block occupies a series of * adresses on the 32 bit backplane bus. */ -static int -r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - *regvar = brcmf_sdio_regrl(bus->sdiodev, - bus->ci->c_inf[idx].base + offset, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); return ret; } -static int -w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) { - u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); + struct brcmf_core *core; int ret; - brcmf_sdio_regwl(bus->sdiodev, - bus->ci->c_inf[idx].base + reg_offset, - regval, &ret); + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); return ret; } static int -brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) +brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) { u8 wr_val = 0, rd_val, cmp_val, bmask; 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_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); - if (err) { - brcmf_err("SDIO_AOS KSO write error: %d\n", err); - return err; - } + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); if (on) { /* device WAKEUP through KSO: @@ -581,26 +737,30 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) * just one write attempt may fail, * read it back until it matches written value */ - rd_val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - &err); + rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + &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_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); + 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; } -#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND) - #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) /* Turn backplane clock on or off */ -static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) +static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) { int err; u8 clkctl, clkreq, devctl; @@ -620,16 +780,16 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); if (err) { brcmf_err("HT Avail request error: %d\n", err); return -EBADE; } /* Check current status */ - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err) { brcmf_err("HT Avail read error: %d\n", err); return -EBADE; @@ -638,8 +798,8 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) /* Go to pending and await interrupt if appropriate */ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { /* Allow only clock-available interrupt */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_err("Devctl error setting CA: %d\n", err); @@ -647,28 +807,28 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); bus->clkstate = CLK_PENDING; return 0; } else if (bus->clkstate == CLK_PENDING) { /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); } /* Otherwise, wait here (polling) for HT Avail */ timeout = jiffies + msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); if (time_after(jiffies, timeout)) break; else @@ -695,22 +855,21 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } #endif /* defined (DEBUG) */ - bus->activity = true; } else { clkreq = 0; if (bus->clkstate == CLK_PENDING) { /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); } bus->clkstate = CLK_SDONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); if (err) { brcmf_err("Failed access turning clock off: %d\n", @@ -722,7 +881,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } /* Change idle/active SD state */ -static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) +static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) { brcmf_dbg(SDIO, "Enter\n"); @@ -735,7 +894,7 @@ static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) } /* Transition SD and backplane clock readiness */ -static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) +static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) { #ifdef DEBUG uint oldstate = bus->clkstate; @@ -746,7 +905,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) /* Early exit if we're already there */ if (bus->clkstate == target) { if (target == CLK_AVAIL) { - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); bus->activity = true; } return 0; @@ -756,32 +915,32 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) case CLK_AVAIL: /* Make sure SD clock is available */ if (bus->clkstate == CLK_NONE) - brcmf_sdbrcm_sdclk(bus, true); + brcmf_sdio_sdclk(bus, true); /* Now request HT Avail on the backplane */ - brcmf_sdbrcm_htclk(bus, true, pendok); - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_htclk(bus, true, pendok); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); bus->activity = true; break; case CLK_SDONLY: /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) - brcmf_sdbrcm_sdclk(bus, true); + brcmf_sdio_sdclk(bus, true); else if (bus->clkstate == CLK_AVAIL) - brcmf_sdbrcm_htclk(bus, false, false); + brcmf_sdio_htclk(bus, false, false); else brcmf_err("request for %d -> %d\n", bus->clkstate, target); - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); break; case CLK_NONE: /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) - brcmf_sdbrcm_htclk(bus, false, false); + brcmf_sdio_htclk(bus, false, false); /* Now remove the SD clock */ - brcmf_sdbrcm_sdclk(bus, false); - brcmf_sdbrcm_wd_timer(bus, 0); + brcmf_sdio_sdclk(bus, false); + brcmf_sdio_wd_timer(bus, 0); break; } #ifdef DEBUG @@ -792,11 +951,12 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) } static int -brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) +brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) { int err = 0; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(SDIO, "request %s currently %s\n", + u8 clkcsr; + + brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); @@ -813,15 +973,27 @@ brcmf_sdbrcm_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; - err = brcmf_sdbrcm_kso_control(bus, false); + 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) - brcmf_sdbrcm_wd_timer(bus, 0); + brcmf_sdio_wd_timer(bus, 0); } else { bus->idlecount = 0; - err = brcmf_sdbrcm_kso_control(bus, true); + err = brcmf_sdio_kso_control(bus, true); } if (!err) { /* Change state */ @@ -831,7 +1003,7 @@ brcmf_sdbrcm_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; } } @@ -839,16 +1011,97 @@ end: /* control clocks */ if (sleep) { if (!bus->sr_enabled) - brcmf_sdbrcm_clkctl(bus, CLK_NONE, pendok); + brcmf_sdio_clkctl(bus, CLK_NONE, pendok); } else { - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, pendok); + brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } - +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; } -static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) +#ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ + return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + shaddr = bus->ci->rambase + bus->ramsize - 4; + + /* + * Read last word in socram to determine + * address of sdpcm_shared structure + */ + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); + sdio_release_host(bus->sdiodev->func[1]); + if (rv < 0) + return rv; + + addr = le32_to_cpu(addr_le); + + brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_err("invalid sdpcm_shared address 0x%08X\n", + addr); + return -EINVAL; + } + + /* Read hndrte_shared structure */ + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + return rv; + + /* Endianness */ + sh->flags = le32_to_cpu(sh_le.flags); + sh->trap_addr = le32_to_cpu(sh_le.trap_addr); + sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); + sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); + sh->assert_line = le32_to_cpu(sh_le.assert_line); + sh->console_addr = le32_to_cpu(sh_le.console_addr); + sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { + brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", + SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK); + return -EPROTO; + } + + return 0; +} + +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ + struct sdpcm_shared sh; + + if (brcmf_sdio_readshared(bus, &sh) == 0) + bus->console_addr = sh.console_addr; +} +#else +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) { u32 intstatus = 0; u32 hmb_data; @@ -891,6 +1144,12 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) else brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", bus->sdpcm_ver); + + /* + * Retrieve console state address now that firmware should have + * updated it. + */ + brcmf_sdio_get_console_addr(bus); } /* @@ -924,7 +1183,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) return intstatus; } -static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) +static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) { uint retries = 0; u16 lastrbc; @@ -936,18 +1195,18 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) rtx ? ", send NAK" : ""); if (abort) - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_RF_TERM, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_RF_TERM, &err); bus->sdcnt.f1regdata++; /* Wait until the packet has been flushed (device/FIFO stable) */ for (lastrbc = retries = 0xffff; retries > 0; retries--) { - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCHI, &err); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCLO, &err); + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCHI, &err); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCLO, &err); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) @@ -977,14 +1236,32 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) /* Clear partial in any case */ bus->cur_read.len = 0; +} - /* If we can't reach the device, signal failure */ - if (err) - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; +static void brcmf_sdio_txfail(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + u8 i, hi, lo; + + /* On failure, abort the command and terminate the frame */ + brcmf_err("sdio error, abort command and terminate frame\n"); + bus->sdcnt.tx_sderrs++; + + brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); + bus->sdcnt.f1regdata++; + + for (i = 0; i < 3; i++) { + hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->sdcnt.f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } } /* return total length of buffer chain */ -static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) +static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) { struct sk_buff *p; uint total; @@ -995,7 +1272,7 @@ static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) return total; } -static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) +static void brcmf_sdio_free_glom(struct brcmf_sdio *bus) { struct sk_buff *cur, *next; @@ -1011,10 +1288,18 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) * host and WiFi dongle which contains information needed for SDIO core and * firmware * - * It consists of 2 parts: hw header and software header + * It consists of 3 parts: hardware header, hardware extension header and + * software header * hardware header (frame tag) - 4 bytes * Byte 0~1: Frame length * Byte 2~3: Checksum, bit-wise inverse of frame length + * hardware extension header - 8 bytes + * Tx glom mode only, N/A for Rx or normal Tx + * Byte 0~1: Packet length excluding hw frame tag + * Byte 2: Reserved + * Byte 3: Frame flags, bit 0: last frame indication + * Byte 4~5: Reserved + * Byte 6~7: Tail padding length * software header - 8 bytes * Byte 0: Rx/Tx sequence number * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag @@ -1025,6 +1310,7 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) * Byte 6~7: Reserved */ #define SDPCM_HWHDR_LEN 4 +#define SDPCM_HWEXT_LEN 8 #define SDPCM_SWHDR_LEN 8 #define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN) /* software header */ @@ -1061,6 +1347,8 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, u8 rx_seq, fc, tx_seq_max; u32 swheader; + trace_brcmf_sdpcm_hdr(SDPCM_RX, header); + /* hw header */ len = get_unaligned_le16(header); checksum = get_unaligned_le16(header + sizeof(u16)); @@ -1072,7 +1360,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, if ((u16)(~(len ^ checksum))) { brcmf_err("HW header checksum error\n"); bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); return -EIO; } if (len < SDPCM_HDRLEN) { @@ -1104,7 +1392,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, type != BRCMF_SDIO_FT_SUPER) { brcmf_err("HW header length too long\n"); bus->sdcnt.rx_toolong++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); rd->len = 0; return -EPROTO; } @@ -1123,7 +1411,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { brcmf_err("seq %d: bad data offset\n", rx_seq); bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); rd->len = 0; return -ENXIO; } @@ -1172,24 +1460,34 @@ static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, struct brcmf_sdio_hdrinfo *hd_info) { - u32 sw_header; + u32 hdrval; + u8 hdr_offset; brcmf_sdio_update_hwhdr(header, hd_info->len); - - sw_header = bus->tx_seq; - sw_header |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & - SDPCM_CHANNEL_MASK; - sw_header |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & - SDPCM_DOFFSET_MASK; - *(((__le32 *)header) + 1) = cpu_to_le32(sw_header); - *(((__le32 *)header) + 2) = 0; + hdr_offset = SDPCM_HWHDR_LEN; + + if (bus->txglom) { + hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); + *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); + hdrval = (u16)hd_info->tail_pad << 16; + *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); + hdr_offset += SDPCM_HWEXT_LEN; + } + + hdrval = hd_info->seq_num; + hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & + SDPCM_CHANNEL_MASK; + hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & + SDPCM_DOFFSET_MASK; + *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); + *(((__le32 *)(header + hdr_offset)) + 1) = 0; + trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); } -static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) +static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) { u16 dlen, totlen; u8 *dptr, num = 0; - u32 align = 0; u16 sublen; struct sk_buff *pfirst, *pnext; @@ -1204,11 +1502,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) brcmf_dbg(SDIO, "start: glomd %p glom %p\n", bus->glomd, skb_peek(&bus->glom)); - if (bus->sdiodev->pdata) - align = bus->sdiodev->pdata->sd_sgentry_align; - if (align < 4) - align = 4; - /* If there's a descriptor, generate the packet chain */ if (bus->glomd) { pfirst = pnext = NULL; @@ -1232,9 +1525,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) pnext = NULL; break; } - if (sublen % align) { + if (sublen % bus->sgentry_align) { brcmf_err("sublen %d not multiple of %d\n", - sublen, align); + sublen, bus->sgentry_align); } totlen += sublen; @@ -1247,7 +1540,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } /* Allocate/chain packet for next subframe */ - pnext = brcmu_pkt_buf_get_skb(sublen + align); + pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); if (pnext == NULL) { brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", num, sublen); @@ -1256,7 +1549,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) skb_queue_tail(&bus->glom, pnext); /* Adhere to start alignment requirements */ - pkt_align(pnext, sublen, align); + pkt_align(pnext, sublen, bus->sgentry_align); } /* If all allocations succeeded, save packet chain @@ -1271,7 +1564,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } pfirst = pnext = NULL; } else { - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); num = 0; } @@ -1294,16 +1587,15 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } pfirst = skb_peek(&bus->glom); - dlen = (u16) brcmf_sdbrcm_glom_len(bus); + dlen = (u16) brcmf_sdio_glom_len(bus); /* Do an SDIO read for the superframe. Configurable iovar to * read directly into the chained packet, or allocate a large * packet and and copy into the chain. */ sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdcard_recv_chain(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, &bus->glom); + errcode = brcmf_sdiod_recv_chain(bus->sdiodev, + &bus->glom, dlen); sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2rxdata++; @@ -1314,12 +1606,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) sdio_claim_host(bus->sdiodev->func[1]); if (bus->glomerr++ < 3) { - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); } else { bus->glomerr = 0; - brcmf_sdbrcm_rxfail(bus, true, false); + brcmf_sdio_rxfail(bus, true, false); bus->sdcnt.rxglomfail++; - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); } sdio_release_host(bus->sdiodev->func[1]); return 0; @@ -1367,12 +1659,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (bus->glomerr++ < 3) { /* Restore superframe header space */ skb_push(pfirst, sfdoff); - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); } else { bus->glomerr = 0; - brcmf_sdbrcm_rxfail(bus, true, false); + brcmf_sdio_rxfail(bus, true, false); bus->sdcnt.rxglomfail++; - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); } sdio_release_host(bus->sdiodev->func[1]); bus->cur_read.len = 0; @@ -1406,19 +1698,18 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) bus->glom.qlen, pfirst, pfirst->data, pfirst->len, pfirst->next, pfirst->prev); + skb_unlink(pfirst, &bus->glom); + brcmf_rx_frame(bus->sdiodev->dev, pfirst); + bus->sdcnt.rxglompkts++; } - /* sent any remaining packets up */ - if (bus->glom.qlen) - brcmf_rx_frames(bus->sdiodev->dev, &bus->glom); bus->sdcnt.rxglomframes++; - bus->sdcnt.rxglompkts += bus->glom.qlen; } return num; } -static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, - bool *pending) +static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, + bool *pending) { DECLARE_WAITQUEUE(wait, current); int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); @@ -1439,7 +1730,7 @@ static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, return timeout; } -static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) +static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->dcmd_resp_wait)) wake_up_interruptible(&bus->dcmd_resp_wait); @@ -1447,7 +1738,7 @@ static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) return 0; } static void -brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) +brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) { uint rdlen, pad; u8 *buf = NULL, *rbuf; @@ -1461,9 +1752,9 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) goto done; rbuf = bus->rxbuf; - pad = ((unsigned long)rbuf % BRCMF_SDALIGN); + pad = ((unsigned long)rbuf % bus->head_align); if (pad) - rbuf += (BRCMF_SDALIGN - pad); + rbuf += (bus->head_align - pad); /* Copy the already-read portion over */ memcpy(buf, hdr, BRCMF_FIRSTREAD); @@ -1477,19 +1768,15 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) if ((pad <= bus->roundup) && (pad < bus->blocksize) && ((len + pad) < bus->sdiodev->bus_if->maxctl)) rdlen += pad; - } else if (rdlen % BRCMF_SDALIGN) { - rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN); + } else if (rdlen % bus->head_align) { + rdlen += bus->head_align - (rdlen % bus->head_align); } - /* Satisfy length-alignment requirements */ - if (rdlen & (ALIGNMENT - 1)) - rdlen = roundup(rdlen, ALIGNMENT); - /* Drop if the read is too big or it exceeds our maximum */ if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { brcmf_err("%d-byte control read exceeds %d-byte buffer\n", rdlen, bus->sdiodev->bus_if->maxctl); - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); goto done; } @@ -1497,15 +1784,12 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", len, len - doff, bus->sdiodev->bus_if->maxctl); bus->sdcnt.rx_toolong++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); goto done; } /* Read remain of frame body */ - sdret = brcmf_sdcard_recv_buf(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, - F2SYNC, rbuf, rdlen); + sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen); bus->sdcnt.f2rxdata++; /* Control frame failures need retransmission */ @@ -1513,7 +1797,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) brcmf_err("read %d control bytes failed: %d\n", rdlen, sdret); bus->sdcnt.rxc_errors++; - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); goto done; } else memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); @@ -1538,26 +1822,25 @@ gotpkt: done: /* Awake any waiters */ - brcmf_sdbrcm_dcmd_resp_wake(bus); + brcmf_sdio_dcmd_resp_wake(bus); } /* Pad read to blocksize for efficiency */ -static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) +static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) { if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { *pad = bus->blocksize - (*rdlen % bus->blocksize); if (*pad <= bus->roundup && *pad < bus->blocksize && *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) *rdlen += *pad; - } else if (*rdlen % BRCMF_SDALIGN) { - *rdlen += BRCMF_SDALIGN - (*rdlen % BRCMF_SDALIGN); + } else if (*rdlen % bus->head_align) { + *rdlen += bus->head_align - (*rdlen % bus->head_align); } } static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) { struct sk_buff *pkt; /* Packet for event or data frames */ - struct sk_buff_head pktlist; /* needed for bus interface */ u16 pad; /* Number of pad bytes to read */ uint rxleft = 0; /* Remaining number of frames allowed */ int ret; /* Return code from calls */ @@ -1571,8 +1854,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxpending = true; for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && - bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; + !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if); rd->seq_num++, rxleft--) { /* Handle glomming separately */ @@ -1580,7 +1862,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) u8 cnt; brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", bus->glomd, skb_peek(&bus->glom)); - cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num); + cnt = brcmf_sdio_rxglom(bus, rd->seq_num); brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); rd->seq_num += cnt - 1; rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; @@ -1591,17 +1873,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) /* read header first for unknow frame length */ sdio_claim_host(bus->sdiodev->func[1]); if (!rd->len) { - ret = brcmf_sdcard_recv_buf(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, - bus->rxhdr, - BRCMF_FIRSTREAD); + ret = brcmf_sdiod_recv_buf(bus->sdiodev, + bus->rxhdr, BRCMF_FIRSTREAD); bus->sdcnt.f2rxhdrs++; if (ret < 0) { brcmf_err("RXHEADER FAILED: %d\n", ret); bus->sdcnt.rx_hdrfail++; - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); sdio_release_host(bus->sdiodev->func[1]); continue; } @@ -1620,9 +1899,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } if (rd->channel == SDPCM_CONTROL_CHANNEL) { - brcmf_sdbrcm_read_control(bus, bus->rxhdr, - rd->len, - rd->dat_offset); + brcmf_sdio_read_control(bus, bus->rxhdr, + rd->len, + rd->dat_offset); /* prepare the descriptor for the next read */ rd->len = rd->len_nxtfrm << 4; rd->len_nxtfrm = 0; @@ -1636,23 +1915,22 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) head_read = BRCMF_FIRSTREAD; } - brcmf_pad(bus, &pad, &rd->len_left); + brcmf_sdio_pad(bus, &pad, &rd->len_left); pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + - BRCMF_SDALIGN); + bus->head_align); if (!pkt) { /* Give up on data, request rtx of events */ brcmf_err("brcmu_pkt_buf_get_skb failed\n"); - brcmf_sdbrcm_rxfail(bus, false, + brcmf_sdio_rxfail(bus, false, RETRYCHAN(rd->channel)); sdio_release_host(bus->sdiodev->func[1]); continue; } skb_pull(pkt, head_read); - pkt_align(pkt, rd->len_left, BRCMF_SDALIGN); + pkt_align(pkt, rd->len_left, bus->head_align); - ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, pkt); + ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt); bus->sdcnt.f2rxdata++; sdio_release_host(bus->sdiodev->func[1]); @@ -1661,7 +1939,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd->len, rd->channel, ret); brcmu_pkt_buf_free_skb(pkt); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_rxfail(bus, true, + brcmf_sdio_rxfail(bus, true, RETRYCHAN(rd->channel)); sdio_release_host(bus->sdiodev->func[1]); continue; @@ -1686,7 +1964,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd->len, roundup(rd_new.len, 16) >> 4); rd->len = 0; - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); sdio_release_host(bus->sdiodev->func[1]); brcmu_pkt_buf_free_skb(pkt); continue; @@ -1708,7 +1986,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) /* Force retry w/normal header read */ rd->len = 0; sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_rxfail(bus, false, true); + brcmf_sdio_rxfail(bus, false, true); sdio_release_host(bus->sdiodev->func[1]); brcmu_pkt_buf_free_skb(pkt); continue; @@ -1733,7 +2011,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) brcmf_err("%s: glom superframe w/o " "descriptor!\n", __func__); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); sdio_release_host(bus->sdiodev->func[1]); } /* prepare the descriptor for the next read */ @@ -1759,9 +2037,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) continue; } - skb_queue_head_init(&pktlist); - skb_queue_tail(&pktlist, pkt); - brcmf_rx_frames(bus->sdiodev->dev, &pktlist); + brcmf_rx_frame(bus->sdiodev->dev, pkt); } rxcount = maxframes - rxleft; @@ -1779,17 +2055,101 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } static void -brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) +brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->ctrl_wait)) wake_up_interruptible(&bus->ctrl_wait); return; } +static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) +{ + u16 head_pad; + u8 *dat_buf; + + dat_buf = (u8 *)(pkt->data); + + /* Check head padding */ + head_pad = ((unsigned long)dat_buf % bus->head_align); + if (head_pad) { + if (skb_headroom(pkt) < head_pad) { + bus->sdiodev->bus_if->tx_realloc++; + head_pad = 0; + if (skb_cow(pkt, head_pad)) + return -ENOMEM; + } + skb_push(pkt, head_pad); + dat_buf = (u8 *)(pkt->data); + memset(dat_buf, 0, head_pad + bus->tx_hdrlen); + } + return head_pad; +} + +/** + * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for + * bus layer usage. + */ /* flag marking a dummy skb added for DMA alignment requirement */ -#define DUMMY_SKB_FLAG 0x10000 +#define ALIGN_SKB_FLAG 0x8000 /* bit mask of data length chopped from the previous packet */ -#define DUMMY_SKB_CHOP_LEN_MASK 0xffff +#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff + +static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, + struct sk_buff_head *pktq, + struct sk_buff *pkt, u16 total_len) +{ + struct brcmf_sdio_dev *sdiodev; + struct sk_buff *pkt_pad; + u16 tail_pad, tail_chop, chain_pad; + unsigned int blksize; + bool lastfrm; + int ntail, ret; + + sdiodev = bus->sdiodev; + blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % bus->sgentry_align); + + /* Check tail padding */ + lastfrm = skb_queue_is_last(pktq, pkt); + tail_pad = 0; + tail_chop = pkt->len % bus->sgentry_align; + if (tail_chop) + tail_pad = bus->sgentry_align - tail_chop; + chain_pad = (total_len + tail_pad) % blksize; + if (lastfrm && chain_pad) + tail_pad += blksize - chain_pad; + if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { + pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + + bus->head_align); + if (pkt_pad == NULL) + return -ENOMEM; + ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); + if (unlikely(ret < 0)) { + kfree_skb(pkt_pad); + return ret; + } + memcpy(pkt_pad->data, + pkt->data + pkt->len - tail_chop, + tail_chop); + *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + skb_trim(pkt, pkt->len - tail_chop); + skb_trim(pkt_pad, tail_pad + tail_chop); + __skb_queue_after(pktq, pkt, pkt_pad); + } else { + ntail = pkt->data_len + tail_pad - + (pkt->end - pkt->tail); + if (skb_cloned(pkt) || ntail > 0) + if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt)) + return -ENOMEM; + __skb_put(pkt, tail_pad); + } + + return tail_pad; +} + /** * brcmf_sdio_txpkt_prep - packet preparation for transmit * @bus: brcmf_sdio structure pointer @@ -1806,86 +2166,66 @@ static int brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, uint chan) { - u16 head_pad, tail_pad, tail_chop, head_align, sg_align; - int ntail; - struct sk_buff *pkt_next, *pkt_new; - u8 *dat_buf; - unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize; + u16 head_pad, total_len; + struct sk_buff *pkt_next; + u8 txseq; + int ret; struct brcmf_sdio_hdrinfo hd_info = {0}; - /* SDIO ADMA requires at least 32 bit alignment */ - head_align = 4; - sg_align = 4; - if (bus->sdiodev->pdata) { - head_align = bus->sdiodev->pdata->sd_head_align > 4 ? - bus->sdiodev->pdata->sd_head_align : 4; - sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ? - bus->sdiodev->pdata->sd_sgentry_align : 4; - } - /* sg entry alignment should be a divisor of block size */ - WARN_ON(blksize % sg_align); - - pkt_next = pktq->next; - dat_buf = (u8 *)(pkt_next->data); + txseq = bus->tx_seq; + total_len = 0; + skb_queue_walk(pktq, pkt_next) { + /* alignment packet inserted in previous + * loop cycle can be skipped as it is + * already properly aligned and does not + * need an sdpcm header. + */ + if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + continue; - /* Check head padding */ - head_pad = ((unsigned long)dat_buf % head_align); - if (head_pad) { - if (skb_headroom(pkt_next) < head_pad) { - bus->sdiodev->bus_if->tx_realloc++; - head_pad = 0; - if (skb_cow(pkt_next, head_pad)) - return -ENOMEM; + /* align packet data pointer */ + ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); + if (ret < 0) + return ret; + head_pad = (u16)ret; + if (head_pad) + memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); + + total_len += pkt_next->len; + + hd_info.len = pkt_next->len; + hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); + if (bus->txglom && pktq->qlen > 1) { + ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, + pkt_next, total_len); + if (ret < 0) + return ret; + hd_info.tail_pad = (u16)ret; + total_len += (u16)ret; } - skb_push(pkt_next, head_pad); - dat_buf = (u8 *)(pkt_next->data); - memset(dat_buf, 0, head_pad + bus->tx_hdrlen); - } - - /* Check tail padding */ - pkt_new = NULL; - tail_chop = pkt_next->len % sg_align; - tail_pad = sg_align - tail_chop; - tail_pad += blksize - (pkt_next->len + tail_pad) % blksize; - if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) { - pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); - if (pkt_new == NULL) - return -ENOMEM; - memcpy(pkt_new->data, - pkt_next->data + pkt_next->len - tail_chop, - tail_chop); - *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop; - skb_trim(pkt_next, pkt_next->len - tail_chop); - __skb_queue_after(pktq, pkt_next, pkt_new); - } else { - ntail = pkt_next->data_len + tail_pad - - (pkt_next->end - pkt_next->tail); - if (skb_cloned(pkt_next) || ntail > 0) - if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC)) - return -ENOMEM; - if (skb_linearize(pkt_next)) - return -ENOMEM; - dat_buf = (u8 *)(pkt_next->data); - __skb_put(pkt_next, tail_pad); - } - - /* Now prep the header */ - if (pkt_new) - hd_info.len = pkt_next->len + tail_chop; - else - hd_info.len = pkt_next->len - tail_pad; - hd_info.channel = chan; - hd_info.dat_offset = head_pad + bus->tx_hdrlen; - brcmf_sdio_hdpack(bus, dat_buf, &hd_info); - - if (BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) - brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, "Tx Frame:\n"); - else if (BRCMF_HDRS_ON()) - brcmf_dbg_hex_dump(true, pkt_next, head_pad + bus->tx_hdrlen, - "Tx Header:\n"); + hd_info.channel = chan; + hd_info.dat_offset = head_pad + bus->tx_hdrlen; + hd_info.seq_num = txseq++; + + /* Now fill the header */ + brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); + + if (BRCMF_BYTES_ON() && + ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || + (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) + brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, + "Tx Frame:\n"); + else if (BRCMF_HDRS_ON()) + brcmf_dbg_hex_dump(true, pkt_next->data, + head_pad + bus->tx_hdrlen, + "Tx Header:\n"); + } + /* Hardware length tag of the first packet should be total + * length of the chain (including padding) + */ + if (bus->txglom) + brcmf_sdio_update_hwhdr(pktq->next->data, total_len); return 0; } @@ -1903,113 +2243,113 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) { u8 *hdr; u32 dat_offset; - u32 dummy_flags, chop_len; + u16 tail_pad; + u16 dummy_flags, chop_len; struct sk_buff *pkt_next, *tmp, *pkt_prev; skb_queue_walk_safe(pktq, pkt_next, tmp) { - dummy_flags = *(u32 *)(pkt_next->cb); - if (dummy_flags & DUMMY_SKB_FLAG) { - chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK; + dummy_flags = *(u16 *)(pkt_next->cb); + if (dummy_flags & ALIGN_SKB_FLAG) { + chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; if (chop_len) { pkt_prev = pkt_next->prev; - memcpy(pkt_prev->data + pkt_prev->len, - pkt_next->data, chop_len); skb_put(pkt_prev, chop_len); } __skb_unlink(pkt_next, pktq); brcmu_pkt_buf_free_skb(pkt_next); } else { - hdr = pkt_next->data + SDPCM_HWHDR_LEN; + hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN; dat_offset = le32_to_cpu(*(__le32 *)hdr); dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT; skb_pull(pkt_next, dat_offset); + if (bus->txglom) { + tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); + skb_trim(pkt_next, pkt_next->len - tail_pad); + } } } } /* Writes a HW/SW header into the packet and sends it. */ /* Assumes: (a) header space already there, (b) caller holds lock */ -static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, - uint chan) +static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) { int ret; - int i; - struct sk_buff_head localq; + struct sk_buff *pkt_next, *tmp; brcmf_dbg(TRACE, "Enter\n"); - __skb_queue_head_init(&localq); - __skb_queue_tail(&localq, pkt); - ret = brcmf_sdio_txpkt_prep(bus, &localq, chan); + ret = brcmf_sdio_txpkt_prep(bus, pktq, chan); if (ret) goto done; sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, &localq); + ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); bus->sdcnt.f2txdata++; - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; + if (ret < 0) + brcmf_sdio_txfail(bus); - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } - - } sdio_release_host(bus->sdiodev->func[1]); - if (ret == 0) - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; done: - brcmf_sdio_txpkt_postp(bus, &localq); - __skb_dequeue_tail(&localq); - brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0); + brcmf_sdio_txpkt_postp(bus, pktq); + if (ret == 0) + bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; + skb_queue_walk_safe(pktq, pkt_next, tmp) { + __skb_unlink(pkt_next, pktq); + brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); + } return ret; } -static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) +static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) { struct sk_buff *pkt; + struct sk_buff_head pktq; u32 intstatus = 0; - int ret = 0, prec_out; + int ret = 0, prec_out, i; uint cnt = 0; - u8 tx_prec_map; + u8 tx_prec_map, pkt_num; brcmf_dbg(TRACE, "Enter\n"); tx_prec_map = ~bus->flowcontrol; /* Send frames until the limit or some other event */ - for (cnt = 0; (cnt < maxframes) && data_ok(bus); cnt++) { - spin_lock_bh(&bus->txqlock); - pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); - if (pkt == NULL) { - spin_unlock_bh(&bus->txqlock); + for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { + pkt_num = 1; + 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)); + __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); + if (pkt == NULL) + break; + __skb_queue_tail(&pktq, pkt); + } + spin_unlock_bh(&bus->txq_lock); + if (i == 0) { + up(&bus->tx_seq_lock); break; } - spin_unlock_bh(&bus->txqlock); - ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL); + 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 */ - if (!bus->intr && cnt) { + if (!bus->intr) { /* Check device status, signal pending interrupt */ sdio_claim_host(bus->sdiodev->func[1]); ret = r_sdreg32(bus, &intstatus, @@ -2034,7 +2374,69 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) return cnt; } -static void brcmf_sdbrcm_bus_stop(struct device *dev) +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; u8 saveclk; @@ -2051,62 +2453,57 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - sdio_claim_host(bus->sdiodev->func[1]); - - /* Enable clock for device interrupts */ - brcmf_sdbrcm_bus_sleep(bus, false, false); + if (bus_if->state == BRCMF_BUS_DOWN) { + sdio_claim_host(sdiodev->func[1]); + + /* Enable clock for device interrupts */ + brcmf_sdio_bus_sleep(bus, false, false); + + /* Disable and clear interrupts at the chip level also */ + w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); + local_hostintmask = bus->hostintmask; + bus->hostintmask = 0; + + /* Force backplane clocks to assure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if (!err) + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + if (err) + brcmf_err("Failed to force clock for F2: err %d\n", + err); - /* Disable and clear interrupts at the chip level also */ - w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); - local_hostintmask = bus->hostintmask; - bus->hostintmask = 0; + /* Turn off the bus (F2), free any pending packets */ + brcmf_dbg(INTR, "disable SDIO interrupts\n"); + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); - /* Change our idea of bus state */ - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; + /* Clear any pending interrupts now that F2 is disabled */ + w_sdreg32(bus, local_hostintmask, + offsetof(struct sdpcmd_regs, intstatus)); - /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (!err) { - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); + sdio_release_host(sdiodev->func[1]); } - if (err) - brcmf_err("Failed to force clock for F2: err %d\n", err); - - /* Turn off the bus (F2), free any pending packets */ - brcmf_dbg(INTR, "disable SDIO interrupts\n"); - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1, - NULL); - - /* Clear any pending interrupts now that F2 is disabled */ - w_sdreg32(bus, local_hostintmask, - offsetof(struct sdpcmd_regs, intstatus)); - - /* Turn off the backplane clock (only) */ - brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); - sdio_release_host(bus->sdiodev->func[1]); - /* Clear the data packet queues */ brcmu_pktq_flush(&bus->txq, true, NULL, NULL); /* Clear any held glomming stuff */ if (bus->glomd) brcmu_pkt_buf_free_skb(bus->glomd); - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); /* Clear rx control and wake any waiters */ spin_lock_bh(&bus->rxctl_lock); bus->rxlen = 0; spin_unlock_bh(&bus->rxctl_lock); - brcmf_sdbrcm_dcmd_resp_wake(bus); + brcmf_sdio_dcmd_resp_wake(bus); /* Reset some F2 state stuff */ bus->rxskip = false; bus->tx_seq = bus->rx_seq = 0; } -static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) { unsigned long flags; @@ -2120,49 +2517,50 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) } } +static void atomic_orr(int val, atomic_t *v) +{ + int old_val; + + old_val = atomic_read(v); + while (atomic_cmpxchg(v, old_val, val | old_val) != old_val) + old_val = atomic_read(v); +} + static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { - u8 idx; + struct brcmf_core *buscore; u32 addr; unsigned long val; - int n, ret; + int ret; - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - addr = bus->ci->c_inf[idx].base + - offsetof(struct sdpcmd_regs, intstatus); + buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); - ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false); + val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) - val = 0; + return ret; val &= bus->hostintmask; atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); /* Clear interrupts */ if (val) { - ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true); + brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - } - - if (ret) { - atomic_set(&bus->intstatus, 0); - } else if (val) { - for_each_set_bit(n, &val, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); + atomic_orr(val, &bus->intstatus); } return ret; } -static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) +static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; unsigned long intstatus; - uint rxlimit = bus->rxbound; /* Rx frames to read before resched */ uint txlimit = bus->txbound; /* Tx frames to send before resched */ - uint framecnt = 0; /* Temporary counter of tx/rx frames */ - int err = 0, n; + uint framecnt; /* Temporary counter of tx/rx frames */ + int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -2174,48 +2572,29 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) #ifdef DEBUG /* Check for inconsistent device control */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - if (err) { - brcmf_err("error reading DEVCTL: %d\n", err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); #endif /* DEBUG */ /* Read CSR, if clock on switch to AVAIL, else ignore */ - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (err) { - brcmf_err("error reading CSR: %d\n", - err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl); if (SBSDIO_HTAV(clkctl)) { - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - if (err) { - brcmf_err("error reading DEVCTL: %d\n", - err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - if (err) { - brcmf_err("error writing DEVCTL: %d\n", - err); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - } + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); bus->clkstate = CLK_AVAIL; } } /* Make sure backplane clock is on */ - brcmf_sdbrcm_bus_sleep(bus, false, true); + brcmf_sdio_bus_sleep(bus, false, true); /* Pending interrupt indicates new device status */ if (atomic_read(&bus->ipend) > 0) { @@ -2246,7 +2625,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) /* Handle host mailbox indication */ if (intstatus & I_HMB_HOST_INT) { intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdbrcm_hostmail(bus); + intstatus |= brcmf_sdio_hostmail(bus); } sdio_release_host(bus->sdiodev->func[1]); @@ -2278,97 +2657,53 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) intstatus &= ~I_HMB_FRAME_IND; /* On frame indication, read available frames */ - if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { - framecnt = brcmf_sdio_readframes(bus, rxlimit); + if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { + brcmf_sdio_readframes(bus, bus->rxbound); if (!bus->rxpending) intstatus &= ~I_HMB_FRAME_IND; - rxlimit -= min(framecnt, rxlimit); } /* Keep still-pending events for next scheduling */ - if (intstatus) { - for_each_set_bit(n, &intstatus, 32) - set_bit(n, (unsigned long *)&bus->intstatus.counter); - } - - brcmf_sdbrcm_clrintr(bus); - - if (data_ok(bus) && bus->ctrl_frame_stat && - (bus->clkstate == CLK_AVAIL)) { - int i; + if (intstatus) + atomic_orr(intstatus, &bus->intstatus); - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, - (u32) bus->ctrl_frame_len); + brcmf_sdio_clrintr(bus); - if (err < 0) { - /* On failure, abort the command and - terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - err); - bus->sdcnt.tx_sderrs++; - - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, &err); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, - &err); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, - &err); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } + 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]); - } else { - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); } - sdio_release_host(bus->sdiodev->func[1]); - bus->ctrl_frame_stat = false; - brcmf_sdbrcm_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; - framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); - txlimit -= framecnt; + brcmf_sdio_sendfromq(bus, framecnt); } - if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) { + if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; atomic_set(&bus->intstatus, 0); } else if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus)) || PKT_AVAILABLE()) { + data_ok(bus))) { atomic_inc(&bus->dpc_tskcnt); } - - /* If we're done for now, turn off clock request. */ - if ((bus->clkstate != CLK_PENDING) - && bus->idletime == BRCMF_IDLE_IMMEDIATE) { - bus->activity = false; - brcmf_dbg(SDIO, "idle state\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } } -static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) +static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; @@ -2377,18 +2712,15 @@ static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) return &bus->txq; } -static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) +static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; - uint datalen, prec; + uint prec; 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\n"); - datalen = pkt->len; + brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); /* Add space for the header */ skb_push(pkt, bus->tx_hdrlen); @@ -2402,7 +2734,9 @@ static int brcmf_sdbrcm_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)) { skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); @@ -2415,7 +2749,7 @@ static int brcmf_sdbrcm_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]) @@ -2433,7 +2767,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) #ifdef DEBUG #define CONSOLE_LINE_MAX 192 -static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) +static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) { struct brcmf_console *c = &bus->console; u8 line[CONSOLE_LINE_MAX], ch; @@ -2446,8 +2780,8 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) /* Read console log struct */ addr = bus->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, - sizeof(c->log_le)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, + sizeof(c->log_le)); if (rv < 0) return rv; @@ -2472,7 +2806,7 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) /* Read the console buffer */ addr = le32_to_cpu(c->log_le.buf); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); if (rv < 0) return rv; @@ -2510,107 +2844,27 @@ break2: } #endif /* DEBUG */ -static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - int i; - int ret; - - bus->ctrl_frame_stat = false; - ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, frame, len); - - if (ret < 0) { - /* On failure, abort the command and terminate the frame */ - brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", - ret); - bus->sdcnt.tx_sderrs++; - - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - u8 hi, lo; - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if (hi == 0 && lo == 0) - break; - } - return ret; - } - - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - - return ret; -} - static int -brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) +brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { - u8 *frame; - u16 len; - 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 % BRCMF_SDALIGN); - if (doff) { - frame -= doff; - len += doff; - msglen += doff; - memset(frame, 0, doff + bus->tx_hdrlen); - } - /* precondition: doff < BRCMF_SDALIGN */ - doff += bus->tx_hdrlen; - - /* Round send length to next SDIO block */ - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - u16 pad = bus->blocksize - (len % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize)) - len += pad; - } else if (len % BRCMF_SDALIGN) { - len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); - } - - /* Satisfy length-alignment requirements */ - if (len & (ALIGNMENT - 1)) - len = roundup(len, ALIGNMENT); - - /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - - /* Make sure backplane clock is on */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_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; - brcmf_sdio_hdpack(bus, frame, &hd_info); + 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, @@ -2621,31 +2875,18 @@ brcmf_sdbrcm_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_tx_frame(bus, frame, len); - sdio_release_host(bus->sdiodev->func[1]); - } while (ret < 0 && retries++ < TXRETRIES); - } - - if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && - atomic_read(&bus->dpc_tskcnt) == 0) { - bus->activity = false; sdio_claim_host(bus->sdiodev->func[1]); - brcmf_dbg(INFO, "idle\n"); - brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); + 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) @@ -2657,72 +2898,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #ifdef DEBUG -static inline bool brcmf_sdio_valid_shared_address(u32 addr) -{ - return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); -} - -static int brcmf_sdio_readshared(struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr; - int rv; - u32 shaddr = 0; - struct sdpcm_shared_le sh_le; - __le32 addr_le; - - shaddr = bus->ci->rambase + bus->ramsize - 4; - - /* - * Read last word in socram to determine - * address of sdpcm_shared structure - */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, false, false); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); - if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); - - /* - * Check if addr is valid. - * NVRAM length at the end of memory should have been overwritten. - */ - if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; - } - - /* Read hndrte_shared structure */ - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); - if (rv < 0) - return rv; - - /* Endianness */ - sh->flags = le32_to_cpu(sh_le.flags); - sh->trap_addr = le32_to_cpu(sh_le.trap_addr); - sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); - sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); - sh->assert_line = le32_to_cpu(sh_le.assert_line); - sh->console_addr = le32_to_cpu(sh_le.console_addr); - sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - - if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { - brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", - SDPCM_SHARED_VERSION, - sh->flags & SDPCM_SHARED_VERSION_MASK); - return -EPROTO; - } - - return 0; -} - static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, struct sdpcm_shared *sh, char __user *data, size_t count) @@ -2736,22 +2911,22 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, /* obtain console information from device memory */ addr = sh->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_ptr = le32_to_cpu(sh_val); addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_size = le32_to_cpu(sh_val); addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_index = le32_to_cpu(sh_val); @@ -2765,8 +2940,8 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, /* obtain the console data from device */ conbuf[console_size] = '\0'; - rv = brcmf_sdio_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, - console_size); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, + console_size); if (rv < 0) goto done; @@ -2803,8 +2978,8 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, return 0; } - error = brcmf_sdio_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, - sizeof(struct brcmf_trap_info)); + error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, + sizeof(struct brcmf_trap_info)); if (error < 0) return error; @@ -2847,14 +3022,14 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, sdio_claim_host(bus->sdiodev->func[1]); if (sh->assert_file_addr != 0) { - error = brcmf_sdio_ramrw(bus->sdiodev, false, - sh->assert_file_addr, (u8 *)file, 80); + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_file_addr, (u8 *)file, 80); if (error < 0) return error; } if (sh->assert_exp_addr != 0) { - error = brcmf_sdio_ramrw(bus->sdiodev, false, - sh->assert_exp_addr, (u8 *)expr, 80); + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_exp_addr, (u8 *)expr, 80); if (error < 0) return error; } @@ -2866,7 +3041,7 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, return simple_read_from_buffer(data, count, &pos, buf, res); } -static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) { int error; struct sdpcm_shared sh; @@ -2887,8 +3062,8 @@ static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) return 0; } -static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data, + size_t count, loff_t *ppos) { int error = 0; struct sdpcm_shared sh; @@ -2929,7 +3104,7 @@ static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, struct brcmf_sdio *bus = f->private_data; int res; - res = brcmf_sdbrcm_died_dump(bus, data, count, ppos); + res = brcmf_sdio_died_dump(bus, data, count, ppos); if (res > 0) *ppos += res; return (ssize_t)res; @@ -2952,9 +3127,11 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) debugfs_create_file("forensics", S_IRUGO, dentry, bus, &brcmf_sdio_forensic_ops); brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); + debugfs_create_u32("console_interval", 0644, dentry, + &bus->console_interval); } #else -static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) { return 0; } @@ -2965,7 +3142,7 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) #endif /* DEBUG */ static int -brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) +brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) { int timeleft; uint rxlen = 0; @@ -2978,7 +3155,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) brcmf_dbg(TRACE, "Enter\n"); /* Wait until control frame is available */ - timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending); + timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending); spin_lock_bh(&bus->rxctl_lock); rxlen = bus->rxlen; @@ -2995,13 +3172,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) rxlen, msglen); } else if (timeleft == 0) { brcmf_err("resumed on timeout\n"); - brcmf_sdbrcm_checkdied(bus); + brcmf_sdio_checkdied(bus); } else if (pending) { brcmf_dbg(CTL, "cancelled\n"); return -ERESTARTSYS; } else { brcmf_dbg(CTL, "resumed for unknown reason?\n"); - brcmf_sdbrcm_checkdied(bus); + brcmf_sdio_checkdied(bus); } if (rxlen) @@ -3012,272 +3189,177 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) return rxlen ? (int)rxlen : -ETIMEDOUT; } -static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) -{ - struct chip_info *ci = bus->ci; - - /* To enter download state, disable ARM and reset SOCRAM. - * To exit download state, simply reset ARM (default is RAM boot). - */ - if (enter) { - bus->alp_only = true; - - brcmf_sdio_chip_enter_download(bus->sdiodev, ci); - } else { - if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci, bus->vars, - bus->varsz)) - return false; - - /* Allow HT Clock now that the ARM is running. */ - bus->alp_only = false; - - bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; - } - - return true; -} - -static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus) -{ - if (bus->firmware->size < bus->fw_ptr + len) - len = bus->firmware->size - bus->fw_ptr; - - memcpy(buf, &bus->firmware->data[bus->fw_ptr], len); - bus->fw_ptr += len; - return len; -} - -static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) +#ifdef DEBUG +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, + u8 *ram_data, uint ram_sz) { + char *ram_cmp; + int err; + bool ret = true; + int address; int offset; - uint len; - u8 *memblock = NULL, *memptr; - int ret; - u8 idx; - - brcmf_dbg(INFO, "Enter\n"); - - ret = request_firmware(&bus->firmware, BRCMF_SDIO_FW_NAME, - &bus->sdiodev->func[2]->dev); - if (ret) { - brcmf_err("Fail to request firmware %d\n", ret); - return ret; - } - bus->fw_ptr = 0; - - memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC); - if (memblock == NULL) { - ret = -ENOMEM; - goto err; - } - if ((u32)(unsigned long)memblock % BRCMF_SDALIGN) - memptr += (BRCMF_SDALIGN - - ((u32)(unsigned long)memblock % BRCMF_SDALIGN)); - - offset = bus->ci->rambase; - - /* Download image */ - len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus); - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4); - if (BRCMF_MAX_CORENUM != idx) - memcpy(&bus->ci->rst_vec, memptr, sizeof(bus->ci->rst_vec)); - while (len) { - ret = brcmf_sdio_ramrw(bus->sdiodev, true, offset, memptr, len); - if (ret) { - brcmf_err("error %d on writing %d membytes at 0x%08x\n", - ret, MEMBLOCK, offset); - goto err; + int len; + + /* read back and verify */ + brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr, + ram_sz); + ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL); + /* do not proceed while no memory but */ + if (!ram_cmp) + return true; + + address = ram_addr; + offset = 0; + while (offset < ram_sz) { + len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK : + ram_sz - offset; + err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len); + if (err) { + brcmf_err("error %d on reading %d membytes at 0x%08x\n", + err, len, address); + ret = false; + break; + } else if (memcmp(ram_cmp, &ram_data[offset], len)) { + brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n", + offset, len); + ret = false; + break; } - - offset += MEMBLOCK; - len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus); + offset += len; + address += len; } -err: - kfree(memblock); - - release_firmware(bus->firmware); - bus->fw_ptr = 0; + kfree(ram_cmp); return ret; } +#else /* DEBUG */ +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, + u8 *ram_data, uint ram_sz) +{ + return true; +} +#endif /* DEBUG */ -/* - * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file - * and ending in a NUL. - * Removes carriage returns, empty lines, comment lines, and converts - * newlines to NULs. - * Shortens buffer as needed and pads with NULs. End of buffer is marked - * by two NULs. -*/ - -static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) -{ - char *varbuf; - char *dp; - bool findNewline; - int column; - int ret = 0; - uint buf_len, n, len; - - len = bus->firmware->size; - varbuf = vmalloc(len); - if (!varbuf) - return -ENOMEM; - - memcpy(varbuf, bus->firmware->data, len); - dp = varbuf; +static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, + const struct firmware *fw) +{ + int err; - findNewline = false; - column = 0; + brcmf_dbg(TRACE, "Enter\n"); - for (n = 0; n < len; n++) { - if (varbuf[n] == 0) - break; - if (varbuf[n] == '\r') - continue; - if (findNewline && varbuf[n] != '\n') - continue; - findNewline = false; - if (varbuf[n] == '#') { - findNewline = true; - continue; - } - if (varbuf[n] == '\n') { - if (column == 0) - continue; - *dp++ = 0; - column = 0; - continue; - } - *dp++ = varbuf[n]; - column++; - } - buf_len = dp - varbuf; - while (dp < varbuf + n) - *dp++ = 0; - - kfree(bus->vars); - /* roundup needed for download to device */ - bus->varsz = roundup(buf_len + 1, 4); - bus->vars = kmalloc(bus->varsz, GFP_KERNEL); - if (bus->vars == NULL) { - bus->varsz = 0; - ret = -ENOMEM; - goto err; - } + err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, + (u8 *)fw->data, fw->size); + if (err) + brcmf_err("error %d on writing %d membytes at 0x%08x\n", + err, (int)fw->size, bus->ci->rambase); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; - /* copy the processed variables and add null termination */ - memcpy(bus->vars, varbuf, buf_len); - bus->vars[buf_len] = 0; -err: - vfree(varbuf); - return ret; + return err; } -static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, + void *vars, u32 varsz) { - int ret; - - ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME, - &bus->sdiodev->func[2]->dev); - if (ret) { - brcmf_err("Fail to request nvram %d\n", ret); - return ret; - } + int address; + int err; - ret = brcmf_process_nvram_vars(bus); + brcmf_dbg(TRACE, "Enter\n"); - release_firmware(bus->firmware); + address = bus->ci->ramsize - varsz + bus->ci->rambase; + err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); + if (err) + brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", + err, varsz, address); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) + err = -EIO; - return ret; + return err; } -static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, + const struct firmware *fw, + void *nvram, u32 nvlen) { - int bcmerror = -1; + int bcmerror = -EFAULT; + u32 rstvec; + + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - if (!brcmf_sdbrcm_download_state(bus, true)) { - brcmf_err("error placing ARM core in reset\n"); - goto err; - } + brcmf_chip_enter_download(bus->ci); + + rstvec = get_unaligned_le32(fw->data); + brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); - if (brcmf_sdbrcm_download_code_file(bus)) { + bcmerror = brcmf_sdio_download_code_file(bus, fw); + release_firmware(fw); + if (bcmerror) { brcmf_err("dongle image file download failed\n"); + brcmf_fw_nvram_free(nvram); goto err; } - if (brcmf_sdbrcm_download_nvram(bus)) { + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); + brcmf_fw_nvram_free(nvram); + if (bcmerror) { brcmf_err("dongle nvram file download failed\n"); goto err; } /* Take arm out of reset */ - if (!brcmf_sdbrcm_download_state(bus, false)) { + if (!brcmf_chip_exit_download(bus->ci, rstvec)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } + /* Allow HT Clock now that the ARM is running. */ + brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD); bcmerror = 0; err: + brcmf_sdio_clkctl(bus, CLK_SDONLY, false); + sdio_release_host(bus->sdiodev->func[1]); return bcmerror; } -static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus) -{ - u32 addr, reg; - - brcmf_dbg(TRACE, "Enter\n"); - - /* old chips with PMU version less than 17 don't support save restore */ - if (bus->ci->pmurev < 17) - return false; - - /* read PMU chipcontrol register 3*/ - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); - brcmf_sdio_regwl(bus->sdiodev, addr, 3, NULL); - addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); - reg = brcmf_sdio_regrl(bus->sdiodev, addr, NULL); - - return (bool)reg; -} - -static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus) +static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) { int err = 0; u8 val; brcmf_dbg(TRACE, "Enter\n"); - val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, - &err); + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err); if (err) { brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); return; } val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, - val, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err); if (err) { brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); return; } /* Add CMD14 Support */ - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, - (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | - SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), - &err); + brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, + (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | + SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), + &err); if (err) { brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); return; } - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_FORCE_HT, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_FORCE_HT, &err); if (err) { brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); return; @@ -3289,7 +3371,7 @@ static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus) } /* enable KSO bit */ -static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) +static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) { u8 val; int err = 0; @@ -3297,11 +3379,10 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); /* KSO bit added in SDIO core rev 12 */ - if (bus->ci->c_inf[1].rev < 12) + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) return 0; - val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - &err); + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); if (err) { brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); return err; @@ -3310,8 +3391,8 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - val, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + val, &err); if (err) { brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); return err; @@ -3322,132 +3403,61 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) } -static bool -brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) -{ - bool ret; - - sdio_claim_host(bus->sdiodev->func[1]); - - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - - ret = _brcmf_sdbrcm_download_firmware(bus) == 0; - - brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); - - sdio_release_host(bus->sdiodev->func[1]); - - return ret; -} - -static int brcmf_sdbrcm_bus_init(struct device *dev) +static int brcmf_sdio_bus_preinit(struct device *dev) { 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; - unsigned long timeout; - u8 ready, enable; - int err, ret = 0; - u8 saveclk; - - brcmf_dbg(TRACE, "Enter\n"); - - /* try to download image and nvram to the dongle */ - if (bus_if->state == BRCMF_BUS_DOWN) { - if (!(brcmf_sdbrcm_download_firmware(bus))) - return -1; - } - - if (!bus->sdiodev->bus_if->drvr) - return 0; - - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); - - sdio_claim_host(bus->sdiodev->func[1]); - - /* Make sure backplane clock is on, needed to generate F2 interrupt */ - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - if (bus->clkstate != CLK_AVAIL) - goto exit; - - /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (!err) { - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); - } - if (err) { - brcmf_err("Failed to force clock for F2: err %d\n", err); - goto exit; - } - - /* Enable function 2 (frame transfers) */ - w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, - offsetof(struct sdpcmd_regs, tosbmailboxdata)); - enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); - - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); - - timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY); - ready = 0; - while (enable != ready) { - ready = brcmf_sdio_regrb(bus->sdiodev, - SDIO_CCCR_IORx, NULL); - if (time_after(jiffies, timeout)) - break; - else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50)) - /* prevent busy waiting if it takes too long */ - msleep_interruptible(20); - } - - brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready); - - /* If F2 successfully enabled, set core and enable interrupts */ - if (ready == enable) { - /* Set up the interrupt mask and enable interrupts */ - bus->hostintmask = HOSTINTMASK; - w_sdreg32(bus, bus->hostintmask, - offsetof(struct sdpcmd_regs, hostintmask)); + uint pad_size; + u32 value; + int err; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); + /* the commands below use the terms tx and rx from + * a device perspective, ie. bus:txglom affects the + * bus transfers from device to host. + */ + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { + /* for sdio core rev < 12, disable txgloming */ + value = 0; + err = brcmf_iovar_data_set(dev, "bus:txglom", &value, + sizeof(u32)); } else { - /* Disable F2 again */ - enable = SDIO_FUNC_ENABLE_1; - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); - ret = -ENODEV; + /* otherwise, set txglomalign */ + value = 4; + if (sdiodev->pdata) + value = sdiodev->pdata->sd_sgentry_align; + /* SDIO ADMA requires at least 32 bit alignment */ + value = max_t(u32, value, 4); + err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, + sizeof(u32)); } - if (brcmf_sdbrcm_sr_capable(bus)) { - brcmf_sdbrcm_sr_init(bus); - } else { - /* Restore previous clock setting */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - saveclk, &err); - } + if (err < 0) + goto done; - if (ret == 0) { - ret = brcmf_sdio_intr_register(bus->sdiodev); - if (ret != 0) - brcmf_err("intr register failed:%d\n", ret); + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; + if (sdiodev->sg_support) { + bus->txglom = false; + value = 1; + pad_size = bus->sdiodev->func[2]->cur_blksize << 1; + err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", + &value, sizeof(u32)); + if (err < 0) { + /* bus:rxglom is allowed to fail */ + err = 0; + } else { + bus->txglom = true; + bus->tx_hdrlen += SDPCM_HWEXT_LEN; + } } + brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); - /* If we didn't come up, turn off backplane clock */ - if (bus_if->state != BRCMF_BUS_DATA) - brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); - -exit: - sdio_release_host(bus->sdiodev->func[1]); - - return ret; +done: + return err; } -void brcmf_sdbrcm_isr(void *arg) +void brcmf_sdio_isr(struct brcmf_sdio *bus) { - struct brcmf_sdio *bus = (struct brcmf_sdio *) arg; - brcmf_dbg(TRACE, "Enter\n"); if (!bus) { @@ -3455,7 +3465,7 @@ void brcmf_sdbrcm_isr(void *arg) return; } - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + if (!brcmf_bus_ready(bus->sdiodev->bus_if)) { brcmf_err("bus is down. we have nothing to do\n"); return; } @@ -3466,7 +3476,6 @@ void brcmf_sdbrcm_isr(void *arg) else if (brcmf_sdio_intr_rstatus(bus)) { brcmf_err("failed backplane access\n"); - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } /* Disable additional interrupts (is this needed now)? */ @@ -3477,7 +3486,7 @@ void brcmf_sdbrcm_isr(void *arg) queue_work(bus->brcmf_wq, &bus->datawork); } -static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) +static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) { #ifdef DEBUG struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); @@ -3501,9 +3510,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) u8 devpend; sdio_claim_host(bus->sdiodev->func[1]); - devpend = brcmf_sdio_regrb(bus->sdiodev, - SDIO_CCCR_INTx, - NULL); + devpend = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_INTx, + NULL); sdio_release_host(bus->sdiodev->func[1]); intstatus = devpend & (INTR_STATUS_FUNC1 | @@ -3533,8 +3542,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->console.count -= bus->console_interval; sdio_claim_host(bus->sdiodev->func[1]); /* Make sure backplane clock is on */ - brcmf_sdbrcm_bus_sleep(bus, false, false); - if (brcmf_sdbrcm_readconsole(bus) < 0) + brcmf_sdio_bus_sleep(bus, false, false); + if (brcmf_sdio_readconsole(bus) < 0) /* stop on error */ bus->console_interval = 0; sdio_release_host(bus->sdiodev->func[1]); @@ -3548,11 +3557,11 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->idlecount = 0; if (bus->activity) { bus->activity = false; - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); } else { brcmf_dbg(SDIO, "idle\n"); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, true, false); + brcmf_sdio_bus_sleep(bus, true, false); sdio_release_host(bus->sdiodev->func[1]); } } @@ -3567,38 +3576,177 @@ static void brcmf_sdio_dataworker(struct work_struct *work) datawork); while (atomic_read(&bus->dpc_tskcnt)) { - brcmf_sdbrcm_dpc(bus); - atomic_dec(&bus->dpc_tskcnt); + atomic_set(&bus->dpc_tskcnt, 0); + brcmf_sdio_dpc(bus); } } -static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 drivestrength) { - brcmf_dbg(TRACE, "Enter\n"); + const struct sdiod_drive_str *str_tab = NULL; + u32 str_mask; + u32 str_shift; + u32 base; + u32 i; + u32 drivestrength_sel = 0; + u32 cc_data_temp; + u32 addr; - kfree(bus->rxbuf); - bus->rxctl = bus->rxbuf = NULL; - bus->rxlen = 0; + if (!(ci->cc_caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + str_tab = sdiod_drvstr_tab1_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + str_tab = sdiod_drvstr_tab6_1v8; + str_mask = 0x00001800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): + /* note: 43143 does not support tristate */ + i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; + if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { + str_tab = sdiod_drvstr_tab2_3v3; + str_mask = 0x00000007; + str_shift = 0; + } else + brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", + ci->name, drivestrength); + break; + case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + str_tab = sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + ci->name, ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + base = brcmf_chip_get_chipcommon(ci)->base; + addr = CORE_CC_REG(base, chipcontrol_addr); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + + brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", + str_tab[i].strength, drivestrength, cc_data_temp); + } } -static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) +static int brcmf_sdio_buscoreprep(void *ctx) { - brcmf_dbg(TRACE, "Enter\n"); + struct brcmf_sdio_dev *sdiodev = ctx; + int err = 0; + u8 clkval, clkset; - if (bus->sdiodev->bus_if->maxctl) { - bus->rxblen = - roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), - ALIGNMENT) + BRCMF_SDALIGN; - bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); - if (!(bus->rxbuf)) - return false; + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (err) { + brcmf_err("error writing for HT off\n"); + return err; } - return true; + /* If register supported, wait for ALPAvail and then force ALP */ + /* This may take up to 15 milliseconds */ + clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); + + if ((clkval & ~SBSDIO_AVBITS) != clkset) { + brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", + clkset, clkval); + return -EACCES; + } + + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + struct brcmf_core *core; + u32 reg_addr; + + /* clear all interrupts */ + core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); + reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + if (rstvec) + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + u32 val, rev; + + val = brcmf_sdiod_regrl(sdiodev, addr, NULL); + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { + rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; + if (rev >= 2) { + val &= ~CID_ID_MASK; + val |= BCM4339_CHIP_ID; + } + } + return val; } +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + + brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { + .prepare = brcmf_sdio_buscoreprep, + .exit_dl = brcmf_sdio_buscore_exitdl, + .read32 = brcmf_sdio_buscore_read32, + .write32 = brcmf_sdio_buscore_write32, +}; + static bool -brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) +brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { u8 clkctl = 0; int err = 0; @@ -3606,23 +3754,21 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) u32 reg_val; u32 drivestrength; - bus->alp_only = true; - sdio_claim_host(bus->sdiodev->func[1]); pr_debug("F1 signature read @0x18000000=0x%4x\n", - brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); + brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); /* - * Force PLL off until brcmf_sdio_chip_attach() + * Force PLL off until brcmf_chip_attach() * programs PLL control regs */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - BRCMF_INIT_CLKCTL1, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + BRCMF_INIT_CLKCTL1, &err); if (!err) - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", @@ -3630,12 +3776,19 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) goto fail; } - if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) { - brcmf_err("brcmf_sdio_chip_attach failed!\n"); + /* SDIO register access works so moving + * state from UNKNOWN to DOWN. + */ + brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); + + bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + if (IS_ERR(bus->ci)) { + brcmf_err("brcmf_chip_attach failed!\n"); + bus->ci = NULL; goto fail; } - if (brcmf_sdbrcm_kso_init(bus)) { + if (brcmf_sdio_kso_init(bus)) { brcmf_err("error enabling KSO\n"); goto fail; } @@ -3644,7 +3797,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) drivestrength = bus->sdiodev->pdata->drive_strength; else drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); /* Get info on the SOCRAM cores... */ bus->ramsize = bus->ci->ramsize; @@ -3654,44 +3807,42 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) } /* Set card control so an SDIO card reset does a WLAN backplane reset */ - reg_val = brcmf_sdio_regrb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, &err); + reg_val = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, &err); if (err) goto fail; reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; - brcmf_sdio_regwb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); + brcmf_sdiod_regwb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); if (err) goto fail; /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, + reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, pmucontrol); - reg_val = brcmf_sdio_regrl(bus->sdiodev, - reg_addr, - &err); + reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); if (err) goto fail; reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); - brcmf_sdio_regwl(bus->sdiodev, - reg_addr, - reg_val, - &err); + brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); if (err) goto fail; - sdio_release_host(bus->sdiodev->func[1]); brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); + /* allocate header buffer */ + bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); + if (!bus->hdrbuf) + return false; /* Locate an appropriately-aligned portion of hdrbuf */ bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], - BRCMF_SDALIGN); + bus->head_align); /* Set the poll and/or interrupt flags */ bus->intr = true; @@ -3706,42 +3857,8 @@ fail: return false; } -static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - sdio_claim_host(bus->sdiodev->func[1]); - - /* Disable F2 to clear any intermediate frame state on the dongle */ - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, - SDIO_FUNC_ENABLE_1, NULL); - - bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; - bus->rxflow = false; - - /* Done with backplane-dependent accesses, can drop clock... */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); - - sdio_release_host(bus->sdiodev->func[1]); - - /* ...and initialize clock/power states */ - bus->clkstate = CLK_SDONLY; - bus->idletime = BRCMF_IDLE_INTERVAL; - bus->idleclock = BRCMF_IDLE_ACTIVE; - - /* Query the F2 block size, set roundup accordingly */ - bus->blocksize = bus->sdiodev->func[2]->cur_blksize; - bus->roundup = min(max_roundup, bus->blocksize); - - /* SR state */ - bus->sleeping = false; - bus->sr_enabled = false; - - return true; -} - static int -brcmf_sdbrcm_watchdog_thread(void *data) +brcmf_sdio_watchdog_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; @@ -3751,9 +3868,10 @@ brcmf_sdbrcm_watchdog_thread(void *data) if (kthread_should_stop()) break; if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { - brcmf_sdbrcm_bus_watchdog(bus); + brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); } else break; } @@ -3761,7 +3879,7 @@ brcmf_sdbrcm_watchdog_thread(void *data) } static void -brcmf_sdbrcm_watchdog(unsigned long data) +brcmf_sdio_watchdog(unsigned long data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; @@ -3774,73 +3892,124 @@ brcmf_sdbrcm_watchdog(unsigned long data) } } -static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) +static struct brcmf_bus_ops brcmf_sdio_bus_ops = { + .stop = brcmf_sdio_bus_stop, + .preinit = brcmf_sdio_bus_preinit, + .txdata = brcmf_sdio_bus_txdata, + .txctl = brcmf_sdio_bus_txctl, + .rxctl = brcmf_sdio_bus_rxctl, + .gettxq = brcmf_sdio_bus_gettxq, +}; + +static void brcmf_sdio_firmware_callback(struct device *dev, + const struct firmware *code, + void *nvram, u32 nvram_len) { - brcmf_dbg(TRACE, "Enter\n"); + 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; + int err = 0; + u8 saveclk; - if (bus->ci) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); - sdio_release_host(bus->sdiodev->func[1]); - brcmf_sdio_chip_detach(&bus->ci); - if (bus->vars && bus->varsz) - kfree(bus->vars); - bus->vars = NULL; + brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); + + /* try to download image and nvram to the dongle */ + if (bus_if->state == BRCMF_BUS_DOWN) { + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; } - brcmf_dbg(TRACE, "Disconnected\n"); -} + if (!bus_if->drvr) + return; -/* Detach and free everything */ -static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - if (bus) { - /* De-register interrupt handler */ - brcmf_sdio_intr_unregister(bus->sdiodev); + sdio_claim_host(sdiodev->func[1]); - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + if (bus->clkstate != CLK_AVAIL) + goto release; - if (bus->sdiodev->bus_if->drvr) { - brcmf_detach(bus->sdiodev->dev); - brcmf_sdbrcm_release_dongle(bus); - } + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + brcmf_err("Failed to force clock for F2: err %d\n", err); + goto release; + } - brcmf_sdbrcm_release_malloc(bus); + /* Enable function 2 (frame transfers) */ + w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, + offsetof(struct sdpcmd_regs, tosbmailboxdata)); + err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); - kfree(bus); + + brcmf_dbg(INFO, "enable F2: err=%d\n", err); + + /* If F2 successfully enabled, set core and enable interrupts */ + if (!err) { + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + w_sdreg32(bus, bus->hostintmask, + offsetof(struct sdpcmd_regs, hostintmask)); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); + } else { + /* Disable F2 again */ + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + goto release; } - brcmf_dbg(TRACE, "Disconnected\n"); -} + if (brcmf_chip_sr_capable(bus->ci)) { + brcmf_sdio_sr_init(bus); + } else { + /* Restore previous clock setting */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + saveclk, &err); + } -static struct brcmf_bus_ops brcmf_sdio_bus_ops = { - .stop = brcmf_sdbrcm_bus_stop, - .init = brcmf_sdbrcm_bus_init, - .txdata = brcmf_sdbrcm_bus_txdata, - .txctl = brcmf_sdbrcm_bus_txctl, - .rxctl = brcmf_sdbrcm_bus_rxctl, - .gettxq = brcmf_sdbrcm_bus_gettxq, -}; + if (err == 0) { + err = brcmf_sdiod_intr_register(sdiodev); + if (err != 0) + brcmf_err("intr register failed:%d\n", err); + } + + /* If we didn't come up, turn off backplane clock */ + if (err != 0) + brcmf_sdio_clkctl(bus, CLK_NONE, false); + + sdio_release_host(sdiodev->func[1]); + + err = brcmf_bus_start(dev); + if (err != 0) { + brcmf_err("dongle is not responding\n"); + goto fail; + } + return; + +release: + sdio_release_host(sdiodev->func[1]); +fail: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); + device_release_driver(dev); +} -void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; - struct brcmf_bus_dcmd *dlst; - u32 dngl_txglom; - u32 txglomalign = 0; - u8 idx; brcmf_dbg(TRACE, "Enter\n"); - /* We make an assumption about address window mappings: - * regsva == SI_ENUM_BASE*/ - /* Allocate private bus interface state */ bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); if (!bus) @@ -3854,6 +4023,18 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->txminmax = BRCMF_TXMINMAX; bus->tx_seq = SDPCM_SEQ_WRAP - 1; + /* platform specific configuration: + * alignments must be at least 4 bytes for ADMA + */ + bus->head_align = ALIGNMENT; + bus->sgentry_align = ALIGNMENT; + if (sdiodev->pdata) { + if (sdiodev->pdata->sd_head_align > ALIGNMENT) + bus->head_align = sdiodev->pdata->sd_head_align; + if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) + bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; + } + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); if (bus->brcmf_wq == NULL) { @@ -3862,24 +4043,25 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) } /* attempt to attach to the dongle */ - if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { - brcmf_err("brcmf_sdbrcm_probe_attach failed\n"); + if (!(brcmf_sdio_probe_attach(bus))) { + brcmf_err("brcmf_sdio_probe_attach failed\n"); goto fail; } 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); /* Set up the watchdog timer */ init_timer(&bus->timer); bus->timer.data = (unsigned long)bus; - bus->timer.function = brcmf_sdbrcm_watchdog; + bus->timer.function = brcmf_sdio_watchdog; /* Initialize watchdog thread */ init_completion(&bus->watchdog_wait); - bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread, + bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, bus, "brcmf_watchdog"); if (IS_ERR(bus->watchdog_tsk)) { pr_warn("brcmf_watchdog thread failed to start\n"); @@ -3898,78 +4080,112 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; /* Attach to the common layer, reserve hdr space */ - ret = brcmf_attach(bus->tx_hdrlen, bus->sdiodev->dev); + ret = brcmf_attach(bus->sdiodev->dev); if (ret != 0) { brcmf_err("brcmf_attach failed\n"); goto fail; } + /* Query the F2 block size, set roundup accordingly */ + bus->blocksize = bus->sdiodev->func[2]->cur_blksize; + bus->roundup = min(max_roundup, bus->blocksize); + /* Allocate buffers */ - if (!(brcmf_sdbrcm_probe_malloc(bus))) { - brcmf_err("brcmf_sdbrcm_probe_malloc failed\n"); - goto fail; + if (bus->sdiodev->bus_if->maxctl) { + bus->sdiodev->bus_if->maxctl += bus->roundup; + bus->rxblen = + roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), + ALIGNMENT) + bus->head_align; + bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); + if (!(bus->rxbuf)) { + brcmf_err("rxbuf allocation failed\n"); + goto fail; + } } - if (!(brcmf_sdbrcm_probe_init(bus))) { - brcmf_err("brcmf_sdbrcm_probe_init failed\n"); - goto fail; - } + sdio_claim_host(bus->sdiodev->func[1]); + + /* Disable F2 to clear any intermediate frame state on the dongle */ + sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); + + bus->rxflow = false; + + /* Done with backplane-dependent accesses, can drop clock... */ + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + + sdio_release_host(bus->sdiodev->func[1]); + + /* ...and initialize clock/power states */ + bus->clkstate = CLK_SDONLY; + bus->idletime = BRCMF_IDLE_INTERVAL; + bus->idleclock = BRCMF_IDLE_ACTIVE; + + /* SR state */ + bus->sleeping = false; + bus->sr_enabled = false; brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); - /* sdio bus core specific dcmd */ - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); - dlst = kzalloc(sizeof(struct brcmf_bus_dcmd), GFP_KERNEL); - if (dlst) { - if (bus->ci->c_inf[idx].rev < 12) { - /* for sdio core rev < 12, disable txgloming */ - dngl_txglom = 0; - dlst->name = "bus:txglom"; - dlst->param = (char *)&dngl_txglom; - dlst->param_len = sizeof(u32); - } else { - /* otherwise, set txglomalign */ - if (sdiodev->pdata) - txglomalign = sdiodev->pdata->sd_sgentry_align; - /* SDIO ADMA requires at least 32 bit alignment */ - if (txglomalign < 4) - txglomalign = 4; - dlst->name = "bus:txglomalign"; - dlst->param = (char *)&txglomalign; - dlst->param_len = sizeof(u32); - } - list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list); - } - - /* if firmware path present try to download and bring up bus */ - ret = brcmf_bus_start(bus->sdiodev->dev); + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, + brcmf_sdio_get_fwname(bus->ci, + BRCMF_FIRMWARE_BIN), + brcmf_sdio_get_fwname(bus->ci, + BRCMF_FIRMWARE_NVRAM), + brcmf_sdio_firmware_callback); if (ret != 0) { - brcmf_err("dongle is not responding\n"); + brcmf_err("async firmware request failed: %d\n", ret); goto fail; } return bus; fail: - brcmf_sdbrcm_release(bus); + brcmf_sdio_remove(bus); return NULL; } -void brcmf_sdbrcm_disconnect(void *ptr) +/* Detach and free everything */ +void brcmf_sdio_remove(struct brcmf_sdio *bus) { - struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr; - brcmf_dbg(TRACE, "Enter\n"); - if (bus) - brcmf_sdbrcm_release(bus); + if (bus) { + /* De-register interrupt handler */ + brcmf_sdiod_intr_unregister(bus->sdiodev); + + brcmf_detach(bus->sdiodev->dev); + + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + + if (bus->ci) { + if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + /* Leave the device in state where it is + * 'quiet'. This is done by putting it in + * download_state which essentially resets + * all necessary cores. + */ + msleep(20); + brcmf_chip_enter_download(bus->ci); + brcmf_sdio_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); + } + brcmf_chip_detach(bus->ci); + } + + kfree(bus->rxbuf); + kfree(bus->hdrbuf); + kfree(bus); + } brcmf_dbg(TRACE, "Disconnected\n"); } -void -brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick) +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) { /* Totally stop the timer */ if (!wdtick && bus->wd_timer_valid) { @@ -3980,7 +4196,7 @@ brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick) } /* don't start the wd until fw is loaded */ - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) + if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA) return; if (wdtick) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c new file mode 100644 index 00000000000..7b7d237c1dd --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * 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/kernel.h> +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/firmware.h> + +#include "dhd_dbg.h" +#include "firmware.h" + +enum nvram_parser_state { + IDLE, + KEY, + VALUE, + COMMENT, + END +}; + +/** + * struct nvram_parser - internal info for parser. + * + * @state: current parser state. + * @fwnv: input buffer being parsed. + * @nvram: output buffer with parse result. + * @nvram_len: lenght of parse result. + * @line: current line. + * @column: current column in line. + * @pos: byte offset in input buffer. + * @entry: start position of key,value entry. + */ +struct nvram_parser { + enum nvram_parser_state state; + const struct firmware *fwnv; + u8 *nvram; + u32 nvram_len; + u32 line; + u32 column; + u32 pos; + u32 entry; +}; + +static bool is_nvram_char(char c) +{ + /* comment marker excluded */ + if (c == '#') + return false; + + /* key and value may have any other readable character */ + return (c > 0x20 && c < 0x7f); +} + +static bool is_whitespace(char c) +{ + return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); +} + +static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) +{ + char c; + + c = nvp->fwnv->data[nvp->pos]; + if (c == '\n') + return COMMENT; + if (is_whitespace(c)) + goto proceed; + if (c == '#') + return COMMENT; + if (is_nvram_char(c)) { + nvp->entry = nvp->pos; + return KEY; + } + brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n", + nvp->line, nvp->column); +proceed: + nvp->column++; + nvp->pos++; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) +{ + enum nvram_parser_state st = nvp->state; + char c; + + c = nvp->fwnv->data[nvp->pos]; + if (c == '=') { + st = VALUE; + } else if (!is_nvram_char(c)) { + brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", + nvp->line, nvp->column); + return COMMENT; + } + + nvp->column++; + nvp->pos++; + return st; +} + +static enum nvram_parser_state +brcmf_nvram_handle_value(struct nvram_parser *nvp) +{ + char c; + char *skv; + char *ekv; + u32 cplen; + + c = nvp->fwnv->data[nvp->pos]; + if (!is_nvram_char(c)) { + /* key,value pair complete */ + ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; + skv = (u8 *)&nvp->fwnv->data[nvp->entry]; + cplen = ekv - skv; + /* copy to output buffer */ + memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); + nvp->nvram_len += cplen; + nvp->nvram[nvp->nvram_len] = '\0'; + nvp->nvram_len++; + return IDLE; + } + nvp->pos++; + nvp->column++; + return VALUE; +} + +static enum nvram_parser_state +brcmf_nvram_handle_comment(struct nvram_parser *nvp) +{ + char *eol, *sol; + + sol = (char *)&nvp->fwnv->data[nvp->pos]; + eol = strchr(sol, '\n'); + if (eol == NULL) + return END; + + /* eat all moving to next line */ + nvp->line++; + nvp->column = 1; + nvp->pos += (eol - sol) + 1; + return IDLE; +} + +static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) +{ + /* final state */ + return END; +} + +static enum nvram_parser_state +(*nv_parser_states[])(struct nvram_parser *nvp) = { + brcmf_nvram_handle_idle, + brcmf_nvram_handle_key, + brcmf_nvram_handle_value, + brcmf_nvram_handle_comment, + brcmf_nvram_handle_end +}; + +static int brcmf_init_nvram_parser(struct nvram_parser *nvp, + const struct firmware *nv) +{ + memset(nvp, 0, sizeof(*nvp)); + nvp->fwnv = nv; + /* Alloc for extra 0 byte + roundup by 4 + length field */ + nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvp->nvram) + return -ENOMEM; + + nvp->line = 1; + nvp->column = 1; + return 0; +} + +/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil + * and ending in a NUL. Removes carriage returns, empty lines, comment lines, + * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. + * End of buffer is completed with token identifying length of buffer. + */ +static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) +{ + struct nvram_parser nvp; + u32 pad; + u32 token; + __le32 token_le; + + if (brcmf_init_nvram_parser(&nvp, nv) < 0) + return NULL; + + while (nvp.pos < nv->size) { + nvp.state = nv_parser_states[nvp.state](&nvp); + if (nvp.state == END) + break; + } + pad = nvp.nvram_len; + *new_length = roundup(nvp.nvram_len + 1, 4); + while (pad != *new_length) { + nvp.nvram[pad] = 0; + pad++; + } + + token = *new_length / 4; + token = (~token << 16) | (token & 0x0000FFFF); + token_le = cpu_to_le32(token); + + memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); + *new_length += sizeof(token_le); + + return nvp.nvram; +} + +void brcmf_fw_nvram_free(void *nvram) +{ + kfree(nvram); +} + +struct brcmf_fw { + struct device *dev; + u16 flags; + const struct firmware *code; + const char *nvram_name; + void (*done)(struct device *dev, const struct firmware *fw, + void *nvram_image, u32 nvram_len); +}; + +static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + u32 nvram_length = 0; + void *nvram = NULL; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + + if (fw) { + nvram = brcmf_fw_nvram_strip(fw, &nvram_length); + release_firmware(fw); + if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + } + + fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); + kfree(fwctx); + return; + +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + if (fwctx->code) + release_firmware(fwctx->code); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) +{ + struct brcmf_fw *fwctx = ctx; + int ret; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); + if (!fw) + goto fail; + + /* only requested code so done here */ + if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + fwctx->code = fw; + ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, + fwctx->dev, GFP_KERNEL, fwctx, + brcmf_fw_request_nvram_done); + + if (!ret) + return; + + /* when nvram is optional call .done() callback here */ + if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) { + fwctx->done(fwctx->dev, fw, NULL, 0); + kfree(fwctx); + return; + } + + /* failed nvram request */ + release_firmware(fw); +fail: + brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); + device_release_driver(fwctx->dev); + kfree(fwctx); +} + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + struct brcmf_fw *fwctx; + + brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); + if (!fw_cb || !code) + return -EINVAL; + + if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) + return -EINVAL; + + fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); + if (!fwctx) + return -ENOMEM; + + fwctx->dev = dev; + fwctx->flags = flags; + fwctx->done = fw_cb; + if (flags & BRCMF_FW_REQUEST_NVRAM) + fwctx->nvram_name = nvram; + + return request_firmware_nowait(THIS_MODULE, true, code, dev, + GFP_KERNEL, fwctx, + brcmf_fw_request_code_done); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h new file mode 100644 index 00000000000..6431bfd7aff --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * 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 BRCMFMAC_FIRMWARE_H +#define BRCMFMAC_FIRMWARE_H + +#define BRCMF_FW_REQUEST 0x000F +#define BRCMF_FW_REQUEST_NVRAM 0x0001 +#define BRCMF_FW_REQ_FLAGS 0x00F0 +#define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 + +void brcmf_fw_nvram_free(void *nvram); +/* + * Request firmware(s) asynchronously. When the asynchronous request + * fails it will not use the callback, but call device_release_driver() + * instead which will call the driver .remove() callback. + */ +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)); + +#endif /* BRCMFMAC_FIRMWARE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index e679214b3c9..51b53a73d07 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -102,7 +102,8 @@ struct brcmf_event; BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ + BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -114,11 +115,59 @@ enum brcmf_fweh_event_code { }; #undef BRCMF_ENUM_DEF +#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8) + /* flags field values in struct brcmf_event_msg */ #define BRCMF_EVENT_MSG_LINK 0x01 #define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 #define BRCMF_EVENT_MSG_GROUP 0x04 +/* status field values in struct brcmf_event_msg */ +#define BRCMF_E_STATUS_SUCCESS 0 +#define BRCMF_E_STATUS_FAIL 1 +#define BRCMF_E_STATUS_TIMEOUT 2 +#define BRCMF_E_STATUS_NO_NETWORKS 3 +#define BRCMF_E_STATUS_ABORT 4 +#define BRCMF_E_STATUS_NO_ACK 5 +#define BRCMF_E_STATUS_UNSOLICITED 6 +#define BRCMF_E_STATUS_ATTEMPT 7 +#define BRCMF_E_STATUS_PARTIAL 8 +#define BRCMF_E_STATUS_NEWSCAN 9 +#define BRCMF_E_STATUS_NEWASSOC 10 +#define BRCMF_E_STATUS_11HQUIET 11 +#define BRCMF_E_STATUS_SUPPRESS 12 +#define BRCMF_E_STATUS_NOCHANS 13 +#define BRCMF_E_STATUS_CS_ABORT 15 +#define BRCMF_E_STATUS_ERROR 16 + +/* reason field values in struct brcmf_event_msg */ +#define BRCMF_E_REASON_INITIAL_ASSOC 0 +#define BRCMF_E_REASON_LOW_RSSI 1 +#define BRCMF_E_REASON_DEAUTH 2 +#define BRCMF_E_REASON_DISASSOC 3 +#define BRCMF_E_REASON_BCNS_LOST 4 +#define BRCMF_E_REASON_MINTXRATE 9 +#define BRCMF_E_REASON_TXFAIL 10 + +#define BRCMF_E_REASON_LINK_BSSCFG_DIS 4 +#define BRCMF_E_REASON_FAST_ROAM_FAILED 5 +#define BRCMF_E_REASON_DIRECTED_ROAM 6 +#define BRCMF_E_REASON_TSPEC_REJECTED 7 +#define BRCMF_E_REASON_BETTER_AP 8 + +/* action field values for brcmf_ifevent */ +#define BRCMF_E_IF_ADD 1 +#define BRCMF_E_IF_DEL 2 +#define BRCMF_E_IF_CHANGE 3 + +/* flag field values for brcmf_ifevent */ +#define BRCMF_E_IF_FLAG_NOIF 1 + +/* role field values for brcmf_ifevent */ +#define BRCMF_E_IF_ROLE_STA 0 +#define BRCMF_E_IF_ROLE_AP 1 +#define BRCMF_E_IF_ROLE_WDS 2 + /** * definitions for event packet validation. */ @@ -157,6 +206,14 @@ struct brcmf_event_msg { u8 bsscfgidx; }; +struct brcmf_if_event { + u8 ifidx; + u8 action; + u8 flags; + u8 bssidx; + u8 role; +}; + typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, const struct brcmf_event_msg *evtmsg, void *data); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 04f395930d8..59a5af5bf99 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -27,6 +27,7 @@ #include "dhd_dbg.h" #include "tracepoint.h" #include "fwil.h" +#include "proto.h" #define MAX_HEX_DUMP_LEN 64 @@ -46,11 +47,9 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) if (data != NULL) len = min_t(uint, len, BRCMF_DCMD_MAXLEN); if (set) - err = brcmf_proto_cdc_set_dcmd(drvr, ifp->ifidx, cmd, data, - len); + err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, data, len); else - err = brcmf_proto_cdc_query_dcmd(drvr, ifp->ifidx, cmd, data, - len); + err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len); if (err >= 0) err = 0; @@ -69,7 +68,7 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); mutex_unlock(&ifp->drvr->proto_block); @@ -87,7 +86,7 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&ifp->drvr->proto_block); @@ -125,7 +124,8 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) } static u32 -brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) +brcmf_create_iovar(char *name, const char *data, u32 datalen, + char *buf, u32 buflen) { u32 len; @@ -145,7 +145,7 @@ brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) s32 -brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len) { struct brcmf_pub *drvr = ifp->drvr; @@ -156,7 +156,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); @@ -196,7 +196,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&drvr->proto_block); return err; @@ -279,7 +279,7 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); @@ -318,7 +318,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, } brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&drvr->proto_block); return err; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 16eb8202fb1..a30be683f4a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -17,12 +17,73 @@ #ifndef _fwil_h_ #define _fwil_h_ +/******************************************************************************* + * Dongle command codes that are interpreted by firmware + ******************************************************************************/ +#define BRCMF_C_GET_VERSION 1 +#define BRCMF_C_UP 2 +#define BRCMF_C_DOWN 3 +#define BRCMF_C_SET_PROMISC 10 +#define BRCMF_C_GET_RATE 12 +#define BRCMF_C_GET_INFRA 19 +#define BRCMF_C_SET_INFRA 20 +#define BRCMF_C_GET_AUTH 21 +#define BRCMF_C_SET_AUTH 22 +#define BRCMF_C_GET_BSSID 23 +#define BRCMF_C_GET_SSID 25 +#define BRCMF_C_SET_SSID 26 +#define BRCMF_C_TERMINATED 28 +#define BRCMF_C_GET_CHANNEL 29 +#define BRCMF_C_SET_CHANNEL 30 +#define BRCMF_C_GET_SRL 31 +#define BRCMF_C_SET_SRL 32 +#define BRCMF_C_GET_LRL 33 +#define BRCMF_C_SET_LRL 34 +#define BRCMF_C_GET_RADIO 37 +#define BRCMF_C_SET_RADIO 38 +#define BRCMF_C_GET_PHYTYPE 39 +#define BRCMF_C_SET_KEY 45 +#define BRCMF_C_SET_PASSIVE_SCAN 49 +#define BRCMF_C_SCAN 50 +#define BRCMF_C_SCAN_RESULTS 51 +#define BRCMF_C_DISASSOC 52 +#define BRCMF_C_REASSOC 53 +#define BRCMF_C_SET_ROAM_TRIGGER 55 +#define BRCMF_C_SET_ROAM_DELTA 57 +#define BRCMF_C_GET_BCNPRD 75 +#define BRCMF_C_SET_BCNPRD 76 +#define BRCMF_C_GET_DTIMPRD 77 +#define BRCMF_C_SET_DTIMPRD 78 +#define BRCMF_C_SET_COUNTRY 84 +#define BRCMF_C_GET_PM 85 +#define BRCMF_C_SET_PM 86 +#define BRCMF_C_GET_CURR_RATESET 114 +#define BRCMF_C_GET_AP 117 +#define BRCMF_C_SET_AP 118 +#define BRCMF_C_GET_RSSI 127 +#define BRCMF_C_GET_WSEC 133 +#define BRCMF_C_SET_WSEC 134 +#define BRCMF_C_GET_PHY_NOISE 135 +#define BRCMF_C_GET_BSS_INFO 136 +#define BRCMF_C_GET_BANDLIST 140 +#define BRCMF_C_SET_SCB_TIMEOUT 158 +#define BRCMF_C_GET_PHYLIST 180 +#define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 +#define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 +#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 +#define BRCMF_C_GET_VALID_CHANNELS 217 +#define BRCMF_C_GET_KEY_PRIMARY 235 +#define BRCMF_C_SET_KEY_PRIMARY 236 +#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258 +#define BRCMF_C_GET_VAR 262 +#define BRCMF_C_SET_VAR 263 + s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); s32 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data); -s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, +s32 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, u32 len); s32 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, u32 len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index ecabb04f33c..2bc68a2137f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -29,6 +29,37 @@ #define BRCMF_ARP_OL_HOST_AUTO_REPLY 0x00000004 #define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008 +#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */ +#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002 + +#define BRCMF_STA_ASSOC 0x10 /* Associated */ + +/* size of brcmf_scan_params not including variable length array */ +#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64 + +/* masks for channel and ssid count */ +#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff +#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16 + +/* primary (ie tx) key */ +#define BRCMF_PRIMARY_KEY (1 << 1) +#define DOT11_BSSTYPE_ANY 2 +#define BRCMF_ESCAN_REQ_VERSION 1 + +#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 + +/* join preference types for join_pref iovar */ +enum brcmf_join_pref_types { + BRCMF_JOIN_PREF_RSSI = 1, + BRCMF_JOIN_PREF_WPA, + BRCMF_JOIN_PREF_BAND, + BRCMF_JOIN_PREF_RSSI_DELTA, +}; enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_CLIENT, @@ -69,6 +100,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. * @@ -90,4 +126,306 @@ enum brcmf_tdls_manual_ep_ops { BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6 }; +/* Pattern matching filter. Specifies an offset within received packets to + * start matching, the pattern to match, the size of the pattern, and a bitmask + * that indicates which bits within the pattern should be matched. + */ +struct brcmf_pkt_filter_pattern_le { + /* + * Offset within received packet to start pattern matching. + * Offset '0' is the first byte of the ethernet header. + */ + __le32 offset; + /* Size of the pattern. Bitmask must be the same size.*/ + __le32 size_bytes; + /* + * Variable length mask and pattern data. mask starts at offset 0. + * Pattern immediately follows mask. + */ + u8 mask_and_pattern[1]; +}; + +/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */ +struct brcmf_pkt_filter_le { + __le32 id; /* Unique filter id, specified by app. */ + __le32 type; /* Filter type (WL_PKT_FILTER_TYPE_xxx). */ + __le32 negate_match; /* Negate the result of filter matches */ + union { /* Filter definitions */ + struct brcmf_pkt_filter_pattern_le pattern; /* Filter pattern */ + } u; +}; + +/* IOVAR "pkt_filter_enable" parameter. */ +struct brcmf_pkt_filter_enable_le { + __le32 id; /* Unique filter id */ + __le32 enable; /* Enable/disable bool */ +}; + +/* BSS info structure + * Applications MUST CHECK ie_offset field and length field to access IEs and + * next bss_info structure in a vector (in struct brcmf_scan_results) + */ +struct brcmf_bss_info_le { + __le32 version; /* version field */ + __le32 length; /* byte length of data in this record, + * starting at version and including IEs + */ + u8 BSSID[ETH_ALEN]; + __le16 beacon_period; /* units are Kusec */ + __le16 capability; /* Capability information */ + u8 SSID_len; + u8 SSID[32]; + struct { + __le32 count; /* # rates in this set */ + u8 rates[16]; /* rates in 500kbps units w/hi bit set if basic */ + } rateset; /* supported rates */ + __le16 chanspec; /* chanspec for bss */ + __le16 atim_window; /* units are Kusec */ + u8 dtim_period; /* DTIM period */ + __le16 RSSI; /* receive signal strength (in dBm) */ + s8 phy_noise; /* noise (in dBm) */ + + u8 n_cap; /* BSS is 802.11N Capable */ + /* 802.11N BSS Capabilities (based on HT_CAP_*): */ + __le32 nbss_cap; + u8 ctl_ch; /* 802.11N BSS control channel number */ + __le32 reserved32[1]; /* Reserved for expansion of BSS properties */ + u8 flags; /* flags */ + u8 reserved[3]; /* Reserved for expansion of BSS properties */ + u8 basic_mcs[MCSSET_LEN]; /* 802.11N BSS required MCS set */ + + __le16 ie_offset; /* offset at which IEs start, from beginning */ + __le32 ie_length; /* byte length of Information Elements */ + __le16 SNR; /* average SNR of during frame reception */ + /* Add new fields here */ + /* variable length Information Elements */ +}; + +struct brcm_rateset_le { + /* # rates in this set */ + __le32 count; + /* rates in 500kbps units w/hi bit set if basic */ + u8 rates[BRCMF_MAXRATES_IN_SET]; +}; + +struct brcmf_ssid { + u32 SSID_len; + unsigned char SSID[32]; +}; + +struct brcmf_ssid_le { + __le32 SSID_len; + unsigned char SSID[32]; +}; + +struct brcmf_scan_params_le { + struct brcmf_ssid_le ssid_le; /* default: {0, ""} */ + u8 bssid[ETH_ALEN]; /* default: bcast */ + s8 bss_type; /* default: any, + * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT + */ + u8 scan_type; /* flags, 0 use default */ + __le32 nprobes; /* -1 use default, number of probes per channel */ + __le32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + __le32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + __le32 home_time; /* -1 use default, dwell time for the + * home channel between channel scans + */ + __le32 channel_num; /* count of channels and ssids that follow + * + * low half is count of channels in + * channel_list, 0 means default (use all + * available channels) + * + * high half is entries in struct brcmf_ssid + * array that follows channel_list, aligned for + * s32 (4 bytes) meaning an odd channel count + * implies a 2-byte pad between end of + * channel_list and first ssid + * + * if ssid count is zero, single ssid in the + * fixed parameter portion is assumed, otherwise + * ssid in the fixed portion is ignored + */ + __le16 channel_list[1]; /* list of chanspecs */ +}; + +struct brcmf_scan_results { + u32 buflen; + u32 version; + u32 count; + struct brcmf_bss_info_le bss_info_le[]; +}; + +struct brcmf_escan_params_le { + __le32 version; + __le16 action; + __le16 sync_id; + struct brcmf_scan_params_le params_le; +}; + +struct brcmf_escan_result_le { + __le32 buflen; + __le32 version; + __le16 sync_id; + __le16 bss_count; + struct brcmf_bss_info_le bss_info_le; +}; + +#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \ + sizeof(struct brcmf_bss_info_le)) + +/* used for association with a specific BSSID and chanspec list */ +struct brcmf_assoc_params_le { + /* 00:00:00:00:00:00: broadcast scan */ + u8 bssid[ETH_ALEN]; + /* 0: all available channels, otherwise count of chanspecs in + * chanspec_list */ + __le32 chanspec_num; + /* list of chanspecs */ + __le16 chanspec_list[1]; +}; + +/** + * struct join_pref params - parameters for preferred join selection. + * + * @type: preference type (see enum brcmf_join_pref_types). + * @len: length of bytes following (currently always 2). + * @rssi_gain: signal gain for selection (only when @type is RSSI_DELTA). + * @band: band to which selection preference applies. + * This is used if @type is BAND or RSSI_DELTA. + */ +struct brcmf_join_pref_params { + u8 type; + u8 len; + u8 rssi_gain; + u8 band; +}; + +/* used for join with or without a specific bssid and channel list */ +struct brcmf_join_params { + struct brcmf_ssid_le ssid_le; + struct brcmf_assoc_params_le params_le; +}; + +/* scan params for extended join */ +struct brcmf_join_scan_params_le { + u8 scan_type; /* 0 use default, active or passive scan */ + __le32 nprobes; /* -1 use default, nr of probes per channel */ + __le32 active_time; /* -1 use default, dwell time per channel for + * active scanning + */ + __le32 passive_time; /* -1 use default, dwell time per channel + * for passive scanning + */ + __le32 home_time; /* -1 use default, dwell time for the home + * channel between channel scans + */ +}; + +/* extended join params */ +struct brcmf_ext_join_params_le { + struct brcmf_ssid_le ssid_le; /* {0, ""}: wildcard scan */ + struct brcmf_join_scan_params_le scan_le; + struct brcmf_assoc_params_le assoc_le; +}; + +struct brcmf_wsec_key { + u32 index; /* key index */ + u32 len; /* key length */ + u8 data[WLAN_MAX_KEY_LEN]; /* key data */ + u32 pad_1[18]; + u32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ + u32 flags; /* misc flags */ + u32 pad_2[3]; + u32 iv_initialized; /* has IV been initialized already? */ + u32 pad_3; + /* Rx IV */ + struct { + u32 hi; /* upper 32 bits of IV */ + u16 lo; /* lower 16 bits of IV */ + } rxiv; + u32 pad_4[2]; + u8 ea[ETH_ALEN]; /* per station */ +}; + +/* + * dongle requires same struct as above but with fields in little endian order + */ +struct brcmf_wsec_key_le { + __le32 index; /* key index */ + __le32 len; /* key length */ + u8 data[WLAN_MAX_KEY_LEN]; /* key data */ + __le32 pad_1[18]; + __le32 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */ + __le32 flags; /* misc flags */ + __le32 pad_2[3]; + __le32 iv_initialized; /* has IV been initialized already? */ + __le32 pad_3; + /* Rx IV */ + struct { + __le32 hi; /* upper 32 bits of IV */ + __le16 lo; /* lower 16 bits of IV */ + } rxiv; + __le32 pad_4[2]; + u8 ea[ETH_ALEN]; /* per station */ +}; + +/* Used to get specific STA parameters */ +struct brcmf_scb_val_le { + __le32 val; + u8 ea[ETH_ALEN]; +}; + +/* channel encoding */ +struct brcmf_channel_info_le { + __le32 hw_channel; + __le32 target_channel; + __le32 scan_channel; +}; + +struct brcmf_sta_info_le { + __le16 ver; /* version of this struct */ + __le16 len; /* length in bytes of this structure */ + __le16 cap; /* sta's advertised capabilities */ + __le32 flags; /* flags defined below */ + __le32 idle; /* time since data pkt rx'd from sta */ + u8 ea[ETH_ALEN]; /* Station address */ + __le32 count; /* # rates in this set */ + u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */ + /* w/hi bit set if basic */ + __le32 in; /* seconds elapsed since associated */ + __le32 listen_interval_inms; /* Min Listen interval in ms for STA */ + __le32 tx_pkts; /* # of packets transmitted */ + __le32 tx_failures; /* # of packets failed */ + __le32 rx_ucast_pkts; /* # of unicast packets received */ + __le32 rx_mcast_pkts; /* # of multicast packets received */ + __le32 tx_rate; /* Rate of last successful tx frame */ + __le32 rx_rate; /* Rate of last successful rx frame */ + __le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */ + __le32 rx_decrypt_failures; /* # of packet decrypted failed */ +}; + +struct brcmf_chanspec_list { + __le32 count; /* # of entries */ + __le32 element[1]; /* variable length uint32 list */ +}; + +/* + * WLC_E_PROBRESP_MSG + * WLC_E_P2P_PROBREQ_MSG + * WLC_E_ACTION_FRAME_RX + */ +struct brcmf_rx_mgmt_data { + __be16 version; + __be16 chanspec; + __be32 rssi; + __be32 mactime; + __be32 rate; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 82f9140f3d3..699908de314 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -27,7 +27,6 @@ #include <brcmu_utils.h> #include <brcmu_wifi.h> #include "dhd.h" -#include "dhd_proto.h" #include "dhd_dbg.h" #include "dhd_bus.h" #include "fwil.h" @@ -36,6 +35,7 @@ #include "fwsignal.h" #include "p2p.h" #include "wl_cfg80211.h" +#include "proto.h" /** * DOC: Firmware Signalling @@ -105,6 +105,7 @@ static struct { }; #undef BRCMF_FWS_TLV_DEF + static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) { int i; @@ -123,6 +124,12 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) #endif /* DEBUG */ /* + * The PKTTAG tlv has additional bytes when firmware-signalling + * mode has REUSESEQ flag set. + */ +#define BRCMF_FWS_TYPE_SEQ_LEN 2 + +/* * flags used to enable tlv signalling from firmware. */ #define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 @@ -147,8 +154,15 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) #define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 #define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02 -#define BRCMF_FWS_RET_OK_NOSCHEDULE 0 -#define BRCMF_FWS_RET_OK_SCHEDULE 1 +#define BRCMF_FWS_RET_OK_NOSCHEDULE 0 +#define BRCMF_FWS_RET_OK_SCHEDULE 1 + +#define BRCMF_FWS_MODE_REUSESEQ_SHIFT 3 /* seq reuse */ +#define BRCMF_FWS_MODE_SET_REUSESEQ(x, val) ((x) = \ + ((x) & ~(1 << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) | \ + (((val) & 1) << BRCMF_FWS_MODE_REUSESEQ_SHIFT)) +#define BRCMF_FWS_MODE_GET_REUSESEQ(x) \ + (((x) >> BRCMF_FWS_MODE_REUSESEQ_SHIFT) & 1) /** * enum brcmf_fws_skb_state - indicates processing state of skb. @@ -168,8 +182,10 @@ enum brcmf_fws_skb_state { /** * struct brcmf_skbuff_cb - control buffer associated with skbuff. * + * @bus_flags: 2 bytes reserved for bus specific parameters * @if_flags: holds interface index and packet related flags. * @htod: host to device packet identifier (used in PKTTAG tlv). + * @htod_seq: this 16-bit is original seq number for every suppress packet. * @state: transmit state of the packet. * @mac: descriptor related to destination for this packet. * @@ -177,8 +193,10 @@ enum brcmf_fws_skb_state { * provides 48 bytes of storage so this structure should not exceed that. */ struct brcmf_skbuff_cb { + u16 bus_flags; u16 if_flags; u32 htod; + u16 htod_seq; enum brcmf_fws_skb_state state; struct brcmf_fws_mac_descriptor *mac; }; @@ -255,6 +273,22 @@ struct brcmf_skbuff_cb { BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) +#define BRCMF_SKB_HTOD_SEQ_FROMFW_MASK 0x2000 +#define BRCMF_SKB_HTOD_SEQ_FROMFW_SHIFT 13 +#define BRCMF_SKB_HTOD_SEQ_FROMDRV_MASK 0x1000 +#define BRCMF_SKB_HTOD_SEQ_FROMDRV_SHIFT 12 +#define BRCMF_SKB_HTOD_SEQ_NR_MASK 0x0fff +#define BRCMF_SKB_HTOD_SEQ_NR_SHIFT 0 + +#define brcmf_skb_htod_seq_set_field(skb, field, value) \ + brcmu_maskset16(&(brcmf_skbcb(skb)->htod_seq), \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT, (value)) +#define brcmf_skb_htod_seq_get_field(skb, field) \ + brcmu_maskget16(brcmf_skbcb(skb)->htod_seq, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_SEQ_ ## field ## _SHIFT) + #define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000 #define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31 #define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000 @@ -263,8 +297,8 @@ struct brcmf_skbuff_cb { #define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24 #define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00 #define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8 -#define BRCMF_FWS_TXSTAT_PKTID_MASK 0x00FFFFFF -#define BRCMF_FWS_TXSTAT_PKTID_SHIFT 0 +#define BRCMF_FWS_TXSTAT_FREERUN_MASK 0x000000FF +#define BRCMF_FWS_TXSTAT_FREERUN_SHIFT 0 #define brcmf_txstatus_get_field(txs, field) \ brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ @@ -441,6 +475,8 @@ struct brcmf_fws_info { unsigned long borrow_defer_timestamp; bool bus_flow_blocked; bool creditmap_received; + u8 mode; + bool avoid_queueing; }; /* @@ -803,20 +839,23 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); } -static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) +static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u8 *wlh; u16 data_offset = 0; u8 fillers; __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); + __le16 pktseq = cpu_to_le16(brcmf_skbcb(skb)->htod_seq); - brcmf_dbg(TRACE, "enter: %s, idx=%d pkttag=0x%08X, hslot=%d\n", + brcmf_dbg(TRACE, "enter: %s, idx=%d hslot=%d htod %X seq %X\n", entry->name, brcmf_skb_if_flags_get_field(skb, INDEX), - le32_to_cpu(pkttag), (le32_to_cpu(pkttag) >> 8) & 0xffff); + (le32_to_cpu(pkttag) >> 8) & 0xffff, + brcmf_skbcb(skb)->htod, brcmf_skbcb(skb)->htod_seq); if (entry->send_tim_signal) data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; - + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) + data_offset += BRCMF_FWS_TYPE_SEQ_LEN; /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; fillers = round_up(data_offset, 4) - data_offset; @@ -828,7 +867,12 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) wlh[0] = BRCMF_FWS_TYPE_PKTTAG; wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; memcpy(&wlh[2], &pkttag, sizeof(pkttag)); - wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { + wlh[1] += BRCMF_FWS_TYPE_SEQ_LEN; + memcpy(&wlh[2 + BRCMF_FWS_TYPE_PKTTAG_LEN], &pktseq, + sizeof(pktseq)); + } + wlh += wlh[1] + 2; if (entry->send_tim_signal) { entry->send_tim_signal = 0; @@ -844,9 +888,7 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) if (fillers) memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); - brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), - data_offset >> 2, skb); - return 0; + return (u8)(data_offset >> 2); } static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, @@ -854,10 +896,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, int fifo, bool send_immediately) { struct sk_buff *skb; - struct brcmf_bus *bus; struct brcmf_skbuff_cb *skcb; s32 err; u32 len; + u8 data_offset; + int ifidx; /* check delayedQ and suppressQ in one call using bitmap */ if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0) @@ -873,6 +916,7 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, /* create a dummy packet and sent that. The traffic */ /* bitmap info will automatically be attached to that packet */ len = BRCMF_FWS_TYPE_PKTTAG_LEN + 2 + + BRCMF_FWS_TYPE_SEQ_LEN + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2 + 4 + fws->drvr->hdrlen; skb = brcmu_pkt_buf_get_skb(len); @@ -882,13 +926,13 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, skcb = brcmf_skbcb(skb); skcb->mac = entry; skcb->state = BRCMF_FWS_SKBSTATE_TIM; - bus = fws->drvr->bus_if; - err = brcmf_fws_hdrpush(fws, skb); - if (err == 0) { - brcmf_fws_unlock(fws); - err = brcmf_bus_txdata(bus, skb); - brcmf_fws_lock(fws); - } + skcb->htod = 0; + skcb->htod_seq = 0; + data_offset = brcmf_fws_hdrpush(fws, skb); + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + brcmf_fws_unlock(fws); + err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); + brcmf_fws_lock(fws); if (err) brcmu_pkt_buf_free_skb(skb); return true; @@ -1170,8 +1214,13 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, { int prec = 2 * fifo; u32 *qfull_stat = &fws->stats.delayq_full_error; - struct brcmf_fws_mac_descriptor *entry; + struct pktq *pq; + struct sk_buff_head *queue; + struct sk_buff *p_head; + struct sk_buff *p_tail; + u32 fr_new; + u32 fr_compare; entry = brcmf_skbcb(p)->mac; if (entry == NULL) { @@ -1183,9 +1232,55 @@ static int brcmf_fws_enq(struct brcmf_fws_info *fws, if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { prec += 1; qfull_stat = &fws->stats.supprq_full_error; - } - if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { + /* Fix out of order delivery of frames. Dont assume frame */ + /* can be inserted at the end, but look for correct position */ + pq = &entry->psq; + if (pktq_full(pq) || pktq_pfull(pq, prec)) { + *qfull_stat += 1; + return -ENFILE; + } + queue = &pq->q[prec].skblist; + + p_head = skb_peek(queue); + p_tail = skb_peek_tail(queue); + fr_new = brcmf_skb_htod_tag_get_field(p, FREERUN); + + while (p_head != p_tail) { + fr_compare = brcmf_skb_htod_tag_get_field(p_tail, + FREERUN); + /* be sure to handle wrap of 256 */ + if (((fr_new > fr_compare) && + ((fr_new - fr_compare) < 128)) || + ((fr_new < fr_compare) && + ((fr_compare - fr_new) > 128))) + break; + p_tail = skb_queue_prev(queue, p_tail); + } + /* Position found. Determine what to do */ + if (p_tail == NULL) { + /* empty list */ + __skb_queue_tail(queue, p); + } else { + fr_compare = brcmf_skb_htod_tag_get_field(p_tail, + FREERUN); + if (((fr_new > fr_compare) && + ((fr_new - fr_compare) < 128)) || + ((fr_new < fr_compare) && + ((fr_compare - fr_new) > 128))) { + /* After tail */ + __skb_queue_after(queue, p_tail, p); + } else { + /* Before tail */ + __skb_insert(p, p_tail->prev, p_tail, queue); + } + } + + /* Complete the counters and statistics */ + pq->len++; + if (pq->hi_prec < prec) + pq->hi_prec = (u8) prec; + } else if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { *qfull_stat += 1; return -ENFILE; } @@ -1275,12 +1370,12 @@ done: } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u32 genbit) + struct sk_buff *skb, u8 ifidx, + u32 genbit, u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u32 hslot; int ret; - u8 ifidx; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); @@ -1294,19 +1389,21 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->generation = genbit; - ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (ret == 0) - ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, - skb); + brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); + brcmf_skbcb(skb)->htod_seq = seq; + if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 1); + brcmf_skb_htod_seq_set_field(skb, FROMFW, 0); + } else { + brcmf_skb_htod_seq_set_field(skb, FROMDRV, 0); + } + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + if (ret != 0) { - /* suppress q is full or hdrpull failed, drop this packet */ - brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, - true); + /* suppress q is full drop this packet */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); } else { - /* - * Mark suppressed to avoid a double free during - * wlfc cleanup - */ + /* Mark suppressed to avoid a double free during wlfc cleanup */ brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot); } @@ -1315,7 +1412,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, static int brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, - u32 genbit) + u32 genbit, u16 seq) { u32 fifo; int ret; @@ -1323,6 +1420,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, struct sk_buff *skb; struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; + u8 ifidx; brcmf_dbg(DATA, "flags %d\n", flags); @@ -1358,8 +1456,8 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, if (entry->suppressed && entry->suppr_transit_count) entry->suppr_transit_count--; - brcmf_dbg(DATA, "%s flags %X htod %X\n", entry->name, skcb->if_flags, - skcb->htod); + brcmf_dbg(DATA, "%s flags %d htod %X seq %X\n", entry->name, flags, + skcb->htod, seq); /* pick up the implicit credit from this packet */ fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); @@ -1371,11 +1469,15 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } brcmf_fws_macdesc_return_req_credit(skb); + if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) { + brcmu_pkt_buf_free_skb(skb); + return -EINVAL; + } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); - + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx, + genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, true); + brcmf_txfinalize(fws->drvr, skb, ifidx, true); return 0; } @@ -1404,10 +1506,12 @@ static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) { __le32 status_le; + __le16 seq_le; u32 status; u32 hslot; u32 genbit; u8 flags; + u16 seq; fws->stats.txs_indicate++; memcpy(&status_le, data, sizeof(status_le)); @@ -1415,9 +1519,16 @@ static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) flags = brcmf_txstatus_get_field(status, FLAGS); hslot = brcmf_txstatus_get_field(status, HSLOT); genbit = brcmf_txstatus_get_field(status, GENERATION); + if (BRCMF_FWS_MODE_GET_REUSESEQ(fws->mode)) { + memcpy(&seq_le, &data[BRCMF_FWS_TYPE_PKTTAG_LEN], + sizeof(seq_le)); + seq = le16_to_cpu(seq_le); + } else { + seq = 0; + } brcmf_fws_lock(fws); - brcmf_fws_txs_process(fws, flags, hslot, genbit); + brcmf_fws_txs_process(fws, flags, hslot, genbit, seq); brcmf_fws_unlock(fws); return BRCMF_FWS_RET_OK_NOSCHEDULE; } @@ -1601,15 +1712,15 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, return 0; } -static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, +static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, struct sk_buff *p) { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); struct brcmf_fws_mac_descriptor *entry = skcb->mac; u8 flags; - brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); - brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); + if (skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED) + brcmf_skb_htod_tag_set_field(p, GENERATION, entry->generation); flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; if (brcmf_skb_if_flags_get_field(p, REQUESTED)) { /* @@ -1619,7 +1730,7 @@ static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; } brcmf_skb_htod_tag_set_field(p, FLAGS, flags); - brcmf_fws_hdrpush(fws, p); + return brcmf_fws_hdrpush(fws, p); } static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, @@ -1650,7 +1761,7 @@ static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, fws->stats.rollback_failed++; hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, - hslot, 0); + hslot, 0, 0); } else { fws->stats.rollback_success++; brcmf_fws_return_credits(fws, fifo, 1); @@ -1687,20 +1798,21 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); struct brcmf_fws_mac_descriptor *entry; - struct brcmf_bus *bus = fws->drvr->bus_if; int rc; u8 ifidx; + u8 data_offset; entry = skcb->mac; if (IS_ERR(entry)) return PTR_ERR(entry); - brcmf_fws_precommit_skb(fws, fifo, skb); + data_offset = brcmf_fws_precommit_skb(fws, fifo, skb); entry->transit_count++; if (entry->suppressed) entry->suppr_transit_count++; + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); brcmf_fws_unlock(fws); - rc = brcmf_bus_txdata(bus, skb); + rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); brcmf_fws_lock(fws); brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, skcb->if_flags, skcb->htod, rc); @@ -1730,6 +1842,8 @@ static int brcmf_fws_assign_htod(struct brcmf_fws_info *fws, struct sk_buff *p, struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); int rc, hslot; + skcb->htod = 0; + skcb->htod_seq = 0; hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); brcmf_skb_htod_tag_set_field(p, FREERUN, skcb->mac->seq[fifo]); @@ -1750,16 +1864,21 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) struct ethhdr *eh = (struct ethhdr *)(skb->data); int fifo = BRCMF_FWS_FIFO_BCMC; bool multicast = is_multicast_ether_addr(eh->h_dest); - bool pae = eh->h_proto == htons(ETH_P_PAE); + int rc = 0; brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto)); /* determine the priority */ if (!skb->priority) - skb->priority = cfg80211_classify8021d(skb); + skb->priority = cfg80211_classify8021d(skb, NULL); drvr->tx_multicast += !!multicast; - if (pae) - atomic_inc(&ifp->pend_8021x_cnt); + + if (fws->avoid_queueing) { + rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); + if (rc < 0) + brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + return rc; + } /* set control buffer information */ skcb->if_flags = 0; @@ -1781,15 +1900,12 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_schedule_deq(fws); } else { brcmf_err("drop skb: no hanger slot\n"); - if (pae) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } - brcmu_pkt_buf_free_skb(skb); + brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + rc = -ENOMEM; } brcmf_fws_unlock(fws); - return 0; + + return rc; } void brcmf_fws_reset_interface(struct brcmf_if *ifp) @@ -1859,13 +1975,13 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) &skb, true); ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); - brcmf_proto_hdrpush(drvr, ifidx, 0, skb); - /* Use bus module to send data frame */ + /* Use proto layer to send data frame */ brcmf_fws_unlock(fws); - ret = brcmf_bus_txdata(drvr->bus_if, skb); + ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) - brcmf_txfinalize(drvr, skb, false); + brcmf_txfinalize(drvr, skb, ifidx, + false); if (fws->bus_flow_blocked) break; } @@ -1906,6 +2022,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr) struct brcmf_fws_info *fws; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; int rc; + u32 mode; drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); if (!drvr->fws) { @@ -1921,6 +2038,13 @@ int brcmf_fws_init(struct brcmf_pub *drvr) fws->drvr = drvr; fws->fcmode = fcmode; + if ((drvr->bus_if->always_use_fws_queue == false) && + (fcmode == BRCMF_FWS_FCMODE_NONE)) { + fws->avoid_queueing = true; + brcmf_dbg(INFO, "FWS queueing will be avoided\n"); + return 0; + } + fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); if (fws->fws_wq == NULL) { brcmf_err("workqueue creation failed\n"); @@ -1964,6 +2088,18 @@ int brcmf_fws_init(struct brcmf_pub *drvr) if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); + /* Enable seq number reuse, if supported */ + if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) { + if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { + mode = 0; + BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); + if (brcmf_fil_iovar_int_set(drvr->iflist[0], + "wlfc_mode", mode) == 0) { + BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); + } + } + } + brcmf_fws_hanger_init(&fws->hanger); brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0); brcmf_fws_macdesc_set_name(fws, &fws->desc.other); @@ -2020,7 +2156,7 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) } brcmf_fws_lock(fws); hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0); brcmf_fws_unlock(fws); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index d7a97453290..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; @@ -812,7 +813,7 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan = request->channels[i]; if (chan->flags & (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_PASSIVE_SCAN)) + IEEE80211_CHAN_NO_IR)) continue; chanspecs[i] = channel_to_chanspec(&p2p->cfg->d11inf, @@ -823,6 +824,7 @@ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, } err = brcmf_p2p_escan(p2p, num_nodfs, chanspecs, search_state, action, P2PAPI_BSSCFG_DEVICE); + kfree(chanspecs); } exit: if (err) @@ -1148,7 +1150,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) pri_vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - INIT_COMPLETION(afx_hdl->act_frm_scan); + reinit_completion(&afx_hdl->act_frm_scan); set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); afx_hdl->is_active = true; afx_hdl->peer_chan = P2P_INVALID_CHANNEL; @@ -1242,7 +1244,7 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, IEEE80211_P2P_ATTR_DEVICE_ID, p2p_dev_addr, sizeof(p2p_dev_addr)); if ((err >= 0) && - (!memcmp(p2p_dev_addr, afx_hdl->tx_dst_addr, ETH_ALEN))) { + (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) { if (!bi->ctl_ch) { ch.chspec = le16_to_cpu(bi->chanspec); cfg->d11inf.decchspec(&ch); @@ -1379,8 +1381,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, (brcmf_p2p_gon_req_collision(p2p, (u8 *)e->addr))) { if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && - (memcmp(afx_hdl->tx_dst_addr, e->addr, - ETH_ALEN) == 0)) { + (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { afx_hdl->peer_chan = ch.chnum; brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n", afx_hdl->peer_chan); @@ -1501,7 +1502,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, brcmf_dbg(TRACE, "Enter\n"); - INIT_COMPLETION(p2p->send_af_done); + reinit_completion(&p2p->send_af_done); clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); @@ -1864,7 +1865,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, cfg->d11inf.decchspec(&ch); if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && - (memcmp(afx_hdl->tx_dst_addr, e->addr, ETH_ALEN) == 0)) { + (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { afx_hdl->peer_chan = ch.chnum; brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n", afx_hdl->peer_chan); @@ -1955,21 +1956,21 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); if (err < 0) { brcmf_err("set p2p_disc error\n"); - brcmf_free_vif(cfg, p2p_vif); + brcmf_free_vif(p2p_vif); goto exit; } /* obtain bsscfg index for P2P discovery */ err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx); if (err < 0) { brcmf_err("retrieving discover bsscfg index failed\n"); - brcmf_free_vif(cfg, p2p_vif); + brcmf_free_vif(p2p_vif); goto exit; } /* Verify that firmware uses same bssidx as driver !! */ if (p2p_ifp->bssidx != bssidx) { brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n", bssidx, p2p_ifp->bssidx); - brcmf_free_vif(cfg, p2p_vif); + brcmf_free_vif(p2p_vif); goto exit; } @@ -1997,7 +1998,7 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p) brcmf_p2p_cancel_remain_on_channel(vif->ifp); brcmf_p2p_deinit_discovery(p2p); /* remove discovery interface */ - brcmf_free_vif(p2p->cfg, vif); + brcmf_free_vif(vif); p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; } /* just set it all to zero */ @@ -2222,7 +2223,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, return &p2p_vif->wdev; fail: - brcmf_free_vif(p2p->cfg, p2p_vif); + brcmf_free_vif(p2p_vif); return ERR_PTR(err); } @@ -2231,31 +2232,12 @@ fail: * * @vif: virtual interface object to delete. */ -static void brcmf_p2p_delete_p2pdev(struct brcmf_cfg80211_info *cfg, +static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p, struct brcmf_cfg80211_vif *vif) { cfg80211_unregister_wdev(&vif->wdev); - cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - brcmf_free_vif(cfg, vif); -} - -/** - * brcmf_p2p_free_p2p_if() - free up net device related data. - * - * @ndev: net device that needs to be freed. - */ -static void brcmf_p2p_free_p2p_if(struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg; - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - - ifp = netdev_priv(ndev); - cfg = ifp->drvr->config; - vif = ifp->vif; - - brcmf_free_vif(cfg, vif); - free_netdev(ifp->ndev); + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; + brcmf_free_vif(vif); } /** @@ -2275,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)) @@ -2286,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, @@ -2335,8 +2314,6 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, brcmf_err("Registering netdevice failed\n"); goto fail; } - /* override destructor */ - ifp->ndev->destructor = brcmf_p2p_free_p2p_if; cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif; /* Disable firmware roaming for P2P interface */ @@ -2349,7 +2326,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, return &ifp->vif->wdev; fail: - brcmf_free_vif(cfg, vif); + brcmf_free_vif(vif); return ERR_PTR(err); } @@ -2358,8 +2335,6 @@ fail: * * @wiphy: wiphy device of interface. * @wdev: wireless device of interface. - * - * TODO: not yet supported. */ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) { @@ -2385,7 +2360,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) break; case NL80211_IFTYPE_P2P_DEVICE: - brcmf_p2p_delete_p2pdev(cfg, vif); + brcmf_p2p_delete_p2pdev(p2p, vif); return 0; default: return -ENOTSUPP; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c new file mode 100644 index 00000000000..b6b46418494 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * 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/types.h> +#include <linux/slab.h> +#include <linux/netdevice.h> + +#include <brcmu_wifi.h> +#include "dhd.h" +#include "dhd_dbg.h" +#include "proto.h" +#include "bcdc.h" + + +int brcmf_proto_attach(struct brcmf_pub *drvr) +{ + struct brcmf_proto *proto; + + proto = kzalloc(sizeof(*proto), GFP_ATOMIC); + if (!proto) + goto fail; + + drvr->proto = proto; + /* BCDC protocol is only protocol supported for the moment */ + if (brcmf_proto_bcdc_attach(drvr)) + goto fail; + + if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || + (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL)) { + brcmf_err("Not all proto handlers have been installed\n"); + goto fail; + } + return 0; + +fail: + kfree(proto); + drvr->proto = NULL; + return -ENOMEM; +} + +void brcmf_proto_detach(struct brcmf_pub *drvr) +{ + if (drvr->proto) { + brcmf_proto_bcdc_detach(drvr); + kfree(drvr->proto); + drvr->proto = NULL; + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h new file mode 100644 index 00000000000..482fb0ba4a3 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 Broadcom Corporation + * + * 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 BRCMFMAC_PROTO_H +#define BRCMFMAC_PROTO_H + +struct brcmf_proto { + int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, + struct sk_buff *skb); + int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len); + int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, + uint len); + int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *skb); + void *pd; +}; + + +int brcmf_proto_attach(struct brcmf_pub *drvr); +void brcmf_proto_detach(struct brcmf_pub *drvr); + +static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, + u8 *ifidx, struct sk_buff *skb) +{ + return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb); +} +static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return drvr->proto->query_dcmd(drvr, ifidx, cmd, buf, len); +} +static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, + uint cmd, void *buf, uint len) +{ + return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); +} +static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + return drvr->proto->txdata(drvr, ifidx, offset, skb); +} + + +#endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c deleted file mode 100644 index ca72177388b..00000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ /dev/null @@ -1,955 +0,0 @@ -/* - * Copyright (c) 2011 Broadcom Corporation - * - * 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. - */ -/* ***** SDIO interface chip backplane handle functions ***** */ - -#include <linux/types.h> -#include <linux/netdevice.h> -#include <linux/mmc/card.h> -#include <linux/ssb/ssb_regs.h> -#include <linux/bcma/bcma.h> - -#include <chipcommon.h> -#include <brcm_hw_ids.h> -#include <brcmu_wifi.h> -#include <brcmu_utils.h> -#include <soc.h> -#include "dhd_dbg.h" -#include "sdio_host.h" -#include "sdio_chip.h" - -/* chip core base & ramsize */ -/* bcm4329 */ -/* SDIO device core, ID 0x829 */ -#define BCM4329_CORE_BUS_BASE 0x18011000 -/* internal memory core, ID 0x80e */ -#define BCM4329_CORE_SOCRAM_BASE 0x18003000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM4329_CORE_ARM_BASE 0x18002000 -#define BCM4329_RAMSIZE 0x48000 - -/* bcm43143 */ -/* SDIO device core */ -#define BCM43143_CORE_BUS_BASE 0x18002000 -/* internal memory core */ -#define BCM43143_CORE_SOCRAM_BASE 0x18004000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM43143_CORE_ARM_BASE 0x18003000 -#define BCM43143_RAMSIZE 0x70000 - -#define SBCOREREV(sbidh) \ - ((((sbidh) & SSB_IDHIGH_RCHI) >> SSB_IDHIGH_RCHI_SHIFT) | \ - ((sbidh) & SSB_IDHIGH_RCLO)) - -/* SOC Interconnect types (aka chip types) */ -#define SOCI_SB 0 -#define SOCI_AI 1 - -/* EROM CompIdentB */ -#define CIB_REV_MASK 0xff000000 -#define CIB_REV_SHIFT 24 - -/* ARM CR4 core specific control flag bits */ -#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 - -#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) -/* SDIO Pad drive strength to select value mappings */ -struct sdiod_drive_str { - u8 strength; /* Pad Drive Strength in mA */ - u8 sel; /* Chip-specific select value */ -}; -/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { - {32, 0x6}, - {26, 0x7}, - {22, 0x4}, - {16, 0x5}, - {12, 0x2}, - {8, 0x3}, - {4, 0x0}, - {0, 0x1} -}; - -/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { - {16, 0x7}, - {12, 0x5}, - {8, 0x3}, - {4, 0x1} -}; - -u8 -brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid) -{ - u8 idx; - - for (idx = 0; idx < BRCMF_MAX_CORENUM; idx++) - if (coreid == ci->c_inf[idx].id) - return idx; - - return BRCMF_MAX_CORENUM; -} - -static u32 -brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) -{ - u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbidhigh), - NULL); - return SBCOREREV(regdata); -} - -static u32 -brcmf_sdio_ai_corerev(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) -{ - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - return (ci->c_inf[idx].cib & CIB_REV_MASK) >> CIB_REV_SHIFT; -} - -static bool -brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) -{ - u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | - SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); - return (SSB_TMSLOW_CLOCK == regdata); -} - -static bool -brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid) -{ - u32 regdata; - u8 idx; - bool ret; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; - - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); - ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); - - return ret; -} - -static void -brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) -{ - u32 regdata, base; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - base = ci->c_inf[idx].base; - - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); - if (regdata & SSB_TMSLOW_RESET) - return; - - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); - if ((regdata & SSB_TMSLOW_CLOCK) != 0) { - /* - * set target reject and spin until busy is clear - * (preserve core-specific bits) - */ - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), - NULL); - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata | SSB_TMSLOW_REJECT, NULL); - - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), - NULL); - udelay(1); - SPINWAIT((brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL) & - SSB_TMSHIGH_BUSY), 100000); - - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL); - if (regdata & SSB_TMSHIGH_BUSY) - brcmf_err("core state still busy\n"); - - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); - if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - regdata |= SSB_IMSTATE_REJECT; - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - udelay(1); - SPINWAIT((brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL) & - SSB_IMSTATE_BUSY), 100000); - } - - /* set reset and reject while enabling the clocks */ - regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | - SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata, NULL); - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), - NULL); - udelay(10); - - /* clear the initiator reject bit */ - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); - if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); - regdata &= ~SSB_IMSTATE_REJECT; - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); - } - } - - /* leave reset and reject asserted */ - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); - udelay(1); -} - -static void -brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) -{ - u8 idx; - u32 regdata; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - /* if core is already in reset, just return */ - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); - if ((regdata & BCMA_RESET_CTL_RESET) != 0) - return; - - /* ensure no pending backplane operation - * 300uc should be sufficient for backplane ops to be finish - * extra 10ms is taken into account for firmware load stage - * after 10300us carry on disabling the core anyway - */ - SPINWAIT(brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_ST, - NULL), 10300); - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_ST, - NULL); - if (regdata) - brcmf_err("disabling core 0x%x with reset status %x\n", - coreid, regdata); - - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - BCMA_RESET_CTL_RESET, NULL); - udelay(1); - - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits, NULL); - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - usleep_range(10, 20); - -} - -static void -brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) -{ - u32 regdata; - u8 idx; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - /* - * Must do the disable sequence first to work for - * arbitrary current core state. - */ - brcmf_sdio_sb_coredisable(sdiodev, ci, coreid, 0); - - /* - * Now do the initialization sequence. - * set reset while enabling the clock and - * forcing them on throughout the core - */ - brcmf_sdio_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, - NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - udelay(1); - - /* clear any serror */ - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - NULL); - if (regdata & SSB_TMSHIGH_SERR) - brcmf_sdio_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - 0, NULL); - - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - NULL); - if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) - brcmf_sdio_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), - NULL); - - /* clear reset and allow it to propagate throughout the core */ - brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - udelay(1); - - /* leave clock enabled */ - brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); - udelay(1); -} - -static void -brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits) -{ - u8 idx; - u32 regdata; - - idx = brcmf_sdio_chip_getinfidx(ci, coreid); - - /* must disable first to work for arbitrary current core state */ - brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits); - - /* now do initialization sequence */ - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - 0, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); - udelay(1); - - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - udelay(1); -} - -#ifdef DEBUG -/* safety check for chipinfo */ -static int brcmf_sdio_chip_cichk(struct chip_info *ci) -{ - u8 core_idx; - - /* check RAM core presence for ARM CM3 core */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); - if (BRCMF_MAX_CORENUM != core_idx) { - core_idx = brcmf_sdio_chip_getinfidx(ci, - BCMA_CORE_INTERNAL_MEM); - if (BRCMF_MAX_CORENUM == core_idx) { - brcmf_err("RAM core not provided with ARM CM3 core\n"); - return -ENODEV; - } - } - - /* check RAM base for ARM CR4 core */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CR4); - if (BRCMF_MAX_CORENUM != core_idx) { - if (ci->rambase == 0) { - brcmf_err("RAM base not provided with ARM CR4 core\n"); - return -ENOMEM; - } - } - - return 0; -} -#else /* DEBUG */ -static inline int brcmf_sdio_chip_cichk(struct chip_info *ci) -{ - return 0; -} -#endif - -static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 regs) -{ - u32 regdata; - int ret; - - /* Get CC core rev - * Chipid is assume to be at offset 0 from regs arg - * For different chiptypes or old sdio hosts w/o chipcommon, - * other ways of recognition should be added here. - */ - ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = regs; - regdata = brcmf_sdio_regrl(sdiodev, - CORE_CC_REG(ci->c_inf[0].base, chipid), - NULL); - ci->chip = regdata & CID_ID_MASK; - ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; - ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; - - brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); - - /* Address of cores for new chips should be added here */ - switch (ci->chip) { - case BCM43143_CHIP_ID: - ci->c_inf[0].wrapbase = ci->c_inf[0].base + 0x00100000; - ci->c_inf[0].cib = 0x2b000000; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = BCM43143_CORE_BUS_BASE; - ci->c_inf[1].wrapbase = ci->c_inf[1].base + 0x00100000; - ci->c_inf[1].cib = 0x18000000; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = BCM43143_CORE_SOCRAM_BASE; - ci->c_inf[2].wrapbase = ci->c_inf[2].base + 0x00100000; - ci->c_inf[2].cib = 0x14000000; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = BCM43143_CORE_ARM_BASE; - ci->c_inf[3].wrapbase = ci->c_inf[3].base + 0x00100000; - ci->c_inf[3].cib = 0x07000000; - ci->ramsize = BCM43143_RAMSIZE; - break; - case BCM43241_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2a084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0e004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x14080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->ramsize = 0x90000; - break; - case BCM4329_CHIP_ID: - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = BCM4329_CORE_BUS_BASE; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = BCM4329_CORE_SOCRAM_BASE; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = BCM4329_CORE_ARM_BASE; - ci->ramsize = BCM4329_RAMSIZE; - break; - case BCM4330_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x27004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x07004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x0d080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x03004211; - ci->ramsize = 0x48000; - break; - case BCM4334_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x29004211; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18002000; - ci->c_inf[1].wrapbase = 0x18102000; - ci->c_inf[1].cib = 0x0d004211; - ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM; - ci->c_inf[2].base = 0x18004000; - ci->c_inf[2].wrapbase = 0x18104000; - ci->c_inf[2].cib = 0x13080401; - ci->c_inf[3].id = BCMA_CORE_ARM_CM3; - ci->c_inf[3].base = 0x18003000; - ci->c_inf[3].wrapbase = 0x18103000; - ci->c_inf[3].cib = 0x07004211; - ci->ramsize = 0x80000; - break; - case BCM4335_CHIP_ID: - ci->c_inf[0].wrapbase = 0x18100000; - ci->c_inf[0].cib = 0x2b084411; - ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; - ci->c_inf[1].base = 0x18005000; - ci->c_inf[1].wrapbase = 0x18105000; - ci->c_inf[1].cib = 0x0f004211; - ci->c_inf[2].id = BCMA_CORE_ARM_CR4; - ci->c_inf[2].base = 0x18002000; - ci->c_inf[2].wrapbase = 0x18102000; - ci->c_inf[2].cib = 0x01084411; - ci->ramsize = 0xc0000; - ci->rambase = 0x180000; - break; - default: - brcmf_err("chipid 0x%x is not supported\n", ci->chip); - return -ENODEV; - } - - ret = brcmf_sdio_chip_cichk(ci); - if (ret) - return ret; - - switch (ci->socitype) { - case SOCI_SB: - ci->iscoreup = brcmf_sdio_sb_iscoreup; - ci->corerev = brcmf_sdio_sb_corerev; - ci->coredisable = brcmf_sdio_sb_coredisable; - ci->resetcore = brcmf_sdio_sb_resetcore; - break; - case SOCI_AI: - ci->iscoreup = brcmf_sdio_ai_iscoreup; - ci->corerev = brcmf_sdio_ai_corerev; - ci->coredisable = brcmf_sdio_ai_coredisable; - ci->resetcore = brcmf_sdio_ai_resetcore; - break; - default: - brcmf_err("socitype %u not supported\n", ci->socitype); - return -ENODEV; - } - - return 0; -} - -static int -brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) -{ - int err = 0; - u8 clkval, clkset; - - /* Try forcing SDIO core to do ALPAvail request only */ - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - if (err) { - brcmf_err("error writing for HT off\n"); - return err; - } - - /* If register supported, wait for ALPAvail and then force ALP */ - /* This may take up to 15 milliseconds */ - clkval = brcmf_sdio_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); - - if ((clkval & ~SBSDIO_AVBITS) != clkset) { - brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", - clkset, clkval); - return -EACCES; - } - - SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL)), - !SBSDIO_ALPAV(clkval)), - PMU_MAX_TRANSITION_DLY); - if (!SBSDIO_ALPAV(clkval)) { - brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", - clkval); - return -EBUSY; - } - - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; - brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - udelay(65); - - /* Also, disable the extra SDIO pull-ups */ - brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); - - return 0; -} - -static void -brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) -{ - u32 base = ci->c_inf[0].base; - - /* get chipcommon rev */ - ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); - - /* get chipcommon capabilites */ - ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev, - CORE_CC_REG(base, capabilities), - NULL); - - /* get pmu caps & rev */ - if (ci->c_inf[0].caps & CC_CAP_PMU) { - ci->pmucaps = - brcmf_sdio_regrl(sdiodev, - CORE_CC_REG(base, pmucapabilities), - NULL); - ci->pmurev = ci->pmucaps & PCAP_REV_MASK; - } - - ci->c_inf[1].rev = ci->corerev(sdiodev, ci, ci->c_inf[1].id); - - brcmf_dbg(INFO, "ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n", - ci->c_inf[0].rev, ci->pmurev, - ci->c_inf[1].rev, ci->c_inf[1].id); - - /* - * Make sure any on-chip ARM is off (in case strapping is wrong), - * or downloaded code was already running. - */ - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); -} - -int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr, u32 regs) -{ - int ret; - struct chip_info *ci; - - brcmf_dbg(TRACE, "Enter\n"); - - /* alloc chip_info_t */ - ci = kzalloc(sizeof(struct chip_info), GFP_ATOMIC); - if (!ci) - return -ENOMEM; - - ret = brcmf_sdio_chip_buscoreprep(sdiodev); - if (ret != 0) - goto err; - - ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); - if (ret != 0) - goto err; - - brcmf_sdio_chip_buscoresetup(sdiodev, ci); - - brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), - 0, NULL); - brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), - 0, NULL); - - *ci_ptr = ci; - return 0; - -err: - kfree(ci); - return ret; -} - -void -brcmf_sdio_chip_detach(struct chip_info **ci_ptr) -{ - brcmf_dbg(TRACE, "Enter\n"); - - kfree(*ci_ptr); - *ci_ptr = NULL; -} - -static char *brcmf_sdio_chip_name(uint chipid, char *buf, uint len) -{ - const char *fmt; - - fmt = ((chipid > 0xa000) || (chipid < 0x4000)) ? "%d" : "%x"; - snprintf(buf, len, fmt, chipid); - return buf; -} - -void -brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 drivestrength) -{ - const struct sdiod_drive_str *str_tab = NULL; - u32 str_mask; - u32 str_shift; - char chn[8]; - u32 base = ci->c_inf[0].base; - u32 i; - u32 drivestrength_sel = 0; - u32 cc_data_temp; - u32 addr; - - if (!(ci->c_inf[0].caps & CC_CAP_PMU)) - return; - - switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): - str_tab = sdiod_drvstr_tab1_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): - /* note: 43143 does not support tristate */ - i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; - if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { - str_tab = sdiod_drvstr_tab2_3v3; - str_mask = 0x00000007; - str_shift = 0; - } else - brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - drivestrength); - break; - default: - brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - brcmf_sdio_chip_name(ci->chip, chn, 8), - ci->chiprev, ci->pmurev); - break; - } - - if (str_tab != NULL) { - for (i = 0; str_tab[i].strength != 0; i++) { - if (drivestrength >= str_tab[i].strength) { - drivestrength_sel = str_tab[i].sel; - break; - } - } - addr = CORE_CC_REG(base, chipcontrol_addr); - brcmf_sdio_regwl(sdiodev, addr, 1, NULL); - cc_data_temp = brcmf_sdio_regrl(sdiodev, addr, NULL); - cc_data_temp &= ~str_mask; - drivestrength_sel <<= str_shift; - cc_data_temp |= drivestrength_sel; - brcmf_sdio_regwl(sdiodev, addr, cc_data_temp, NULL); - - brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", - str_tab[i].strength, drivestrength, cc_data_temp); - } -} - -#ifdef DEBUG -static bool -brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, - char *nvram_dat, uint nvram_sz) -{ - char *nvram_ularray; - int err; - bool ret = true; - - /* read back and verify */ - brcmf_dbg(INFO, "Compare NVRAM dl & ul; size=%d\n", nvram_sz); - nvram_ularray = kmalloc(nvram_sz, GFP_KERNEL); - /* do not proceed while no memory but */ - if (!nvram_ularray) - return true; - - /* Upload image to verify downloaded contents. */ - memset(nvram_ularray, 0xaa, nvram_sz); - - /* Read the vars list to temp buffer for comparison */ - err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray, - nvram_sz); - if (err) { - brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n", - err, nvram_sz, nvram_addr); - } else if (memcmp(nvram_dat, nvram_ularray, nvram_sz)) { - brcmf_err("Downloaded NVRAM image is corrupted\n"); - ret = false; - } - kfree(nvram_ularray); - - return ret; -} -#else /* DEBUG */ -static inline bool -brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, - char *nvram_dat, uint nvram_sz) -{ - return true; -} -#endif /* DEBUG */ - -static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, - char *nvram_dat, uint nvram_sz) -{ - int err; - u32 nvram_addr; - u32 token; - __le32 token_le; - - nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase; - - /* Write the vars list */ - err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz); - if (err) { - brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", - err, nvram_sz, nvram_addr); - return false; - } - - if (!brcmf_sdio_chip_verifynvram(sdiodev, nvram_addr, - nvram_dat, nvram_sz)) - return false; - - /* generate token: - * nvram size, converted to words, in lower 16-bits, checksum - * in upper 16-bits. - */ - token = nvram_sz / 4; - token = (~token << 16) | (token & 0x0000FFFF); - token_le = cpu_to_le32(token); - - brcmf_dbg(INFO, "RAM size: %d\n", ci->ramsize); - brcmf_dbg(INFO, "nvram is placed at %d, size %d, token=0x%08x\n", - nvram_addr, nvram_sz, token); - - /* Write the length token to the last word */ - if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase), - (u8 *)&token_le, 4)) - return false; - - return true; -} - -static void -brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) -{ - u32 zeros = 0; - - ci->coredisable(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); - ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0); - - /* clear length token */ - brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4); -} - -static bool -brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - char *nvram_dat, uint nvram_sz) -{ - u8 core_idx; - u32 reg_addr; - - if (!ci->iscoreup(sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) { - brcmf_err("SOCRAM core is down after reset?\n"); - return false; - } - - if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz)) - return false; - - /* clear all interrupts */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); - reg_addr = ci->c_inf[core_idx].base; - reg_addr += offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); - - return true; -} - -static inline void -brcmf_sdio_chip_cr4_enterdl(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) -{ - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, - ARMCR4_BCMA_IOCTL_CPUHALT); -} - -static bool -brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - char *nvram_dat, uint nvram_sz) -{ - u8 core_idx; - u32 reg_addr; - - if (!brcmf_sdio_chip_writenvram(sdiodev, ci, nvram_dat, nvram_sz)) - return false; - - /* clear all interrupts */ - core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); - reg_addr = ci->c_inf[core_idx].base; - reg_addr += offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - /* Write reset vector to address 0 */ - brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec, - sizeof(ci->rst_vec)); - - /* restore ARM */ - ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0); - - return true; -} - -void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci) -{ - u8 arm_core_idx; - - arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); - if (BRCMF_MAX_CORENUM != arm_core_idx) { - brcmf_sdio_chip_cm3_enterdl(sdiodev, ci); - return; - } - - brcmf_sdio_chip_cr4_enterdl(sdiodev, ci); -} - -bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, char *nvram_dat, - uint nvram_sz) -{ - u8 arm_core_idx; - - arm_core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_ARM_CM3); - if (BRCMF_MAX_CORENUM != arm_core_idx) - return brcmf_sdio_chip_cm3_exitdl(sdiodev, ci, nvram_dat, - nvram_sz); - - return brcmf_sdio_chip_cr4_exitdl(sdiodev, ci, nvram_dat, nvram_sz); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h deleted file mode 100644 index 83c041f1bf4..00000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright (c) 2011 Broadcom Corporation - * - * 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 _BRCMFMAC_SDIO_CHIP_H_ -#define _BRCMFMAC_SDIO_CHIP_H_ - -/* - * Core reg address translation. - * Both macro's returns a 32 bits byte address on the backplane bus. - */ -#define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) -#define CORE_BUS_REG(base, field) \ - (base + offsetof(struct sdpcmd_regs, field)) -#define CORE_SB(base, field) \ - (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) - -/* SDIO function 1 register CHIPCLKCSR */ -/* Force ALP request to backplane */ -#define SBSDIO_FORCE_ALP 0x01 -/* Force HT request to backplane */ -#define SBSDIO_FORCE_HT 0x02 -/* Force ILP request to backplane */ -#define SBSDIO_FORCE_ILP 0x04 -/* Make ALP ready (power up xtal) */ -#define SBSDIO_ALP_AVAIL_REQ 0x08 -/* Make HT ready (power up PLL) */ -#define SBSDIO_HT_AVAIL_REQ 0x10 -/* Squelch clock requests from HW */ -#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 -/* Status: ALP is ready */ -#define SBSDIO_ALP_AVAIL 0x40 -/* Status: HT is ready */ -#define SBSDIO_HT_AVAIL 0x80 -#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) -#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) -#define SBSDIO_CLKAV(regval, alponly) \ - (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) - -#define BRCMF_MAX_CORENUM 6 - -struct chip_core_info { - u16 id; - u16 rev; - u32 base; - u32 wrapbase; - u32 caps; - u32 cib; -}; - -struct chip_info { - u32 chip; - u32 chiprev; - u32 socitype; - /* core info */ - /* always put chipcommon core at 0, bus core at 1 */ - struct chip_core_info c_inf[BRCMF_MAX_CORENUM]; - u32 pmurev; - u32 pmucaps; - u32 ramsize; - u32 rambase; - u32 rst_vec; /* reset vertor for ARM CR4 core */ - - bool (*iscoreup)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - u16 coreid); - u32 (*corerev)(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, - u16 coreid); - void (*coredisable)(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits); - void (*resetcore)(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u16 coreid, u32 core_bits); -}; - -struct sbconfig { - u32 PAD[2]; - u32 sbipsflag; /* initiator port ocp slave flag */ - u32 PAD[3]; - u32 sbtpsflag; /* target port ocp slave flag */ - u32 PAD[11]; - u32 sbtmerrloga; /* (sonics >= 2.3) */ - u32 PAD; - u32 sbtmerrlog; /* (sonics >= 2.3) */ - u32 PAD[3]; - u32 sbadmatch3; /* address match3 */ - u32 PAD; - u32 sbadmatch2; /* address match2 */ - u32 PAD; - u32 sbadmatch1; /* address match1 */ - u32 PAD[7]; - u32 sbimstate; /* initiator agent state */ - u32 sbintvec; /* interrupt mask */ - u32 sbtmstatelow; /* target state */ - u32 sbtmstatehigh; /* target state */ - u32 sbbwa0; /* bandwidth allocation table0 */ - u32 PAD; - u32 sbimconfiglow; /* initiator configuration */ - u32 sbimconfighigh; /* initiator configuration */ - u32 sbadmatch0; /* address match0 */ - u32 PAD; - u32 sbtmconfiglow; /* target configuration */ - u32 sbtmconfighigh; /* target configuration */ - u32 sbbconfig; /* broadcast configuration */ - u32 PAD; - u32 sbbstate; /* broadcast state */ - u32 PAD[3]; - u32 sbactcnfg; /* activate configuration */ - u32 PAD[3]; - u32 sbflagst; /* current sbflags */ - u32 PAD[3]; - u32 sbidlow; /* identification */ - u32 sbidhigh; /* identification */ -}; - -/* sdio core registers */ -struct sdpcmd_regs { - u32 corecontrol; /* 0x00, rev8 */ - u32 corestatus; /* rev8 */ - u32 PAD[1]; - u32 biststatus; /* rev8 */ - - /* PCMCIA access */ - u16 pcmciamesportaladdr; /* 0x010, rev8 */ - u16 PAD[1]; - u16 pcmciamesportalmask; /* rev8 */ - u16 PAD[1]; - u16 pcmciawrframebc; /* rev8 */ - u16 PAD[1]; - u16 pcmciaunderflowtimer; /* rev8 */ - u16 PAD[1]; - - /* interrupt */ - u32 intstatus; /* 0x020, rev8 */ - u32 hostintmask; /* rev8 */ - u32 intmask; /* rev8 */ - u32 sbintstatus; /* rev8 */ - u32 sbintmask; /* rev8 */ - u32 funcintmask; /* rev4 */ - u32 PAD[2]; - u32 tosbmailbox; /* 0x040, rev8 */ - u32 tohostmailbox; /* rev8 */ - u32 tosbmailboxdata; /* rev8 */ - u32 tohostmailboxdata; /* rev8 */ - - /* synchronized access to registers in SDIO clock domain */ - u32 sdioaccess; /* 0x050, rev8 */ - u32 PAD[3]; - - /* PCMCIA frame control */ - u8 pcmciaframectrl; /* 0x060, rev8 */ - u8 PAD[3]; - u8 pcmciawatermark; /* rev8 */ - u8 PAD[155]; - - /* interrupt batching control */ - u32 intrcvlazy; /* 0x100, rev8 */ - u32 PAD[3]; - - /* counters */ - u32 cmd52rd; /* 0x110, rev8 */ - u32 cmd52wr; /* rev8 */ - u32 cmd53rd; /* rev8 */ - u32 cmd53wr; /* rev8 */ - u32 abort; /* rev8 */ - u32 datacrcerror; /* rev8 */ - u32 rdoutofsync; /* rev8 */ - u32 wroutofsync; /* rev8 */ - u32 writebusy; /* rev8 */ - u32 readwait; /* rev8 */ - u32 readterm; /* rev8 */ - u32 writeterm; /* rev8 */ - u32 PAD[40]; - u32 clockctlstatus; /* rev8 */ - u32 PAD[7]; - - u32 PAD[128]; /* DMA engines */ - - /* SDIO/PCMCIA CIS region */ - char cis[512]; /* 0x400-0x5ff, rev6 */ - - /* PCMCIA function control registers */ - char pcmciafcr[256]; /* 0x600-6ff, rev6 */ - u16 PAD[55]; - - /* PCMCIA backplane access */ - u16 backplanecsr; /* 0x76E, rev6 */ - u16 backplaneaddr0; /* rev6 */ - u16 backplaneaddr1; /* rev6 */ - u16 backplaneaddr2; /* rev6 */ - u16 backplaneaddr3; /* rev6 */ - u16 backplanedata0; /* rev6 */ - u16 backplanedata1; /* rev6 */ - u16 backplanedata2; /* rev6 */ - u16 backplanedata3; /* rev6 */ - u16 PAD[31]; - - /* sprom "size" & "blank" info */ - u16 spromstatus; /* 0x7BE, rev2 */ - u32 PAD[464]; - - u16 PAD[0x80]; -}; - -extern int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr, u32 regs); -extern void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); -extern void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, - u32 drivestrength); -extern u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); -extern void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci); -extern bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, char *nvram_dat, - uint nvram_sz); - -#endif /* _BRCMFMAC_SDIO_CHIP_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 2b5407f002e..3deab7959a0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -164,11 +164,9 @@ struct brcmf_sdio; struct brcmf_sdio_dev { struct sdio_func *func[SDIO_MAX_FUNCS]; u8 num_funcs; /* Supported funcs on client */ - u32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; u32 sbwad; /* Save backplane window address */ - void *bus; + struct brcmf_sdio *bus; atomic_t suspend; /* suspend flag */ - wait_queue_head_t request_byte_wait; wait_queue_head_t request_word_wait; wait_queue_head_t request_buffer_wait; struct device *dev; @@ -178,25 +176,117 @@ struct brcmf_sdio_dev { bool irq_en; /* irq enable flags */ spinlock_t irq_en_lock; bool irq_wake; /* irq wake enable flags */ + bool sg_support; + uint max_request_size; + ushort max_segment_count; + uint max_segment_size; + uint txglomsz; + struct sg_table sgtable; +}; + +/* sdio core registers */ +struct sdpcmd_regs { + u32 corecontrol; /* 0x00, rev8 */ + u32 corestatus; /* rev8 */ + u32 PAD[1]; + u32 biststatus; /* rev8 */ + + /* PCMCIA access */ + u16 pcmciamesportaladdr; /* 0x010, rev8 */ + u16 PAD[1]; + u16 pcmciamesportalmask; /* rev8 */ + u16 PAD[1]; + u16 pcmciawrframebc; /* rev8 */ + u16 PAD[1]; + u16 pcmciaunderflowtimer; /* rev8 */ + u16 PAD[1]; + + /* interrupt */ + u32 intstatus; /* 0x020, rev8 */ + u32 hostintmask; /* rev8 */ + u32 intmask; /* rev8 */ + u32 sbintstatus; /* rev8 */ + u32 sbintmask; /* rev8 */ + u32 funcintmask; /* rev4 */ + u32 PAD[2]; + u32 tosbmailbox; /* 0x040, rev8 */ + u32 tohostmailbox; /* rev8 */ + u32 tosbmailboxdata; /* rev8 */ + u32 tohostmailboxdata; /* rev8 */ + + /* synchronized access to registers in SDIO clock domain */ + u32 sdioaccess; /* 0x050, rev8 */ + u32 PAD[3]; + + /* PCMCIA frame control */ + u8 pcmciaframectrl; /* 0x060, rev8 */ + u8 PAD[3]; + u8 pcmciawatermark; /* rev8 */ + u8 PAD[155]; + + /* interrupt batching control */ + u32 intrcvlazy; /* 0x100, rev8 */ + u32 PAD[3]; + + /* counters */ + u32 cmd52rd; /* 0x110, rev8 */ + u32 cmd52wr; /* rev8 */ + u32 cmd53rd; /* rev8 */ + u32 cmd53wr; /* rev8 */ + u32 abort; /* rev8 */ + u32 datacrcerror; /* rev8 */ + u32 rdoutofsync; /* rev8 */ + u32 wroutofsync; /* rev8 */ + u32 writebusy; /* rev8 */ + u32 readwait; /* rev8 */ + u32 readterm; /* rev8 */ + u32 writeterm; /* rev8 */ + u32 PAD[40]; + u32 clockctlstatus; /* rev8 */ + u32 PAD[7]; + + u32 PAD[128]; /* DMA engines */ + + /* SDIO/PCMCIA CIS region */ + char cis[512]; /* 0x400-0x5ff, rev6 */ + + /* PCMCIA function control registers */ + char pcmciafcr[256]; /* 0x600-6ff, rev6 */ + u16 PAD[55]; + + /* PCMCIA backplane access */ + u16 backplanecsr; /* 0x76E, rev6 */ + u16 backplaneaddr0; /* rev6 */ + u16 backplaneaddr1; /* rev6 */ + u16 backplaneaddr2; /* rev6 */ + u16 backplaneaddr3; /* rev6 */ + u16 backplanedata0; /* rev6 */ + u16 backplanedata1; /* rev6 */ + u16 backplanedata2; /* rev6 */ + u16 backplanedata3; /* rev6 */ + u16 PAD[31]; + + /* sprom "size" & "blank" info */ + u16 spromstatus; /* 0x7BE, rev2 */ + u32 PAD[464]; + + u16 PAD[0x80]; }; /* Register/deregister interrupt handler. */ -extern int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev); -extern int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); /* sdio device register access interface */ -extern u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -extern u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -extern void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, - u8 data, int *ret); -extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, - u32 data, int *ret); -extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, - void *data, bool write); +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, + int *ret); +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, + int *ret); /* Buffer transfer to/from device (client) core via cmd53. * fn: function number - * addr: backplane address (i.e. >= regsva from attach) * flags: backplane width, address increment, sync/async * buf: pointer to memory data buffer * nbytes: number of bytes to transfer to/from buf @@ -206,22 +296,14 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, * Returns 0 or error code. * NOTE: Async operation is not currently supported. */ -extern int -brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq); -extern int -brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes); - -extern int -brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt); -extern int -brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes); -extern int -brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq); +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq); +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); + +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt); +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen); /* Flags bits */ @@ -237,46 +319,16 @@ brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, * nbytes: number of bytes to transfer to/from buf * Returns 0 or error code. */ -extern int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, - u32 addr, u8 *buf, uint nbytes); -extern int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, - u32 address, u8 *data, uint size); +int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size); /* Issue an abort to the specified function */ -extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn); - -/* platform specific/high level functions */ -extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); -extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev); - -/* attach, return handler on success, NULL if failed. - * The handler shall be provided by all subsequent calls. No local cache - * cfghdl points to the starting address of pci device mapped memory - */ -extern int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev); -extern void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev); - -/* read or write one byte using cmd52 */ -extern int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, - uint fnc, uint addr, u8 *byte); - -/* read or write 2/4 bytes using cmd53 */ -extern int -brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, - uint rw, uint fnc, uint addr, - u32 *word, uint nbyte); - -/* Watchdog timer interface for pm ops */ -extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, - bool enable); +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); -extern void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev); -extern void brcmf_sdbrcm_disconnect(void *ptr); -extern void brcmf_sdbrcm_isr(void *arg); +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdio_remove(struct brcmf_sdio *bus); +void brcmf_sdio_isr(struct brcmf_sdio *bus); -extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); -extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, - wait_queue_head_t *wq); -extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev); #endif /* _BRCM_SDH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h index bc291711289..4d7d51f9571 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h @@ -78,16 +78,18 @@ TRACE_EVENT(brcmf_hexdump, TP_ARGS(data, len), TP_STRUCT__entry( __field(unsigned long, len) + __field(unsigned long, addr) __dynamic_array(u8, hdata, len) ), TP_fast_assign( __entry->len = len; + __entry->addr = (unsigned long)data; memcpy(__get_dynamic_array(hdata), data, len); ), - TP_printk("hexdump [length=%lu]", __entry->len) + TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len) ); -TRACE_EVENT(brcmf_bdchdr, +TRACE_EVENT(brcmf_bcdchdr, TP_PROTO(void *data), TP_ARGS(data), TP_STRUCT__entry( @@ -105,7 +107,35 @@ TRACE_EVENT(brcmf_bdchdr, memcpy(__get_dynamic_array(signal), (u8 *)data + 4, __entry->siglen); ), - TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) + TP_printk("bcdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) +); + +#ifndef SDPCM_RX +#define SDPCM_RX 0 +#endif +#ifndef SDPCM_TX +#define SDPCM_TX 1 +#endif +#ifndef SDPCM_GLOM +#define SDPCM_GLOM 2 +#endif + +TRACE_EVENT(brcmf_sdpcm_hdr, + TP_PROTO(u8 dir, void *data), + TP_ARGS(dir, data), + TP_STRUCT__entry( + __field(u8, dir) + __field(u16, len) + __dynamic_array(u8, hdr, dir == SDPCM_GLOM ? 20 : 12) + ), + TP_fast_assign( + memcpy(__get_dynamic_array(hdr), data, dir == SDPCM_GLOM ? 20 : 12); + __entry->len = *(u8 *)data | (*((u8 *)data + 1) << 8); + __entry->dir = dir; + ), + TP_printk("sdpcm: %s len %u, seq %d", + __entry->dir == SDPCM_RX ? "RX" : "TX", + __entry->len, ((u8 *)__get_dynamic_array(hdr))[4]) ); #ifdef CONFIG_BRCM_TRACING diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index f4aea47e073..d06fcb05adf 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -25,6 +25,7 @@ #include <dhd_bus.h> #include <dhd_dbg.h> +#include "firmware.h" #include "usb_rdl.h" #include "usb.h" @@ -61,12 +62,6 @@ struct brcmf_usb_image { u8 *image; int image_len; }; -static struct list_head fw_image_list; - -struct intr_transfer_buf { - u32 notification; - u32 reserved; -}; struct brcmf_usbdev_info { struct brcmf_usbdev bus_pub; /* MUST BE FIRST */ @@ -75,7 +70,7 @@ struct brcmf_usbdev_info { struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; - uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; + uint rx_pipe, tx_pipe, rx_pipe2; int rx_low_watermark; int tx_low_watermark; @@ -87,7 +82,7 @@ struct brcmf_usbdev_info { struct brcmf_usbreq *tx_reqs; struct brcmf_usbreq *rx_reqs; - u8 *image; /* buffer for combine fw and nvram */ + const u8 *image; /* buffer for combine fw and nvram */ int image_len; struct usb_device *usbdev; @@ -104,10 +99,6 @@ struct brcmf_usbdev_info { ulong ctl_op; struct urb *bulk_urb; /* used for FW download */ - struct urb *intr_urb; /* URB for interrupt endpoint */ - int intr_size; /* Size of interrupt message */ - int interval; /* Interrupt polling interval */ - struct intr_transfer_buf intr; /* Data buffer for interrupt endpoint */ }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, @@ -435,7 +426,6 @@ static void brcmf_usb_rx_complete(struct urb *urb) struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; struct sk_buff *skb; - struct sk_buff_head skbq; brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); brcmf_usb_del_fromq(devinfo, req); @@ -450,10 +440,8 @@ static void brcmf_usb_rx_complete(struct urb *urb) } if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { - skb_queue_head_init(&skbq); - skb_queue_tail(&skbq, skb); skb_put(skb, urb->actual_length); - brcmf_rx_frames(devinfo->dev, &skbq); + brcmf_rx_frame(devinfo->dev, skb); brcmf_usb_rx_refill(devinfo, req); } else { brcmu_pkt_buf_free_skb(skb); @@ -525,48 +513,15 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) /* update state of upper layer */ if (state == BRCMFMAC_USB_STATE_DOWN) { brcmf_dbg(USB, "DBUS is down\n"); - bcmf_bus->state = BRCMF_BUS_DOWN; + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN); } else if (state == BRCMFMAC_USB_STATE_UP) { brcmf_dbg(USB, "DBUS is up\n"); - bcmf_bus->state = BRCMF_BUS_DATA; + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DATA); } else { brcmf_dbg(USB, "DBUS current state=%d\n", state); } } -static void -brcmf_usb_intr_complete(struct urb *urb) -{ - struct brcmf_usbdev_info *devinfo = - (struct brcmf_usbdev_info *)urb->context; - int err; - - brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); - - if (devinfo == NULL) - return; - - if (unlikely(urb->status)) { - if (urb->status == -ENOENT || - urb->status == -ESHUTDOWN || - urb->status == -ENODEV) { - brcmf_usb_state_change(devinfo, - BRCMFMAC_USB_STATE_DOWN); - } - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) { - brcmf_err("intr cb when DBUS down, ignoring\n"); - return; - } - - if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { - err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); - if (err) - brcmf_err("usb_submit_urb, err=%d\n", err); - } -} - static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); @@ -622,7 +577,6 @@ static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); u16 ifnum; - int ret; brcmf_dbg(USB, "Enter\n"); if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) @@ -631,23 +585,6 @@ static int brcmf_usb_up(struct device *dev) /* Success, indicate devinfo is fully up */ brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP); - if (devinfo->intr_urb) { - usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev, - devinfo->intr_pipe, - &devinfo->intr, - devinfo->intr_size, - (usb_complete_t)brcmf_usb_intr_complete, - devinfo, - devinfo->interval); - - ret = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); - if (ret) { - brcmf_err("USB_SUBMIT_URB failed with status %d\n", - ret); - return -EINVAL; - } - } - if (devinfo->ctl_urb) { devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0); devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0); @@ -684,8 +621,6 @@ static void brcmf_usb_down(struct device *dev) return; brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); - if (devinfo->intr_urb) - usb_kill_urb(devinfo->intr_urb); if (devinfo->ctl_urb) usb_kill_urb(devinfo->ctl_urb); @@ -1024,7 +959,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) } err = brcmf_usb_dlstart(devinfo, - devinfo->image, devinfo->image_len); + (u8 *)devinfo->image, devinfo->image_len); if (err == 0) err = brcmf_usb_dlrun(devinfo); return err; @@ -1039,7 +974,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) brcmf_usb_free_q(&devinfo->rx_freeq, false); brcmf_usb_free_q(&devinfo->tx_freeq, false); - usb_free_urb(devinfo->intr_urb); usb_free_urb(devinfo->ctl_urb); usb_free_urb(devinfo->bulk_urb); @@ -1083,68 +1017,20 @@ static int check_file(const u8 *headers) return -1; } -static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) +static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) { - s8 *fwname; - const struct firmware *fw; - struct brcmf_usb_image *fw_image; - int err; - - brcmf_dbg(USB, "Enter\n"); switch (devinfo->bus_pub.devid) { case 43143: - fwname = BRCMF_USB_43143_FW_NAME; - break; + return BRCMF_USB_43143_FW_NAME; case 43235: case 43236: case 43238: - fwname = BRCMF_USB_43236_FW_NAME; - break; + return BRCMF_USB_43236_FW_NAME; case 43242: - fwname = BRCMF_USB_43242_FW_NAME; - break; + return BRCMF_USB_43242_FW_NAME; default: - return -EINVAL; - break; - } - brcmf_dbg(USB, "Loading FW %s\n", fwname); - list_for_each_entry(fw_image, &fw_image_list, list) { - if (fw_image->fwname == fwname) { - devinfo->image = fw_image->image; - devinfo->image_len = fw_image->image_len; - return 0; - } - } - /* fw image not yet loaded. Load it now and add to list */ - err = request_firmware(&fw, fwname, devinfo->dev); - if (!fw) { - brcmf_err("fail to request firmware %s\n", fwname); - return err; - } - if (check_file(fw->data) < 0) { - brcmf_err("invalid firmware %s\n", fwname); - return -EINVAL; + return NULL; } - - fw_image = kzalloc(sizeof(*fw_image), GFP_ATOMIC); - if (!fw_image) - return -ENOMEM; - INIT_LIST_HEAD(&fw_image->list); - list_add_tail(&fw_image->list, &fw_image_list); - fw_image->fwname = fwname; - fw_image->image = vmalloc(fw->size); - if (!fw_image->image) - return -ENOMEM; - - memcpy(fw_image->image, fw->data, fw->size); - fw_image->image_len = fw->size; - - release_firmware(fw); - - devinfo->image = fw_image->image; - devinfo->image_len = fw_image->image_len; - - return 0; } @@ -1189,11 +1075,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, goto error; devinfo->tx_freecount = ntxq; - devinfo->intr_urb = usb_alloc_urb(0, GFP_ATOMIC); - if (!devinfo->intr_urb) { - brcmf_err("usb_alloc_urb (intr) failed\n"); - goto error; - } devinfo->ctl_urb = usb_alloc_urb(0, GFP_ATOMIC); if (!devinfo->ctl_urb) { brcmf_err("usb_alloc_urb (ctl) failed\n"); @@ -1205,16 +1086,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, goto error; } - if (!brcmf_usb_dlneeded(devinfo)) - return &devinfo->bus_pub; - - brcmf_dbg(USB, "Start fw downloading\n"); - if (brcmf_usb_get_fw(devinfo)) - goto error; - - if (brcmf_usb_fw_download(devinfo)) - goto error; - return &devinfo->bus_pub; error: @@ -1225,18 +1096,77 @@ error: static struct brcmf_bus_ops brcmf_usb_bus_ops = { .txdata = brcmf_usb_tx, - .init = brcmf_usb_up, .stop = brcmf_usb_down, .txctl = brcmf_usb_tx_ctlpkt, .rxctl = brcmf_usb_rx_ctlpkt, }; +static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) +{ + int ret; + + /* Attach to the common driver interface */ + ret = brcmf_attach(devinfo->dev); + if (ret) { + brcmf_err("brcmf_attach failed\n"); + return ret; + } + + ret = brcmf_usb_up(devinfo->dev); + if (ret) + goto fail; + + ret = brcmf_bus_start(devinfo->dev); + if (ret) + goto fail; + + return 0; +fail: + brcmf_detach(devinfo->dev); + return ret; +} + +static void brcmf_usb_probe_phase2(struct device *dev, + const struct firmware *fw, + void *nvram, u32 nvlen) +{ + struct brcmf_bus *bus = dev_get_drvdata(dev); + struct brcmf_usbdev_info *devinfo; + int ret; + + brcmf_dbg(USB, "Start fw downloading\n"); + ret = check_file(fw->data); + if (ret < 0) { + brcmf_err("invalid firmware\n"); + release_firmware(fw); + goto error; + } + + devinfo = bus->bus_priv.usb->devinfo; + devinfo->image = fw->data; + devinfo->image_len = fw->size; + + ret = brcmf_usb_fw_download(devinfo); + release_firmware(fw); + if (ret) + goto error; + + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto error; + + return; +error: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); + device_release_driver(dev); +} + static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) { struct brcmf_bus *bus = NULL; struct brcmf_usbdev *bus_pub = NULL; - int ret; struct device *dev = devinfo->dev; + int ret; brcmf_dbg(USB, "Enter\n"); bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); @@ -1254,24 +1184,22 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->bus_priv.usb = bus_pub; dev_set_drvdata(dev, bus); bus->ops = &brcmf_usb_bus_ops; - bus->chip = bus_pub->devid; - bus->chiprev = bus_pub->chiprev; - - /* Attach to the common driver interface */ - ret = brcmf_attach(0, dev); - if (ret) { - brcmf_err("brcmf_attach failed\n"); - goto fail; - } + bus->proto_type = BRCMF_PROTO_BCDC; + bus->always_use_fws_queue = true; - ret = brcmf_bus_start(dev); - if (ret) { - brcmf_err("dongle is not responding\n"); - brcmf_detach(dev); - goto fail; + if (!brcmf_usb_dlneeded(devinfo)) { + ret = brcmf_usb_bus_setup(devinfo); + if (ret) + goto fail; } + bus->chip = bus_pub->devid; + bus->chiprev = bus_pub->chiprev; + /* request firmware here */ + brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); return 0; + fail: /* Release resources in reverse order */ kfree(bus); @@ -1359,9 +1287,6 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto fail; } - endpoint_num = endpoint->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; - devinfo->intr_pipe = usb_rcvintpipe(usb, endpoint_num); - devinfo->rx_pipe = 0; devinfo->rx_pipe2 = 0; devinfo->tx_pipe = 0; @@ -1393,16 +1318,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) } } - /* Allocate interrupt URB and data buffer */ - /* RNDIS says 8-byte intr, our old drivers used 4-byte */ - if (IFEPDESC(usb, CONTROL_IF, 0).wMaxPacketSize == cpu_to_le16(16)) - devinfo->intr_size = 8; - else - devinfo->intr_size = 4; - - devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; - - if (usb->speed == USB_SPEED_HIGH) + if (usb->speed == USB_SPEED_SUPER) + brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n"); + else if (usb->speed == USB_SPEED_HIGH) brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); else brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); @@ -1457,23 +1375,18 @@ static int brcmf_usb_resume(struct usb_interface *intf) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(USB, "Enter\n"); - if (!brcmf_attach(0, devinfo->dev)) - return brcmf_bus_start(&usb->dev); - - return 0; + return brcmf_usb_bus_setup(devinfo); } static int brcmf_usb_reset_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - brcmf_dbg(USB, "Enter\n"); - if (!brcmf_usb_fw_download(devinfo)) - return brcmf_usb_resume(intf); - - return -EIO; + return brcmf_fw_get_firmwares(&usb->dev, 0, + brcmf_usb_get_fwname(devinfo), NULL, + brcmf_usb_probe_phase2); } #define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c @@ -1508,16 +1421,6 @@ static struct usb_driver brcmf_usbdrvr = { .disable_hub_initiated_lpm = 1, }; -static void brcmf_release_fw(struct list_head *q) -{ - struct brcmf_usb_image *fw_image, *next; - - list_for_each_entry_safe(fw_image, next, q, list) { - vfree(fw_image->image); - list_del_init(&fw_image->list); - } -} - static int brcmf_usb_reset_device(struct device *dev, void *notused) { /* device past is the usb interface so we @@ -1536,12 +1439,10 @@ void brcmf_usb_exit(void) ret = driver_for_each_device(drv, NULL, NULL, brcmf_usb_reset_device); usb_deregister(&brcmf_usbdrvr); - brcmf_release_fw(&fw_image_list); } void brcmf_usb_register(void) { brcmf_dbg(USB, "Enter\n"); - INIT_LIST_HEAD(&fw_image_list); usb_register(&brcmf_usbdrvr); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 571f013cebb..d8fa276e368 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -18,6 +18,7 @@ #include <linux/kernel.h> #include <linux/etherdevice.h> +#include <linux/module.h> #include <net/cfg80211.h> #include <net/netlink.h> @@ -190,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 = { @@ -202,9 +204,9 @@ static struct ieee80211_supported_band __wl_band_5ghz_a = { /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags - * NL80211_RRF_PASSIVE_SCAN and NL80211_RRF_NO_IBSS for 5GHz channels (for - * 36..48 and 149..165). With respect to these flags, wpa_supplicant doesn't - * start p2p operations on 5GHz channels. All the changes in world regulatory + * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). + * With respect to these flags, wpa_supplicant doesn't * start p2p + * operations on 5GHz channels. All the changes in world regulatory * domain are to be done here. */ static const struct ieee80211_regdomain brcmf_regdom = { @@ -219,9 +221,9 @@ static const struct ieee80211_regdomain brcmf_regdom = { */ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), /* IEEE 802.11a, channel 36..64 */ - REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } + REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } }; static const u32 __wl_cipher_suites[] = { @@ -251,6 +253,10 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +static int brcmf_roamoff; +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); +MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); + /* Quarter dBm units to mW * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 * Table is offset so the last entry is largest mW value that fits in @@ -335,6 +341,60 @@ static u8 brcmf_mw_to_qdbm(u16 mw) return qdbm; } +static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, + struct cfg80211_chan_def *ch) +{ + struct brcmu_chan ch_inf; + s32 primary_offset; + + brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", + ch->chan->center_freq, ch->center_freq1, ch->width); + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); + primary_offset = ch->center_freq1 - ch->chan->center_freq; + switch (ch->width) { + case NL80211_CHAN_WIDTH_20: + ch_inf.bw = BRCMU_CHAN_BW_20; + WARN_ON(primary_offset != 0); + break; + case NL80211_CHAN_WIDTH_40: + ch_inf.bw = BRCMU_CHAN_BW_40; + if (primary_offset < 0) + ch_inf.sb = BRCMU_CHAN_SB_U; + else + ch_inf.sb = BRCMU_CHAN_SB_L; + break; + case NL80211_CHAN_WIDTH_80: + ch_inf.bw = BRCMU_CHAN_BW_80; + if (primary_offset < 0) { + if (primary_offset < -CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_UU; + else + ch_inf.sb = BRCMU_CHAN_SB_UL; + } else { + if (primary_offset > CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_LL; + else + ch_inf.sb = BRCMU_CHAN_SB_LU; + } + break; + default: + WARN_ON_ONCE(1); + } + switch (ch->chan->band) { + case IEEE80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case IEEE80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; + default: + WARN_ON_ONCE(1); + } + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch) { @@ -351,13 +411,11 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, * triples, returning a pointer to the substring whose first element * matches tag */ -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key) { - struct brcmf_tlv *elt; - int totlen; - - elt = (struct brcmf_tlv *)buf; - totlen = buflen; + const struct brcmf_tlv *elt = buf; + int totlen = buflen; /* find tagged parameter */ while (totlen >= TLV_HDR_LEN) { @@ -378,8 +436,8 @@ struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key) * not update the tlvs buffer pointer/length. */ static bool -brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, - u8 *oui, u32 oui_len, u8 type) +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) { /* If the contents match the OUI and the type */ if (ie[TLV_LEN_OFF] >= oui_len + 1 && @@ -401,12 +459,12 @@ brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len, } static struct brcmf_vs_tlv * -brcmf_find_wpaie(u8 *parse, u32 len) +brcmf_find_wpaie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, + if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) return (struct brcmf_vs_tlv *)ie; } @@ -414,9 +472,9 @@ brcmf_find_wpaie(u8 *parse, u32 len) } static struct brcmf_vs_tlv * -brcmf_find_wpsie(u8 *parse, u32 len) +brcmf_find_wpsie(const u8 *parse, u32 len) { - struct brcmf_tlv *ie; + const struct brcmf_tlv *ie; while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, @@ -491,6 +549,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, @@ -569,6 +640,9 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, if (err) brcmf_err("Scan abort failed\n"); } + + brcmf_set_mpc(ifp, 1); + /* * e-scan can be initiated by scheduled scan * which takes precedence. @@ -578,12 +652,10 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, cfg->sched_escan = false; if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); - brcmf_set_mpc(ifp, 1); } else if (scan_request) { brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, aborted); - brcmf_set_mpc(ifp, 1); } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); @@ -651,7 +723,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: @@ -667,12 +738,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: @@ -696,7 +765,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; @@ -1095,10 +1164,10 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) BRCMF_C_DISASSOC, NULL, 0); if (err) { brcmf_err("WLC_DISASSOC failed (%d)\n", err); - cfg80211_disconnected(vif->wdev.netdev, 0, - NULL, 0, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); + cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL); + } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); @@ -1222,8 +1291,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, params->chandef.chan->center_freq); if (params->channel_fixed) { /* adding chanspec */ - chanspec = channel_to_chanspec(&cfg->d11inf, - params->chandef.chan); + chanspec = chandef_to_chanspec(&cfg->d11inf, + ¶ms->chandef); join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); join_params.params_le.chanspec_num = cpu_to_le32(1); @@ -1340,13 +1409,14 @@ static s32 brcmf_set_auth_type(struct net_device *ndev, } static s32 -brcmf_set_set_cipher(struct net_device *ndev, - struct cfg80211_connect_params *sme) +brcmf_set_wsec_mode(struct net_device *ndev, + struct cfg80211_connect_params *sme, bool mfp) { struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); struct brcmf_cfg80211_security *sec; s32 pval = 0; s32 gval = 0; + s32 wsec; s32 err = 0; if (sme->crypto.n_ciphers_pairwise) { @@ -1398,7 +1468,12 @@ brcmf_set_set_cipher(struct net_device *ndev, if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && sme->privacy) pval = AES_ENABLED; - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", pval | gval); + + if (mfp) + wsec = pval | gval | MFP_CAPABLE; + else + wsec = pval | gval; + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); if (err) { brcmf_err("error (%d)\n", err); return err; @@ -1562,13 +1637,12 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; - void *ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; u32 ie_len; struct brcmf_ext_join_params_le *ext_join_params; u16 chanspec; - s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -1591,7 +1665,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ie_len = wpa_ie->len + TLV_HDR_LEN; } else { /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len, + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, + sme->ie_len, WLAN_EID_RSN); if (rsn_ie) { ie = rsn_ie; @@ -1636,7 +1711,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = brcmf_set_set_cipher(ndev, sme); + err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); if (err) { brcmf_err("wl_set_set_cipher failed (%d)\n", err); goto done; @@ -1678,22 +1753,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) @@ -1706,6 +1768,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, @@ -1758,6 +1839,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, return -EIO; clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); scbval.val = cpu_to_le32(reason_code); @@ -1912,7 +1994,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)); @@ -1980,7 +2062,9 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (mac_addr) { + if (mac_addr && + (params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { brcmf_dbg(TRACE, "Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } @@ -2009,7 +2093,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)); @@ -2153,7 +2237,7 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; @@ -2163,12 +2247,14 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, s32 err = 0; u8 *bssid = profile->bssid; struct brcmf_sta_info_le sta_info_le; + u32 beacon_period; + u32 dtim_period; brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac); 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, @@ -2185,7 +2271,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); @@ -2217,6 +2303,30 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, sinfo->signal = rssi; brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD, + &beacon_period); + if (err) { + brcmf_err("Could not get beacon period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.beacon_interval = + beacon_period; + brcmf_dbg(CONN, "Beacon peroid %d\n", + beacon_period); + } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD, + &dtim_period); + if (err) { + brcmf_err("Could not get DTIM period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.dtim_period = dtim_period; + brcmf_dbg(CONN, "DTIM peroid %d\n", + dtim_period); + } + sinfo->filled |= STATION_INFO_BSS_PARAM; } } else err = -EPERM; @@ -2443,18 +2553,13 @@ 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) { struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); struct brcmf_bss_info_le *bi; struct brcmf_ssid *ssid; - struct brcmf_tlv *tim; + const struct brcmf_tlv *tim; u16 beacon_interval; u8 dtim_period; size_t ie_len; @@ -2556,8 +2661,8 @@ brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, ch_bss.band == ch_bss_info_le.band && bss_info_le->SSID_len == bss->SSID_len && !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { - if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == - (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) { + if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == + (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) { s16 bss_rssi = le16_to_cpu(bss->RSSI); s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); @@ -2566,13 +2671,13 @@ brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, */ if (bss_info_rssi > bss_rssi) bss->RSSI = bss_info_le->RSSI; - } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && - (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { + } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) && + (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) { /* preserve the on-channel rssi measurement * if the new measurement is off channel */ bss->RSSI = bss_info_le->RSSI; - bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; + bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL; } return 1; } @@ -2988,6 +3093,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, } set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + cfg->escan_info.run = brcmf_run_escan; err = brcmf_do_escan(cfg, wiphy, ifp, request); if (err) { clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); @@ -3073,7 +3179,7 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } if (!request->n_ssids || !request->n_match_sets) { - brcmf_err("Invalid sched scan req!! n_ssids:%d\n", + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", request->n_ssids); return -EINVAL; } @@ -3218,8 +3324,9 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) } static s32 -brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie) +brcmf_configure_wpaie(struct net_device *ndev, + const struct brcmf_vs_tlv *wpa_ie, + bool is_rsn_ie) { struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ @@ -3682,42 +3789,26 @@ brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, } static s32 -brcmf_cfg80211_set_channel(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, - struct ieee80211_channel *channel) -{ - u16 chanspec; - s32 err; - - brcmf_dbg(TRACE, "band=%d, center_freq=%d\n", channel->band, - channel->center_freq); - - chanspec = channel_to_chanspec(&cfg->d11inf, channel); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - - return err; -} - -static s32 brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_ap_settings *settings) { s32 ie_offset; struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *ssid_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; - struct brcmf_tlv *rsn_ie; - struct brcmf_vs_tlv *wpa_ie; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; struct brcmf_join_params join_params; enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; + u16 chanspec; - brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n", - cfg80211_get_chandef_type(&settings->chandef), - settings->beacon_interval, - settings->dtim_period); + brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", + settings->chandef.chan->hw_value, + settings->chandef.center_freq1, settings->chandef.width, + settings->beacon_interval, settings->dtim_period); brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); @@ -3774,9 +3865,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - err = brcmf_cfg80211_set_channel(cfg, ifp, settings->chandef.chan); + chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); if (err < 0) { - brcmf_err("Set Channel failed, %d\n", err); + brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); goto exit; } @@ -3923,7 +4015,7 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, static int brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, - u8 *mac) + const u8 *mac) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_scb_val_le scbval; @@ -3973,11 +4065,12 @@ brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, static int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct ieee80211_channel *chan = params->chan; + const u8 *buf = params->buf; + size_t len = params->len; const struct ieee80211_mgmt *mgmt; struct brcmf_cfg80211_vif *vif; s32 err = 0; @@ -4150,7 +4243,7 @@ static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) } static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, - struct net_device *ndev, u8 *peer, + struct net_device *ndev, const u8 *peer, enum nl80211_tdls_operation oper) { struct brcmf_if *ifp; @@ -4217,32 +4310,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 */ @@ -4337,11 +4404,13 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) WIPHY_FLAG_OFFCHAN_TX | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); err = wiphy_register(wiphy); if (err < 0) { @@ -4358,9 +4427,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, { struct brcmf_cfg80211_vif *vif; - if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT) - return ERR_PTR(-ENOSPC); - brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", sizeof(*vif)); vif = kzalloc(sizeof(*vif), GFP_KERNEL); @@ -4370,28 +4436,31 @@ 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; brcmf_init_prof(&vif->profile); list_add_tail(&vif->list, &cfg->vif_list); - cfg->vif_cnt++; return vif; } -void brcmf_free_vif(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif) +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif) { list_del(&vif->list); - cfg->vif_cnt--; - kfree(vif); - if (!cfg->vif_cnt) { - wiphy_unregister(cfg->wiphy); - wiphy_free(cfg->wiphy); - } +} + +void brcmf_cfg80211_free_netdev(struct net_device *ndev) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + ifp = netdev_priv(ndev); + vif = ifp->vif; + + brcmf_free_vif(vif); + free_netdev(ndev); } static bool brcmf_is_linkup(const struct brcmf_event_msg *e) @@ -4412,7 +4481,9 @@ static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) u32 event = e->event_code; u16 flags = e->flags; - if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) { + if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DISASSOC_IND) || + ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { brcmf_dbg(CONN, "Processing link down\n"); return true; } @@ -4654,16 +4725,18 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct net_device *ndev = ifp->ndev; struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan; s32 err = 0; - 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"); if (brcmf_is_ibssmode(ifp->vif)) { + chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); memcpy(profile->bssid, e->addr, ETH_ALEN); wl_inform_ibss(cfg, ndev, e->addr); - cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL); + cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); set_bit(BRCMF_VIF_STATUS_CONNECTED, @@ -4674,10 +4747,6 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); - if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) - cfg80211_disconnected(ndev, 0, NULL, 0, - GFP_KERNEL); } brcmf_link_down(ifp->vif); brcmf_init_prof(ndev_to_prof(ndev)); @@ -4871,11 +4940,8 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) cfg->scan_request = NULL; cfg->pwr_save = true; - cfg->roam_on = true; /* roam on & off switch. - we enable roam per default */ - cfg->active_scan = true; /* we do active scan for - specific scan per default */ - cfg->dongle_up = false; /* dongle is not up yet */ + cfg->active_scan = true; /* we do active scan per default */ + cfg->dongle_up = false; /* dongle is not up yet */ err = brcmf_init_priv_mem(cfg); if (err) return err; @@ -4900,6 +4966,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_CAP_40MHZ); + 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) { @@ -4957,6 +5047,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); @@ -4978,24 +5079,24 @@ cfg80211_p2p_attach_out: wl_deinit_priv(cfg); cfg80211_attach_out: - brcmf_free_vif(cfg, vif); + brcmf_free_vif(vif); return NULL; } void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) { - struct brcmf_cfg80211_vif *vif; - struct brcmf_cfg80211_vif *tmp; + if (!cfg) + return; - wl_deinit_priv(cfg); + WARN_ON(!list_empty(&cfg->vif_list)); + wiphy_unregister(cfg->wiphy); brcmf_btcoex_detach(cfg); - list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) { - brcmf_free_vif(cfg, vif); - } + wl_deinit_priv(cfg); + wiphy_free(cfg->wiphy); } static s32 -brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) +brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { s32 err = 0; __le32 roamtrigger[2]; @@ -5005,7 +5106,7 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) * Setup timeout if Beacons are lost and roam is * off to report link down */ - if (roamvar) { + if (brcmf_roamoff) { err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); if (err) { brcmf_err("bcn_timeout error (%d)\n", err); @@ -5017,8 +5118,9 @@ brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout) * Enable/Disable built-in roaming to allow supplicant * to take care of roaming */ - brcmf_dbg(INFO, "Internal Roaming = %s\n", roamvar ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar); + brcmf_dbg(INFO, "Internal Roaming = %s\n", + brcmf_roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); if (err) { brcmf_err("roam_off error (%d)\n", err); goto dongle_rom_out; @@ -5086,7 +5188,8 @@ dongle_scantime_out: } -static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) +static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, + u32 bw_cap[]) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct ieee80211_channel *band_chan_arr; @@ -5099,7 +5202,6 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) enum ieee80211_band band; u32 channel; u32 *n_cnt; - bool ht40_allowed; u32 index; u32 ht40_flag; bool update; @@ -5132,18 +5234,20 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) array_size = ARRAY_SIZE(__wl_2ghz_channels); n_cnt = &__wl_band_2ghz.n_channels; band = IEEE80211_BAND_2GHZ; - ht40_allowed = (bw_cap == WLC_N_BW_40ALL); } else if (ch.band == BRCMU_CHAN_BAND_5G) { band_chan_arr = __wl_5ghz_a_channels; array_size = ARRAY_SIZE(__wl_5ghz_a_channels); n_cnt = &__wl_band_5ghz_a.n_channels; band = IEEE80211_BAND_5GHZ; - ht40_allowed = !(bw_cap == WLC_N_BW_20ALL); } else { - brcmf_err("Invalid channel Sepc. 0x%x.\n", ch.chspec); + brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); continue; } - if (!ht40_allowed && ch.bw == BRCMU_CHAN_BW_40) + if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_40) + continue; + if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_80) continue; update = false; for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { @@ -5161,10 +5265,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) ieee80211_channel_to_frequency(ch.chnum, band); band_chan_arr[index].hw_value = ch.chnum; - if (ch.bw == BRCMU_CHAN_BW_40 && ht40_allowed) { - /* assuming the order is HT20, HT40 Upper, - * HT40 lower from chanspecs - */ + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_80MHZ; + } else if (ch.bw == BRCMU_CHAN_BW_40) { ht40_flag = band_chan_arr[index].flags & IEEE80211_CHAN_NO_HT40; if (ch.sb == BRCMU_CHAN_SB_U) { @@ -5185,8 +5292,13 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) IEEE80211_CHAN_NO_HT40MINUS; } } else { + /* disable other bandwidths for now as mentioned + * order assure they are enabled for subsequent + * chanspecs. + */ band_chan_arr[index].flags = - IEEE80211_CHAN_NO_HT40; + IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; ch.bw = BRCMU_CHAN_BW_20; cfg->d11inf.encchspec(&ch); channel = ch.chspec; @@ -5197,10 +5309,10 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) if (channel & WL_CHAN_RADAR) band_chan_arr[index].flags |= (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IBSS); + IEEE80211_CHAN_NO_IR); if (channel & WL_CHAN_PASSIVE) band_chan_arr[index].flags |= - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR; } } if (!update) @@ -5212,6 +5324,95 @@ exit: return err; } +static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) +{ + u32 band, mimo_bwcap; + int err; + + band = WLC_BAND_2G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[IEEE80211_BAND_2GHZ] = band; + band = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[IEEE80211_BAND_5GHZ] = band; + return; + } + WARN_ON(1); + return; + } + brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); + mimo_bwcap = 0; + err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap); + if (err) + /* assume 20MHz if firmware does not give a clue */ + mimo_bwcap = WLC_N_BW_20ALL; + + switch (mimo_bwcap) { + case WLC_N_BW_40ALL: + bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; + /* fall-thru */ + case WLC_N_BW_20IN2G_40IN5G: + bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; + /* fall-thru */ + case WLC_N_BW_20ALL: + bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; + bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; + break; + default: + brcmf_err("invalid mimo_bw_cap value\n"); + } +} + +static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + band->ht_cap.ht_supported = true; + if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) +{ + u16 mcs_map; + int i; + + for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) + mcs_map = (mcs_map << 2) | supp; + + return cpu_to_le16(mcs_map); +} + +static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + __le16 mcs_map; + + /* not allowed in 2.4G band */ + if (band->band == IEEE80211_BAND_2GHZ) + return; + + band->vht_cap.vht_supported = true; + /* 80MHz is mandatory */ + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { + band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; + } + /* all support 256-QAM */ + mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); + band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; + band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; +} static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) { @@ -5219,14 +5420,17 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) struct wiphy *wiphy; s32 phy_list; u32 band_list[3]; - u32 nmode; - u32 bw_cap = 0; + u32 nmode = 0; + u32 vhtmode = 0; + u32 bw_cap[2] = { 0, 0 }; + u32 rxchain; + u32 nchain; s8 phy; s32 err; u32 nband; s32 i; - struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; - s32 index; + struct ieee80211_supported_band *bands[2] = { NULL, NULL }; + struct ieee80211_supported_band *band; err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST, &phy_list, sizeof(phy_list)); @@ -5248,15 +5452,26 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", band_list[0], band_list[1], band_list[2]); + (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); if (err) { brcmf_err("nmode error (%d)\n", err); } else { - err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &bw_cap); - if (err) - brcmf_err("mimo_bw_cap error (%d)\n", err); + brcmf_get_bwcap(ifp, bw_cap); + } + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], + bw_cap[IEEE80211_BAND_5GHZ]); + + err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); + if (err) { + brcmf_err("rxchain error (%d)\n", err); + nchain = 1; + } else { + for (nchain = 0; rxchain; nchain++) + rxchain = rxchain & (rxchain - 1); } - brcmf_dbg(INFO, "nmode=%d, mimo_bw_cap=%d\n", nmode, bw_cap); + brcmf_dbg(INFO, "nchain=%d\n", nchain); err = brcmf_construct_reginfo(cfg, bw_cap); if (err) { @@ -5265,40 +5480,23 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) } nband = band_list[0]; - memset(bands, 0, sizeof(bands)); for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) { - index = -1; + band = NULL; if ((band_list[i] == WLC_BAND_5G) && - (__wl_band_5ghz_a.n_channels > 0)) { - index = IEEE80211_BAND_5GHZ; - bands[index] = &__wl_band_5ghz_a; - if ((bw_cap == WLC_N_BW_40ALL) || - (bw_cap == WLC_N_BW_20IN2G_40IN5G)) - bands[index]->ht_cap.cap |= - IEEE80211_HT_CAP_SGI_40; - } else if ((band_list[i] == WLC_BAND_2G) && - (__wl_band_2ghz.n_channels > 0)) { - index = IEEE80211_BAND_2GHZ; - bands[index] = &__wl_band_2ghz; - if (bw_cap == WLC_N_BW_40ALL) - bands[index]->ht_cap.cap |= - IEEE80211_HT_CAP_SGI_40; - } + (__wl_band_5ghz_a.n_channels > 0)) + band = &__wl_band_5ghz_a; + else if ((band_list[i] == WLC_BAND_2G) && + (__wl_band_2ghz.n_channels > 0)) + band = &__wl_band_2ghz; + else + continue; - if ((index >= 0) && nmode) { - bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - bands[index]->ht_cap.ht_supported = true; - bands[index]->ht_cap.ampdu_factor = - IEEE80211_HT_MAX_AMPDU_64K; - bands[index]->ht_cap.ampdu_density = - IEEE80211_HT_MPDU_DENSITY_16; - /* An HT shall support all EQM rates for one spatial - * stream - */ - bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; - } + if (nmode) + brcmf_update_ht_cap(band, bw_cap, nchain); + if (vhtmode) + brcmf_update_vht_cap(band, bw_cap, nchain); + bands[band->band] = band; } wiphy = cfg_to_wiphy(cfg); @@ -5343,7 +5541,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dbg(INFO, "power save set to %s\n", (power_mode ? "enabled" : "disabled")); - err = brcmf_dongle_roam(ifp, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT); + err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); if (err) goto default_conf_out; err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index d9bdaf9a72d..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; @@ -402,7 +385,6 @@ struct brcmf_cfg80211_info { bool ibss_starter; bool pwr_save; bool dongle_up; - bool roam_on; bool scan_tried; u8 *dcmd_buf; u8 *extra_buf; @@ -412,7 +394,6 @@ struct brcmf_cfg80211_info { struct work_struct escan_timeout_work; u8 *escan_ioctl_buf; struct list_head vif_list; - u8 vif_cnt; struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; struct brcmu_d11inf d11inf; @@ -487,13 +468,13 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp); struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype type, bool pm_block); -void brcmf_free_vif(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif); +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, const u8 *vndr_ie_buf, u32 vndr_ie_len); s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); -struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key); +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key); u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, struct ieee80211_channel *ch); u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state); @@ -507,5 +488,6 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, bool fw_abort); void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); +void brcmf_cfg80211_free_netdev(struct net_device *ndev); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h index a8a267b5b87..2d08c155c23 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h @@ -172,19 +172,19 @@ struct si_info { /* AMBA Interconnect exported externs */ -extern u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); +u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); /* === exported functions === */ -extern struct si_pub *ai_attach(struct bcma_bus *pbus); -extern void ai_detach(struct si_pub *sih); -extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); -extern void ai_clkctl_init(struct si_pub *sih); -extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); -extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); -extern bool ai_deviceremoved(struct si_pub *sih); +struct si_pub *ai_attach(struct bcma_bus *pbus); +void ai_detach(struct si_pub *sih); +uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); +void ai_clkctl_init(struct si_pub *sih); +u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); +bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); +bool ai_deviceremoved(struct si_pub *sih); /* Enable Ex-PA for 4313 */ -extern void ai_epa_4313war(struct si_pub *sih); +void ai_epa_4313war(struct si_pub *sih); static inline u32 ai_get_cccaps(struct si_pub *sih) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h index 73d01e58610..03bdcf29bd5 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h @@ -37,17 +37,17 @@ struct brcms_ampdu_session { u16 dma_len; }; -extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, - struct brcms_c_info *wlc); -extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, - struct sk_buff *p); -extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); +void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, + struct brcms_c_info *wlc); +int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, + struct sk_buff *p); +void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); -extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); -extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); -extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, - struct sk_buff *p, struct tx_status *txs); -extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); -extern void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); +struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); +void brcms_c_ampdu_detach(struct ampdu_info *ampdu); +void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, + struct sk_buff *p, struct tx_status *txs); +void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); +void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); #endif /* _BRCM_AMPDU_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h index 97ea3881a8e..a3d487ab196 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h @@ -17,13 +17,11 @@ #ifndef _BRCM_ANTSEL_H_ #define _BRCM_ANTSEL_H_ -extern struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); -extern void brcms_c_antsel_detach(struct antsel_info *asi); -extern void brcms_c_antsel_init(struct antsel_info *asi); -extern void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, - bool sel, - u8 id, u8 fbid, u8 *antcfg, - u8 *fbantcfg); -extern u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); +struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); +void brcms_c_antsel_detach(struct antsel_info *asi); +void brcms_c_antsel_init(struct antsel_info *asi); +void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, + u8 id, u8 fbid, u8 *antcfg, u8 *fbantcfg); +u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); #endif /* _BRCM_ANTSEL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index cc87926f505..635ae034c7e 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -59,23 +59,18 @@ #define BRCM_2GHZ_2412_2462 REG_RULE(2412-10, 2462+10, 40, 0, 19, 0) #define BRCM_2GHZ_2467_2472 REG_RULE(2467-10, 2472+10, 20, 0, 19, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define BRCM_5GHZ_5180_5240 REG_RULE(5180-10, 5240+10, 40, 0, 21, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define BRCM_5GHZ_5260_5320 REG_RULE(5260-10, 5320+10, 40, 0, 21, \ - NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_DFS | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define BRCM_5GHZ_5500_5700 REG_RULE(5500-10, 5700+10, 40, 0, 21, \ - NL80211_RRF_PASSIVE_SCAN | \ NL80211_RRF_DFS | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define BRCM_5GHZ_5745_5825 REG_RULE(5745-10, 5825+10, 40, 0, 21, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) static const struct ieee80211_regdomain brcms_regdom_x2 = { .n_reg_rules = 6, @@ -395,7 +390,7 @@ brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false); brcms_b_set_chanspec(wlc->hw, chanspec, - !!(ch->flags & IEEE80211_CHAN_PASSIVE_SCAN), + !!(ch->flags & IEEE80211_CHAN_NO_IR), &txpwr); } @@ -657,8 +652,8 @@ static void brcms_reg_apply_radar_flags(struct wiphy *wiphy) */ if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch->flags |= IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_NO_IR; } } @@ -684,18 +679,15 @@ brcms_reg_apply_beaconing_flags(struct wiphy *wiphy, continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - rule = freq_reg_info(wiphy, ch->center_freq); + rule = freq_reg_info(wiphy, + MHZ_TO_KHZ(ch->center_freq)); if (IS_ERR(rule)) continue; - if (!(rule->flags & NL80211_RRF_NO_IBSS)) - ch->flags &= ~IEEE80211_CHAN_NO_IBSS; - if (!(rule->flags & NL80211_RRF_PASSIVE_SCAN)) - ch->flags &= - ~IEEE80211_CHAN_PASSIVE_SCAN; + if (!(rule->flags & NL80211_RRF_NO_IR)) + ch->flags &= ~IEEE80211_CHAN_NO_IR; } else if (ch->beacon_found) { - ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN); + ch->flags &= ~IEEE80211_CHAN_NO_IR; } } } @@ -775,8 +767,8 @@ void brcms_c_regd_init(struct brcms_c_info *wlc) } wlc->wiphy->reg_notifier = brcms_reg_notifier; - wlc->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_STRICT_REGULATORY; + wlc->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG; wiphy_apply_custom_regulatory(wlc->wiphy, regd->regdomain); brcms_reg_apply_beaconing_flags(wiphy, NL80211_REGDOM_SET_BY_DRIVER); } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h index 006483a0abe..39dd3a5b297 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.h @@ -32,20 +32,16 @@ #define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */ -extern struct brcms_cm_info * -brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); +struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); -extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); +void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); -extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, - u16 chspec); +bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec); -extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, - u16 chanspec, - struct txpwr_limits *txpwr); -extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, - u16 chanspec, - u8 local_constraint_qdbm); -extern void brcms_c_regd_init(struct brcms_c_info *wlc); +void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, + struct txpwr_limits *txpwr); +void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, + u8 local_constraint_qdbm); +void brcms_c_regd_init(struct brcms_c_info *wlc); #endif /* _WLC_CHANNEL_H */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index edc5d105ff9..43c71bfaa47 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -125,13 +125,13 @@ static struct ieee80211_channel brcms_2ghz_chantable[] = { CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(12, 2467, - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(13, 2472, - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN2GHZ(14, 2484, - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS | IEEE80211_CHAN_NO_OFDM) }; @@ -144,51 +144,51 @@ static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = { CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS), /* UNII-2 */ CHAN5GHZ(52, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(56, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(60, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(64, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), /* MID */ CHAN5GHZ(100, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(104, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(108, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(112, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(116, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(120, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(124, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(128, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(132, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40MINUS), CHAN5GHZ(136, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS), + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS), CHAN5GHZ(140, - IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS | + IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS), /* UNII-3 */ CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS), @@ -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; @@ -899,7 +897,8 @@ static bool brcms_tx_flush_completed(struct brcms_info *wl) return result; } -static void brcms_ops_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void brcms_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct brcms_info *wl = hw->priv; int ret; @@ -1071,7 +1070,6 @@ static int ieee_hw_init(struct ieee80211_hw *hw) hw->max_rates = 2; /* Primary rate and 1 fallback rate */ /* channel change time is dependent on chip and band */ - hw->channel_change_time = 7 * 1000; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_ADHOC); @@ -1095,12 +1093,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/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h index 4090032e81a..198053dfc31 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h @@ -88,26 +88,26 @@ struct brcms_info { }; /* misc callbacks */ -extern void brcms_init(struct brcms_info *wl); -extern uint brcms_reset(struct brcms_info *wl); -extern void brcms_intrson(struct brcms_info *wl); -extern u32 brcms_intrsoff(struct brcms_info *wl); -extern void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); -extern int brcms_up(struct brcms_info *wl); -extern void brcms_down(struct brcms_info *wl); -extern void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, - bool state, int prio); -extern bool brcms_rfkill_set_hw_state(struct brcms_info *wl); +void brcms_init(struct brcms_info *wl); +uint brcms_reset(struct brcms_info *wl); +void brcms_intrson(struct brcms_info *wl); +u32 brcms_intrsoff(struct brcms_info *wl); +void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); +int brcms_up(struct brcms_info *wl); +void brcms_down(struct brcms_info *wl); +void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, + bool state, int prio); +bool brcms_rfkill_set_hw_state(struct brcms_info *wl); /* timer functions */ -extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl, - void (*fn) (void *arg), void *arg, - const char *name); -extern void brcms_free_timer(struct brcms_timer *timer); -extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); -extern bool brcms_del_timer(struct brcms_timer *timer); -extern void brcms_dpc(unsigned long data); -extern void brcms_timer(struct brcms_timer *t); -extern void brcms_fatal_error(struct brcms_info *wl); +struct brcms_timer *brcms_init_timer(struct brcms_info *wl, + void (*fn) (void *arg), void *arg, + const char *name); +void brcms_free_timer(struct brcms_timer *timer); +void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); +bool brcms_del_timer(struct brcms_timer *timer); +void brcms_dpc(unsigned long data); +void brcms_timer(struct brcms_timer *t); +void brcms_fatal_error(struct brcms_info *wl); #endif /* _BRCM_MAC80211_IF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 4608e0eb149..af8ba64ace3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -1906,14 +1906,14 @@ static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ /* If macaddr exists, use it (Sromrev4, CIS, ...). */ if (!is_zero_ether_addr(sprom->il0mac)) { - memcpy(etheraddr, sprom->il0mac, 6); + memcpy(etheraddr, sprom->il0mac, ETH_ALEN); return; } if (wlc_hw->_nbands > 1) - memcpy(etheraddr, sprom->et1mac, 6); + memcpy(etheraddr, sprom->et1mac, ETH_ALEN); else - memcpy(etheraddr, sprom->il0mac, 6); + memcpy(etheraddr, sprom->il0mac, ETH_ALEN); } /* power both the pll and external oscillator on/off */ @@ -4870,14 +4870,11 @@ static void brcms_c_detach_module(struct brcms_c_info *wlc) /* * low level detach */ -static int brcms_b_detach(struct brcms_c_info *wlc) +static void brcms_b_detach(struct brcms_c_info *wlc) { uint i; struct brcms_hw_band *band; struct brcms_hardware *wlc_hw = wlc->hw; - int callbacks; - - callbacks = 0; brcms_b_detach_dmapio(wlc_hw); @@ -4900,9 +4897,6 @@ static int brcms_b_detach(struct brcms_c_info *wlc) ai_detach(wlc_hw->sih); wlc_hw->sih = NULL; } - - return callbacks; - } /* @@ -4917,14 +4911,15 @@ static int brcms_b_detach(struct brcms_c_info *wlc) */ uint brcms_c_detach(struct brcms_c_info *wlc) { - uint callbacks = 0; + uint callbacks; if (wlc == NULL) return 0; - callbacks += brcms_b_detach(wlc); + brcms_b_detach(wlc); /* delete software timers */ + callbacks = 0; if (!brcms_c_radio_monitor_stop(wlc)) callbacks++; @@ -5695,7 +5690,7 @@ static bool brcms_c_chipmatch_pci(struct bcma_device *core) return true; if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) return true; - if (device == BCM4313_D11N2G_ID) + if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID) return true; if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) return true; @@ -7108,7 +7103,6 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, struct sk_buff *p, struct ieee80211_rx_status *rx_status) { - int preamble; int channel; u32 rspec; unsigned char *plcp; @@ -7191,7 +7185,6 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET; /* Determine short preamble and rate_idx */ - preamble = 0; if (is_cck_rate(rspec)) { if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) rx_status->flag |= RX_FLAG_SHORTPRE; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/brcm80211/brcmsmac/main.h index b5d7a38b53f..c4d135cff04 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.h @@ -616,66 +616,54 @@ struct brcms_bss_cfg { struct brcms_bss_info *current_bss; }; -extern int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, - struct sk_buff *p); -extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, - uint *blocks); - -extern int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); -extern void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); -extern u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, - uint mac_len); -extern u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, - u32 rspec, - bool use_rspec, u16 mimo_ctlchbw); -extern u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, - u32 rts_rate, - u32 frame_rate, - u8 rts_preamble_type, - u8 frame_preamble_type, uint frame_len, - bool ba); -extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, - struct ieee80211_sta *sta, - void (*dma_callback_fn)); -extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); -extern int brcms_c_set_nmode(struct brcms_c_info *wlc); -extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, - u32 bcn_rate); -extern void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, - u8 antsel_type); -extern void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, - u16 chanspec, - bool mute, struct txpwr_limits *txpwr); -extern void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, - u16 v); -extern u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); -extern void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, - u16 val, int bands); -extern void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); -extern void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); -extern void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); -extern void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); -extern void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); -extern void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, - u32 override_bit); -extern void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, - u32 override_bit); -extern void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, - int offset, int len, void *buf); -extern u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); -extern void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, - uint offset, const void *buf, int len, - u32 sel); -extern void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, - void *buf, int len, u32 sel); -extern void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); -extern u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); -extern void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); -extern void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); -extern void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); -extern void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); -extern void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, - u8 stf_mode); -extern void brcms_c_init_scb(struct scb *scb); +int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p); +int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, + uint *blocks); + +int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); +void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); +u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len); +u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, + bool use_rspec, u16 mimo_ctlchbw); +u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, + u32 rts_rate, u32 frame_rate, + u8 rts_preamble_type, u8 frame_preamble_type, + uint frame_len, bool ba); +void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, + struct ieee80211_sta *sta, void (*dma_callback_fn)); +void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); +int brcms_c_set_nmode(struct brcms_c_info *wlc); +void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rate); +void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type); +void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, + bool mute, struct txpwr_limits *txpwr); +void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v); +u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); +void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, + int bands); +void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); +void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); +void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); +void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); +void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); +void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, + u32 override_bit); +void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, + u32 override_bit); +void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, + int len, void *buf); +u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); +void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, + const void *buf, int len, u32 sel); +void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, + void *buf, int len, u32 sel); +void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); +u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); +void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); +void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); +void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); +void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); +void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode); +void brcms_c_init_scb(struct scb *scb); #endif /* _BRCM_MAIN_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h index e34a71e7d24..4d3734f48d9 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h @@ -179,121 +179,106 @@ struct shared_phy_params { }; -extern struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); -extern struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, - struct bcma_device *d11core, - int bandtype, struct wiphy *wiphy); -extern void wlc_phy_detach(struct brcms_phy_pub *ppi); - -extern bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, - u16 *phyrev, u16 *radioid, - u16 *radiover); -extern bool wlc_phy_get_encore(struct brcms_phy_pub *pih); -extern u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); - -extern void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); -extern void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); -extern void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); -extern void wlc_phy_watchdog(struct brcms_phy_pub *ppi); -extern int wlc_phy_down(struct brcms_phy_pub *ppi); -extern u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); -extern void wlc_phy_cal_init(struct brcms_phy_pub *ppi); -extern void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); - -extern void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, - u16 chanspec); -extern u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); -extern void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, - u16 newch); -extern u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); -extern void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); - -extern int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, - struct d11rxhdr *rxh); -extern void wlc_phy_por_inform(struct brcms_phy_pub *ppi); -extern void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); -extern bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); - -extern void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); - -extern void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); -extern void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); - - -extern void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); - -extern void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, - bool wide_filter); -extern void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, - struct brcms_chanvec *channels); -extern u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, - uint band); - -extern void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, - u8 *_min_, u8 *_max_, int rate); -extern void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, - uint chan, u8 *_max_, u8 *_min_); -extern void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, - uint band, s32 *, s32 *, u32 *); -extern void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, - struct txpwr_limits *, - u16 chanspec); -extern int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, - bool *override); -extern int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, - bool override); -extern void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, - struct txpwr_limits *); -extern bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); -extern void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, - bool hwpwrctrl); -extern u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); -extern u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); -extern bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); - -extern void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, - u8 rxchain); -extern void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, - u8 rxchain); -extern void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, - u8 *rxchain); -extern u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); -extern s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, - u16 chanspec); -extern void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); - -extern void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); -extern void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); -extern void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); -extern void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); - -extern void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); -extern void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); -extern void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); -extern void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); - -extern void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); - -extern void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, - struct tx_power *power, uint channel); - -extern void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); -extern bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); -extern void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, - u8 txpwr_percent); -extern void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); -extern void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, - bool bf_preempt); -extern void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); - -extern void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); - -extern void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); -extern void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); - -extern const u8 *wlc_phy_get_ofdm_rate_lookup(void); - -extern s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, - u8 mcs_offset); -extern s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); +struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); +struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, + struct bcma_device *d11core, int bandtype, + struct wiphy *wiphy); +void wlc_phy_detach(struct brcms_phy_pub *ppi); + +bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, + u16 *phyrev, u16 *radioid, u16 *radiover); +bool wlc_phy_get_encore(struct brcms_phy_pub *pih); +u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); + +void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); +void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); +void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); +void wlc_phy_watchdog(struct brcms_phy_pub *ppi); +int wlc_phy_down(struct brcms_phy_pub *ppi); +u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); +void wlc_phy_cal_init(struct brcms_phy_pub *ppi); +void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); + +void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec); +u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); +void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch); +u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); +void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); + +int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh); +void wlc_phy_por_inform(struct brcms_phy_pub *ppi); +void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); +bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); + +void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); + +void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); +void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); + + +void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); + +void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, + bool wide_filter); +void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, + struct brcms_chanvec *channels); +u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band); + +void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, u8 *_min_, + u8 *_max_, int rate); +void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, + u8 *_max_, u8 *_min_); +void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint band, + s32 *, s32 *, u32 *); +void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *, + u16 chanspec); +int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override); +int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override); +void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, + struct txpwr_limits *); +bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); +void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl); +u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); +u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); +bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); + +void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); +void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); +void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain); +u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); +s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec); +void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); + +void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); +void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); +void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); +void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); + +void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); +void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); +void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); +void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); + +void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); + +void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, + struct tx_power *power, uint channel); + +void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); +bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); +void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent); +void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); +void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt); +void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); + +void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); + +void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); +void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); + +const u8 *wlc_phy_get_ofdm_rate_lookup(void); + +s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, + u8 mcs_offset); +s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); #endif /* _BRCM_PHY_HAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h index 1dc767c3165..4960f7d2680 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h @@ -910,113 +910,103 @@ struct lcnphy_radio_regs { u8 do_init_g; }; -extern u16 read_phy_reg(struct brcms_phy *pi, u16 addr); -extern void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); - -extern u16 read_radio_reg(struct brcms_phy *pi, u16 addr); -extern void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, - u16 val); -extern void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); - -extern void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); - -extern void wlc_phyreg_enter(struct brcms_phy_pub *pih); -extern void wlc_phyreg_exit(struct brcms_phy_pub *pih); -extern void wlc_radioreg_enter(struct brcms_phy_pub *pih); -extern void wlc_radioreg_exit(struct brcms_phy_pub *pih); - -extern void wlc_phy_read_table(struct brcms_phy *pi, - const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, - u16 tblDatalo); -extern void wlc_phy_write_table(struct brcms_phy *pi, - const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, u16 tblDatalo); -extern void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, - uint tbl_offset, u16 tblAddr, u16 tblDataHi, - u16 tblDataLo); -extern void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); - -extern void write_phy_channel_reg(struct brcms_phy *pi, uint val); -extern void wlc_phy_txpower_update_shm(struct brcms_phy *pi); - -extern u8 wlc_phy_nbits(s32 value); -extern void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); - -extern uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, - struct radio_20xx_regs *radioregs); -extern uint wlc_phy_init_radio_regs(struct brcms_phy *pi, - const struct radio_regs *radioregs, - u16 core_offset); - -extern void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); - -extern void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); -extern void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, - s32 *eps_imag); - -extern void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); -extern void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); - -extern bool wlc_phy_attach_nphy(struct brcms_phy *pi); -extern bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_detach_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_init_nphy(struct brcms_phy *pi); -extern void wlc_phy_init_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_cal_init_nphy(struct brcms_phy *pi); -extern void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, - u16 chanspec); -extern void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, - u16 chanspec); -extern void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, - u16 chanspec); -extern int wlc_phy_channel2freq(uint channel); -extern int wlc_phy_chanspec_freq2bandrange_lpssn(uint); -extern int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); - -extern void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); -extern s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); - -extern void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); -extern void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); -extern void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); - -extern void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); -extern void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); -extern void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); -extern void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, - u16 max_val, bool iqcalmode); - -extern void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, - u8 *max_pwr, u8 rate_id); -extern void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, - u8 rate_mcs_end, - u8 rate_ofdm_start); -extern void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, - u8 rate_ofdm_start, - u8 rate_ofdm_end, - u8 rate_mcs_start); - -extern u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); -extern s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); -extern s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); -extern s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); -extern void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); -extern void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); -extern void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); -extern void wlc_2064_vco_cal(struct brcms_phy *pi); - -extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); +u16 read_phy_reg(struct brcms_phy *pi, u16 addr); +void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); + +u16 read_radio_reg(struct brcms_phy *pi, u16 addr); +void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); +void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); +void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); +void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); + +void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); + +void wlc_phyreg_enter(struct brcms_phy_pub *pih); +void wlc_phyreg_exit(struct brcms_phy_pub *pih); +void wlc_radioreg_enter(struct brcms_phy_pub *pih); +void wlc_radioreg_exit(struct brcms_phy_pub *pih); + +void wlc_phy_read_table(struct brcms_phy *pi, + const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDatalo); +void wlc_phy_write_table(struct brcms_phy *pi, + const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDatalo); +void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, + u16 tblAddr, u16 tblDataHi, u16 tblDataLo); +void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); + +void write_phy_channel_reg(struct brcms_phy *pi, uint val); +void wlc_phy_txpower_update_shm(struct brcms_phy *pi); + +u8 wlc_phy_nbits(s32 value); +void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); + +uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, + struct radio_20xx_regs *radioregs); +uint wlc_phy_init_radio_regs(struct brcms_phy *pi, + const struct radio_regs *radioregs, + u16 core_offset); + +void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); + +void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); +void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag); + +void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); +void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); + +bool wlc_phy_attach_nphy(struct brcms_phy *pi); +bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); + +void wlc_phy_detach_lcnphy(struct brcms_phy *pi); + +void wlc_phy_init_nphy(struct brcms_phy *pi); +void wlc_phy_init_lcnphy(struct brcms_phy *pi); + +void wlc_phy_cal_init_nphy(struct brcms_phy *pi); +void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); + +void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec); +void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec); +void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, u16 chanspec); +int wlc_phy_channel2freq(uint channel); +int wlc_phy_chanspec_freq2bandrange_lpssn(uint); +int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); + +void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); +s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); + +void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); +void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); +void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); + +void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); +void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); +void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); +void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, + bool iqcalmode); + +void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, + u8 *max_pwr, u8 rate_id); +void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, + u8 rate_mcs_end, u8 rate_ofdm_start); +void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, + u8 rate_ofdm_end, u8 rate_mcs_start); + +u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); +s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); +s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); +s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); +void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); +void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); +void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); +void wlc_2064_vco_cal(struct brcms_phy *pi); + +void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); #define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18 #define LCNPHY_TX_POWER_TABLE_SIZE 128 @@ -1030,26 +1020,24 @@ extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); #define LCNPHY_TX_PWR_CTRL_TEMPBASED 0xE001 -extern void wlc_lcnphy_write_table(struct brcms_phy *pi, - const struct phytbl_info *pti); -extern void wlc_lcnphy_read_table(struct brcms_phy *pi, - struct phytbl_info *pti); -extern void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); -extern void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); -extern void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); -extern u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); -extern void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, - u8 *eq0, u8 *fi0, u8 *fq0); -extern void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); -extern void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); -extern bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); -extern void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); -extern s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); -extern void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, - s8 *cck_pwr); -extern void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); - -extern s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); +void wlc_lcnphy_write_table(struct brcms_phy *pi, + const struct phytbl_info *pti); +void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti); +void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); +void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); +void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); +u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); +void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0, + u8 *fq0); +void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); +void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); +bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); +void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); +s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); +void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr); +void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); + +s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); #define NPHY_MAX_HPVGA1_INDEX 10 #define NPHY_DEF_HPVGA1_INDEXLIMIT 7 @@ -1060,9 +1048,8 @@ struct phy_iq_est { u32 q_pwr; }; -extern void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, - bool enable); -extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); +void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable); +void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); #define wlc_phy_write_table_nphy(pi, pti) \ wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73) @@ -1076,10 +1063,10 @@ extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); #define wlc_nphy_table_data_write(pi, w, v) \ wlc_phy_table_data_write((pi), (w), (v)) -extern void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, - u32 w, void *d); -extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, - u32, const void *); +void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, u32 w, + void *d); +void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, u32, + const void *); #define PHY_IPA(pi) \ ((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \ @@ -1089,73 +1076,67 @@ extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, if (NREV_LT((pi)->pubpi.phy_rev, 3)) \ (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) -extern void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); -extern void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); -extern void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); - -extern u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); -extern void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); - -extern void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); - -extern void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); -extern s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); - -extern u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); - -extern void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, - u16 num_samps, u8 wait_time, - u8 wait_for_crs); - -extern void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, - struct nphy_iq_comp *comp); -extern void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); - -extern void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, - u8 rxcore_bitmask); -extern u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); - -extern void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); -extern void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); -extern void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); -extern void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); -extern u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); - -extern struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); -extern int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, - struct nphy_txgains target_gain, - bool full, bool m); -extern int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, - struct nphy_txgains target_gain, - u8 type, bool d); -extern void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, - s8 txpwrindex, bool res); -extern void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); -extern int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, - s32 *rssi_buf, u8 nsamps); -extern void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); -extern int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); -extern void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, - s32 dBm_targetpower, bool debug); -extern int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, - u8 mode, u8, bool); -extern void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); -extern void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, - u8 num_samps); -extern void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); - -extern int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, - struct d11rxhdr *rxh); +void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); +void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); +void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); + +u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); +void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); + +void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); + +void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); +s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); + +u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); + +void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, + u16 num_samps, u8 wait_time, u8 wait_for_crs); + +void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, + struct nphy_iq_comp *comp); +void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); + +void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask); +u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); + +void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); +void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); +void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); +void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); +u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); + +struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); +int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, + struct nphy_txgains target_gain, bool full, bool m); +int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, + u8 type, bool d); +void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, + s8 txpwrindex, bool res); +void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); +int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, + s32 *rssi_buf, u8 nsamps); +void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); +int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); +void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, + bool debug); +int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 mode, + u8, bool); +void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); +void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, + u8 num_samps); +void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); + +int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh); #define NPHY_TESTPATTERN_BPHY_EVM 0 #define NPHY_TESTPATTERN_BPHY_RFCS 1 -extern void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); +void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset); -extern s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, - u16 chanspec); +s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec); -extern bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); +bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); #endif /* _BRCM_PHY_INT_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h index 2c5b66b7597..dd8774717ad 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h @@ -124,56 +124,49 @@ struct brcms_phy; -extern struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, - struct brcms_info *wl, - struct brcms_c_info *wlc); -extern void wlc_phy_shim_detach(struct phy_shim_info *physhim); +struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, + struct brcms_info *wl, + struct brcms_c_info *wlc); +void wlc_phy_shim_detach(struct phy_shim_info *physhim); /* PHY to WL utility functions */ -extern struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, - void (*fn) (struct brcms_phy *pi), - void *arg, const char *name); -extern void wlapi_free_timer(struct wlapi_timer *t); -extern void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); -extern bool wlapi_del_timer(struct wlapi_timer *t); -extern void wlapi_intrson(struct phy_shim_info *physhim); -extern u32 wlapi_intrsoff(struct phy_shim_info *physhim); -extern void wlapi_intrsrestore(struct phy_shim_info *physhim, - u32 macintmask); - -extern void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, - u16 v); -extern u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); -extern void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, - u16 mask, u16 val, int bands); -extern void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); -extern void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); -extern void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); -extern void wlapi_enable_mac(struct phy_shim_info *physhim); -extern void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, - u32 val); -extern void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); -extern void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); -extern void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); -extern void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); -extern void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); -extern void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); -extern void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info * - physhim); -extern void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info * - physhim); -extern void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, - int len, void *buf); -extern u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, - u8 rate); -extern void wlapi_ucode_sample_init(struct phy_shim_info *physhim); -extern void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, - void *buf, int, u32 sel); -extern void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, - const void *buf, int, u32); - -extern void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, - u32 phy_mode); -extern u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); +struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, + void (*fn)(struct brcms_phy *pi), + void *arg, const char *name); +void wlapi_free_timer(struct wlapi_timer *t); +void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); +bool wlapi_del_timer(struct wlapi_timer *t); +void wlapi_intrson(struct phy_shim_info *physhim); +u32 wlapi_intrsoff(struct phy_shim_info *physhim); +void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask); + +void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v); +u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); +void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val, + int bands); +void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); +void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); +void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); +void wlapi_enable_mac(struct phy_shim_info *physhim); +void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val); +void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); +void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); +void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); +void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); +void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); +void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); +void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim); +void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim); +void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, + int len, void *buf); +u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate); +void wlapi_ucode_sample_init(struct phy_shim_info *physhim); +void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, void *buf, + int, u32 sel); +void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, const void *buf, + int, u32); + +void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, u32 phy_mode); +u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); #endif /* _BRCM_PHY_SHIM_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h index 20e2012d5a3..a014bbc4f93 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h @@ -20,7 +20,7 @@ #include "types.h" -extern u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); -extern u32 si_pmu_measure_alpclk(struct si_pub *sih); +u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); +u32 si_pmu_measure_alpclk(struct si_pub *sih); #endif /* _BRCM_PMU_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h index d36ea5e1cc4..4da38cb4f31 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h @@ -266,83 +266,76 @@ struct brcms_antselcfg { }; /* common functions for every port */ -extern struct brcms_c_info * -brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, - bool piomode, uint *perr); -extern uint brcms_c_detach(struct brcms_c_info *wlc); -extern int brcms_c_up(struct brcms_c_info *wlc); -extern uint brcms_c_down(struct brcms_c_info *wlc); - -extern bool brcms_c_chipmatch(struct bcma_device *core); -extern void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); -extern void brcms_c_reset(struct brcms_c_info *wlc); - -extern void brcms_c_intrson(struct brcms_c_info *wlc); -extern u32 brcms_c_intrsoff(struct brcms_c_info *wlc); -extern void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); -extern bool brcms_c_intrsupd(struct brcms_c_info *wlc); -extern bool brcms_c_isr(struct brcms_c_info *wlc); -extern bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); -extern bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, - struct sk_buff *sdu, - struct ieee80211_hw *hw); -extern bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); -extern void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, - int val); -extern int brcms_c_get_header_len(void); -extern void brcms_c_set_addrmatch(struct brcms_c_info *wlc, - int match_reg_offset, - const u8 *addr); -extern void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, - const struct ieee80211_tx_queue_params *arg, - bool suspend); -extern struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); -extern void brcms_c_ampdu_flush(struct brcms_c_info *wlc, - struct ieee80211_sta *sta, u16 tid); -extern void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, - u8 ba_wsize, uint max_rx_ampdu_bytes); -extern int brcms_c_module_register(struct brcms_pub *pub, - const char *name, struct brcms_info *hdl, - int (*down_fn)(void *handle)); -extern int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, - struct brcms_info *hdl); -extern void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); -extern void brcms_c_enable_mac(struct brcms_c_info *wlc); -extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); -extern void brcms_c_scan_start(struct brcms_c_info *wlc); -extern void brcms_c_scan_stop(struct brcms_c_info *wlc); -extern int brcms_c_get_curband(struct brcms_c_info *wlc); -extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); -extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); -extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc, +struct brcms_c_info *brcms_c_attach(struct brcms_info *wl, + struct bcma_device *core, uint unit, + bool piomode, uint *perr); +uint brcms_c_detach(struct brcms_c_info *wlc); +int brcms_c_up(struct brcms_c_info *wlc); +uint brcms_c_down(struct brcms_c_info *wlc); + +bool brcms_c_chipmatch(struct bcma_device *core); +void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); +void brcms_c_reset(struct brcms_c_info *wlc); + +void brcms_c_intrson(struct brcms_c_info *wlc); +u32 brcms_c_intrsoff(struct brcms_c_info *wlc); +void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); +bool brcms_c_intrsupd(struct brcms_c_info *wlc); +bool brcms_c_isr(struct brcms_c_info *wlc); +bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); +bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, + struct ieee80211_hw *hw); +bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); +void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val); +int brcms_c_get_header_len(void); +void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, + const u8 *addr); +void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, + const struct ieee80211_tx_queue_params *arg, + bool suspend); +struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); +void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, + u16 tid); +void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, + u8 ba_wsize, uint max_rx_ampdu_bytes); +int brcms_c_module_register(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl, + int (*down_fn)(void *handle)); +int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl); +void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); +void brcms_c_enable_mac(struct brcms_c_info *wlc); +void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); +void brcms_c_scan_start(struct brcms_c_info *wlc); +void brcms_c_scan_stop(struct brcms_c_info *wlc); +int brcms_c_get_curband(struct brcms_c_info *wlc); +int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); +int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); +void brcms_c_get_current_rateset(struct brcms_c_info *wlc, struct brcm_rateset *currs); -extern int brcms_c_set_rateset(struct brcms_c_info *wlc, - struct brcm_rateset *rs); -extern int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); -extern u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); -extern void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, +int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs); +int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); +u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); +void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override); -extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, - u8 interval); -extern u64 brcms_c_tsf_get(struct brcms_c_info *wlc); -extern void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf); -extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); -extern int brcms_c_get_tx_power(struct brcms_c_info *wlc); -extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); -extern void brcms_c_mute(struct brcms_c_info *wlc, bool on); -extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); -extern void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr); -extern void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, - const u8 *bssid, u8 *ssid, size_t ssid_len); -extern void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr); -extern void brcms_c_update_beacon(struct brcms_c_info *wlc); -extern void brcms_c_set_new_beacon(struct brcms_c_info *wlc, - struct sk_buff *beacon, u16 tim_offset, - u16 dtim_period); -extern void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, - struct sk_buff *probe_resp); -extern void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable); -extern void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, - size_t ssid_len); +void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval); +u64 brcms_c_tsf_get(struct brcms_c_info *wlc); +void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf); +int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); +int brcms_c_get_tx_power(struct brcms_c_info *wlc); +bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); +void brcms_c_mute(struct brcms_c_info *wlc, bool on); +bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); +void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr); +void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, + u8 *ssid, size_t ssid_len); +void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr); +void brcms_c_update_beacon(struct brcms_c_info *wlc); +void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, + u16 tim_offset, u16 dtim_period); +void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, + struct sk_buff *probe_resp); +void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable); +void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len); #endif /* _BRCM_PUB_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/brcm80211/brcmsmac/rate.h index 980d578825c..5bb88b78ed6 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/rate.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/rate.h @@ -216,34 +216,30 @@ static inline u8 cck_phy2mac_rate(u8 signal) /* sanitize, and sort a rateset with the basic bit(s) preserved, validate * rateset */ -extern bool -brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, - const struct brcms_c_rateset *hw_rs, - bool check_brate, u8 txstreams); +bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, + const struct brcms_c_rateset *hw_rs, + bool check_brate, u8 txstreams); /* copy rateset src to dst as-is (no masking or sorting) */ -extern void brcms_c_rateset_copy(const struct brcms_c_rateset *src, - struct brcms_c_rateset *dst); +void brcms_c_rateset_copy(const struct brcms_c_rateset *src, + struct brcms_c_rateset *dst); /* would be nice to have these documented ... */ -extern u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); - -extern void brcms_c_rateset_filter(struct brcms_c_rateset *src, - struct brcms_c_rateset *dst, bool basic_only, u8 rates, uint xmask, - bool mcsallow); - -extern void -brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, - const struct brcms_c_rateset *rs_hw, uint phy_type, - int bandtype, bool cck_only, uint rate_mask, - bool mcsallow, u8 bw, u8 txstreams); - -extern s16 brcms_c_rate_legacy_phyctl(uint rate); - -extern void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); -extern void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); -extern void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, - u8 txstreams); -extern void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, - u8 bw); +u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); + +void brcms_c_rateset_filter(struct brcms_c_rateset *src, + struct brcms_c_rateset *dst, bool basic_only, + u8 rates, uint xmask, bool mcsallow); + +void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, + const struct brcms_c_rateset *rs_hw, uint phy_type, + int bandtype, bool cck_only, uint rate_mask, + bool mcsallow, u8 bw, u8 txstreams); + +s16 brcms_c_rate_legacy_phyctl(uint rate); + +void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); +void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); +void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams); +void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw); #endif /* _BRCM_RATE_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/brcm80211/brcmsmac/stf.h index 19f6580f69b..ba9493009a3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/stf.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.h @@ -19,24 +19,19 @@ #include "types.h" -extern int brcms_c_stf_attach(struct brcms_c_info *wlc); -extern void brcms_c_stf_detach(struct brcms_c_info *wlc); +int brcms_c_stf_attach(struct brcms_c_info *wlc); +void brcms_c_stf_detach(struct brcms_c_info *wlc); -extern void brcms_c_tempsense_upd(struct brcms_c_info *wlc); -extern void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, - u16 *ss_algo_channel, - u16 chanspec); -extern int brcms_c_stf_ss_update(struct brcms_c_info *wlc, - struct brcms_band *band); -extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); -extern int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, - bool force); -extern bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); -extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); -extern void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); -extern u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, - u32 rspec); -extern u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, - u32 rspec); +void brcms_c_tempsense_upd(struct brcms_c_info *wlc); +void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, + u16 *ss_algo_channel, u16 chanspec); +int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band); +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); +int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force); +bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); +void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); +u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec); +u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec); #endif /* _BRCM_STF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h index 18750a814b4..c87dd89bcb7 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h @@ -43,16 +43,14 @@ struct brcms_ucode { u32 *bcm43xx_bomminor; }; -extern int -brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); +int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); -extern void brcms_ucode_data_free(struct brcms_ucode *ucode); +void brcms_ucode_data_free(struct brcms_ucode *ucode); -extern int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, - unsigned int idx); -extern int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, - unsigned int idx); -extern void brcms_ucode_free_buf(void *); -extern int brcms_check_firmwares(struct brcms_info *wl); +int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, unsigned int idx); +int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, + unsigned int idx); +void brcms_ucode_free_buf(void *); +int brcms_check_firmwares(struct brcms_info *wl); #endif /* _BRCM_UCODE_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmutil/d11.c b/drivers/net/wireless/brcm80211/brcmutil/d11.c index 30e54e2c6c9..2b2522bdd8e 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/d11.c +++ b/drivers/net/wireless/brcm80211/brcmutil/d11.c @@ -21,19 +21,46 @@ #include <brcmu_wifi.h> #include <brcmu_d11.h> -static void brcmu_d11n_encchspec(struct brcmu_chan *ch) +static u16 d11n_sb(enum brcmu_chan_sb sb) { - ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK; + switch (sb) { + case BRCMU_CHAN_SB_NONE: + return BRCMU_CHSPEC_D11N_SB_N; + case BRCMU_CHAN_SB_L: + return BRCMU_CHSPEC_D11N_SB_L; + case BRCMU_CHAN_SB_U: + return BRCMU_CHSPEC_D11N_SB_U; + default: + WARN_ON(1); + } + return 0; +} - switch (ch->bw) { +static u16 d11n_bw(enum brcmu_chan_bw bw) +{ + switch (bw) { case BRCMU_CHAN_BW_20: - ch->chspec |= BRCMU_CHSPEC_D11N_BW_20 | BRCMU_CHSPEC_D11N_SB_N; - break; + return BRCMU_CHSPEC_D11N_BW_20; case BRCMU_CHAN_BW_40: + return BRCMU_CHSPEC_D11N_BW_40; default: - WARN_ON_ONCE(1); - break; + WARN_ON(1); } + return 0; +} + +static void brcmu_d11n_encchspec(struct brcmu_chan *ch) +{ + if (ch->bw == BRCMU_CHAN_BW_20) + ch->sb = BRCMU_CHAN_SB_NONE; + + ch->chspec = 0; + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, + BRCMU_CHSPEC_CH_SHIFT, ch->chnum); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK, + 0, d11n_sb(ch->sb)); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK, + 0, d11n_bw(ch->bw)); if (ch->chnum <= CH_MAX_2G_CHANNEL) ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G; @@ -41,23 +68,34 @@ static void brcmu_d11n_encchspec(struct brcmu_chan *ch) ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G; } -static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) +static u16 d11ac_bw(enum brcmu_chan_bw bw) { - ch->chspec = ch->chnum & BRCMU_CHSPEC_CH_MASK; - - switch (ch->bw) { + switch (bw) { case BRCMU_CHAN_BW_20: - ch->chspec |= BRCMU_CHSPEC_D11AC_BW_20; - break; + return BRCMU_CHSPEC_D11AC_BW_20; case BRCMU_CHAN_BW_40: + return BRCMU_CHSPEC_D11AC_BW_40; case BRCMU_CHAN_BW_80: - case BRCMU_CHAN_BW_80P80: - case BRCMU_CHAN_BW_160: + return BRCMU_CHSPEC_D11AC_BW_80; default: - WARN_ON_ONCE(1); - break; + WARN_ON(1); } + return 0; +} +static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) +{ + if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE) + ch->sb = BRCMU_CHAN_SB_L; + + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK, + BRCMU_CHSPEC_CH_SHIFT, ch->chnum); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb); + brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK, + 0, d11ac_bw(ch->bw)); + + ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK; if (ch->chnum <= CH_MAX_2G_CHANNEL) ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; else @@ -73,6 +111,7 @@ static void brcmu_d11n_decchspec(struct brcmu_chan *ch) switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) { case BRCMU_CHSPEC_D11N_BW_20: ch->bw = BRCMU_CHAN_BW_20; + ch->sb = BRCMU_CHAN_SB_NONE; break; case BRCMU_CHSPEC_D11N_BW_40: ch->bw = BRCMU_CHAN_BW_40; @@ -112,6 +151,7 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) { case BRCMU_CHSPEC_D11AC_BW_20: ch->bw = BRCMU_CHAN_BW_20; + ch->sb = BRCMU_CHAN_SB_NONE; break; case BRCMU_CHSPEC_D11AC_BW_40: ch->bw = BRCMU_CHAN_BW_40; @@ -128,6 +168,25 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) break; case BRCMU_CHSPEC_D11AC_BW_80: ch->bw = BRCMU_CHAN_BW_80; + ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK, + BRCMU_CHSPEC_D11AC_SB_SHIFT); + switch (ch->sb) { + case BRCMU_CHAN_SB_LL: + ch->chnum -= CH_30MHZ_APART; + break; + case BRCMU_CHAN_SB_LU: + ch->chnum -= CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_UL: + ch->chnum += CH_10MHZ_APART; + break; + case BRCMU_CHAN_SB_UU: + ch->chnum += CH_30MHZ_APART; + break; + default: + WARN_ON_ONCE(1); + break; + } break; case BRCMU_CHSPEC_D11AC_BW_8080: case BRCMU_CHSPEC_D11AC_BW_160: diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index c1fe245bb07..d816270db3b 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -41,5 +41,8 @@ #define BCM4331_CHIP_ID 0x4331 #define BCM4334_CHIP_ID 0x4334 #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/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/brcm80211/include/brcmu_d11.h index 92623f02b1c..f9745ea8b3e 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_d11.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_d11.h @@ -108,13 +108,7 @@ enum brcmu_chan_bw { }; enum brcmu_chan_sb { - BRCMU_CHAN_SB_NONE = 0, - BRCMU_CHAN_SB_L, - BRCMU_CHAN_SB_U, - BRCMU_CHAN_SB_LL, - BRCMU_CHAN_SB_LU, - BRCMU_CHAN_SB_UL, - BRCMU_CHAN_SB_UU, + BRCMU_CHAN_SB_NONE = -1, BRCMU_CHAN_SB_LLL, BRCMU_CHAN_SB_LLU, BRCMU_CHAN_SB_LUL, @@ -123,6 +117,12 @@ enum brcmu_chan_sb { BRCMU_CHAN_SB_ULU, BRCMU_CHAN_SB_UUL, BRCMU_CHAN_SB_UUU, + BRCMU_CHAN_SB_L = BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_U = BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_LL = BRCMU_CHAN_SB_LLL, + BRCMU_CHAN_SB_LU = BRCMU_CHAN_SB_LLU, + BRCMU_CHAN_SB_UL = BRCMU_CHAN_SB_LUL, + BRCMU_CHAN_SB_UU = BRCMU_CHAN_SB_LUU, }; struct brcmu_chan { @@ -140,6 +140,6 @@ struct brcmu_d11inf { void (*decchspec)(struct brcmu_chan *ch); }; -extern void brcmu_d11_attach(struct brcmu_d11inf *d11inf); +void brcmu_d11_attach(struct brcmu_d11inf *d11inf); #endif /* _BRCMU_CHANNELS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h index 898cacb8d01..8ba445b3fd7 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h @@ -114,31 +114,29 @@ static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec) return skb_peek_tail(&pq->q[prec].skblist); } -extern struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, - struct sk_buff *p); -extern struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, - struct sk_buff *p); -extern struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); -extern struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); -extern struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, - bool (*match_fn)(struct sk_buff *p, - void *arg), - void *arg); +struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p); +struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, + struct sk_buff *p); +struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); +struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); +struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, + bool (*match_fn)(struct sk_buff *p, + void *arg), + void *arg); /* packet primitives */ -extern struct sk_buff *brcmu_pkt_buf_get_skb(uint len); -extern void brcmu_pkt_buf_free_skb(struct sk_buff *skb); +struct sk_buff *brcmu_pkt_buf_get_skb(uint len); +void brcmu_pkt_buf_free_skb(struct sk_buff *skb); /* Empty the queue at particular precedence level */ /* callback function fn(pkt, arg) returns true if pkt belongs to if */ -extern void brcmu_pktq_pflush(struct pktq *pq, int prec, - bool dir, bool (*fn)(struct sk_buff *, void *), void *arg); +void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg); /* operations on a set of precedences in packet queue */ -extern int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); -extern struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, - int *prec_out); +int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); +struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); /* operations on packet queue as a whole */ @@ -167,11 +165,11 @@ static inline bool pktq_empty(struct pktq *pq) return pq->len == 0; } -extern void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); +void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); /* prec_out may be NULL if caller is not interested in return value */ -extern struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); -extern void brcmu_pktq_flush(struct pktq *pq, bool dir, - bool (*fn)(struct sk_buff *, void *), void *arg); +struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); +void brcmu_pktq_flush(struct pktq *pq, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg); /* externs */ /* ip address */ @@ -204,13 +202,13 @@ static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift) /* externs */ /* format/print */ #ifdef DEBUG -extern void brcmu_prpkt(const char *msg, struct sk_buff *p0); +void brcmu_prpkt(const char *msg, struct sk_buff *p0); #else #define brcmu_prpkt(a, b) #endif /* DEBUG */ #ifdef DEBUG -extern __printf(3, 4) +__printf(3, 4) void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...); #else __printf(3, 4) diff --git a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h index 0505cc065e0..76b5d3a8629 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_wifi.h @@ -29,6 +29,7 @@ #define CH_UPPER_SB 0x01 #define CH_LOWER_SB 0x02 #define CH_EWA_VALID 0x04 +#define CH_30MHZ_APART 6 #define CH_20MHZ_APART 4 #define CH_10MHZ_APART 2 #define CH_5MHZ_APART 1 /* 2G band channels are 5 Mhz apart */ @@ -82,6 +83,20 @@ #define WLC_N_BW_40ALL 1 #define WLC_N_BW_20IN2G_40IN5G 2 +#define WLC_BW_20MHZ_BIT BIT(0) +#define WLC_BW_40MHZ_BIT BIT(1) +#define WLC_BW_80MHZ_BIT BIT(2) +#define WLC_BW_160MHZ_BIT BIT(3) + +/* Bandwidth capabilities */ +#define WLC_BW_CAP_20MHZ (WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_40MHZ (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_80MHZ (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \ + WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_160MHZ (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \ + WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT) +#define WLC_BW_CAP_UNRESTRICTED 0xFF + /* band types */ #define WLC_BAND_AUTO 0 /* auto-select */ #define WLC_BAND_5G 1 /* 5 Ghz */ @@ -203,6 +218,9 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WSEC_SWFLAG 0x0008 /* to go into transition mode without setting wep */ #define SES_OW_ENABLED 0x0040 +/* MFP */ +#define MFP_CAPABLE 0x0200 +#define MFP_REQUIRED 0x0400 /* WPA authentication mode bitvec */ #define WPA_AUTH_DISABLED 0x0000 /* Legacy (i.e., non-WPA) */ diff --git a/drivers/net/wireless/cw1200/cw1200_sdio.c b/drivers/net/wireless/cw1200/cw1200_sdio.c index ebdcdf44f15..d3acc85932a 100644 --- a/drivers/net/wireless/cw1200/cw1200_sdio.c +++ b/drivers/net/wireless/cw1200/cw1200_sdio.c @@ -108,9 +108,9 @@ static irqreturn_t cw1200_gpio_irq(int irq, void *dev_id) struct hwbus_priv *self = dev_id; if (self->core) { - sdio_claim_host(self->func); + cw1200_sdio_lock(self); cw1200_irq_handler(self->core); - sdio_release_host(self->func); + cw1200_sdio_unlock(self); return IRQ_HANDLED; } else { return IRQ_NONE; diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index 755a0c8edfe..40078f5f932 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -365,7 +365,7 @@ static struct hwbus_ops cw1200_spi_hwbus_ops = { static int cw1200_spi_probe(struct spi_device *func) { const struct cw1200_platform_data_spi *plat_data = - func->dev.platform_data; + dev_get_platdata(&func->dev); struct hwbus_priv *self; int status; @@ -443,7 +443,7 @@ static int cw1200_spi_disconnect(struct spi_device *func) } kfree(self); } - cw1200_spi_off(func->dev.platform_data); + cw1200_spi_off(dev_get_platdata(&func->dev)); return 0; } diff --git a/drivers/net/wireless/cw1200/debug.c b/drivers/net/wireless/cw1200/debug.c index e323b4d5433..34f97c31eec 100644 --- a/drivers/net/wireless/cw1200/debug.c +++ b/drivers/net/wireless/cw1200/debug.c @@ -41,6 +41,8 @@ static const char * const cw1200_debug_link_id[] = { "REQ", "SOFT", "HARD", + "RESET", + "RESET_REMAP", }; static const char *cw1200_debug_mode(int mode) diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c index acdff0f7f95..e23d67e0bfe 100644 --- a/drivers/net/wireless/cw1200/fwio.c +++ b/drivers/net/wireless/cw1200/fwio.c @@ -14,7 +14,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/vmalloc.h> #include <linux/sched.h> #include <linux/firmware.h> @@ -203,8 +202,8 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) } /* calculate the block size */ - tx_size = block_size = min((size_t)(firmware->size - put), - (size_t)DOWNLOAD_BLOCK_SIZE); + tx_size = block_size = min_t(size_t, firmware->size - put, + DOWNLOAD_BLOCK_SIZE); memcpy(buf, &firmware->data[put], block_size); if (block_size < DOWNLOAD_BLOCK_SIZE) { diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 090f01577dd..3e78cc3ccb7 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/firmware.h> #include <linux/etherdevice.h> #include <linux/vmalloc.h> @@ -302,7 +301,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; - hw->channel_change_time = 1000; /* TODO: find actual value */ hw->queues = 4; priv->rts_threshold = -1; diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c index b37abb9f045..6907c8fd457 100644 --- a/drivers/net/wireless/cw1200/pm.c +++ b/drivers/net/wireless/cw1200/pm.c @@ -225,7 +225,7 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) cw1200_set_pm(priv, &priv->powersave_mode); if (wait_event_interruptible_timeout(priv->ps_mode_switch_done, !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) { - goto revert3; + goto revert4; } } @@ -254,11 +254,11 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) /* Stop serving thread */ if (cw1200_bh_suspend(priv)) - goto revert4; + goto revert5; ret = timer_pending(&priv->mcast_timeout); if (ret) - goto revert5; + goto revert6; /* Store suspend state */ pm_state->suspend_state = state; @@ -280,9 +280,9 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return 0; -revert5: +revert6: WARN_ON(cw1200_bh_resume(priv)); -revert4: +revert5: cw1200_resume_work(priv, &priv->bss_loss_work, state->bss_loss_tmo); cw1200_resume_work(priv, &priv->join_timeout, @@ -291,6 +291,7 @@ revert4: state->direct_probe); cw1200_resume_work(priv, &priv->link_id_gc_work, state->link_id_gc); +revert4: kfree(state); revert3: wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off); diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index ee3c19037aa..9afcd4ce336 100644 --- a/drivers/net/wireless/cw1200/scan.c +++ b/drivers/net/wireless/cw1200/scan.c @@ -173,8 +173,9 @@ void cw1200_scan_work(struct work_struct *work) cw1200_set_pm(priv, &priv->powersave_mode); if (priv->scan.status < 0) - wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan failed (%d).\n", - priv->scan.status); + wiphy_warn(priv->hw->wiphy, + "[SCAN] Scan failed (%d).\n", + priv->scan.status); else if (priv->scan.req) wiphy_dbg(priv->hw->wiphy, "[SCAN] Scan completed.\n"); @@ -197,9 +198,9 @@ void cw1200_scan_work(struct work_struct *work) if ((*it)->band != first->band) break; if (((*it)->flags ^ first->flags) & - IEEE80211_CHAN_PASSIVE_SCAN) + IEEE80211_CHAN_NO_IR) break; - if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + if (!(first->flags & IEEE80211_CHAN_NO_IR) && (*it)->max_power != first->max_power) break; } @@ -210,7 +211,7 @@ void cw1200_scan_work(struct work_struct *work) else scan.max_tx_rate = WSM_TRANSMIT_RATE_1; scan.num_probes = - (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; + (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; scan.num_ssids = priv->scan.n_ssids; scan.ssids = &priv->scan.ssids[0]; scan.num_channels = it - priv->scan.curr; @@ -233,7 +234,7 @@ void cw1200_scan_work(struct work_struct *work) } for (i = 0; i < scan.num_channels; ++i) { scan.ch[i].number = priv->scan.curr[i]->hw_value; - if (priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + if (priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { scan.ch[i].min_chan_time = 50; scan.ch[i].max_chan_time = 100; } else { @@ -241,7 +242,7 @@ void cw1200_scan_work(struct work_struct *work) scan.ch[i].max_chan_time = 25; } } - if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + if (!(first->flags & IEEE80211_CHAN_NO_IR) && priv->scan.output_power != first->max_power) { priv->scan.output_power = first->max_power; wsm_set_output_power(priv, diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 010b252be58..cd0cad7f775 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -13,6 +13,7 @@ #include <linux/sched.h> #include <linux/firmware.h> #include <linux/module.h> +#include <linux/etherdevice.h> #include "cw1200.h" #include "sta.h" @@ -555,8 +556,8 @@ u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, pr_debug("[STA] multicast: %pM\n", ha->addr); memcpy(&priv->multicast_filter.macaddrs[count], ha->addr, ETH_ALEN); - if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && - memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) + if (!ether_addr_equal(ha->addr, broadcast_ipv4) && + !ether_addr_equal(ha->addr, broadcast_ipv6)) priv->has_multicast_subscription = true; count++; } @@ -935,7 +936,8 @@ static int __cw1200_flush(struct cw1200_common *priv, bool drop) return ret; } -void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct cw1200_common *priv = hw->priv; diff --git a/drivers/net/wireless/cw1200/sta.h b/drivers/net/wireless/cw1200/sta.h index 35babb62cc6..b7e386b7662 100644 --- a/drivers/net/wireless/cw1200/sta.h +++ b/drivers/net/wireless/cw1200/sta.h @@ -40,7 +40,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value); -void cw1200_flush(struct ieee80211_hw *hw, u32 queues, bool drop); +void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); u64 cw1200_prepare_multicast(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list); diff --git a/drivers/net/wireless/cw1200/txrx.c b/drivers/net/wireless/cw1200/txrx.c index e824d4d4a18..0bd541175ec 100644 --- a/drivers/net/wireless/cw1200/txrx.c +++ b/drivers/net/wireless/cw1200/txrx.c @@ -1166,8 +1166,7 @@ void cw1200_rx_cb(struct cw1200_common *priv, return; } else if (ieee80211_is_beacon(frame->frame_control) && !arg->status && priv->vif && - !memcmp(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid, - ETH_ALEN)) { + ether_addr_equal(ieee80211_get_SA(frame), priv->vif->bss_conf.bssid)) { const u8 *tim_ie; u8 *ies = ((struct ieee80211_mgmt *) (skb->data))->u.beacon.variable; diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index d39e3e24077..599f30f2284 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -563,7 +563,7 @@ hostap_rx_frame_wds(local_info_t *local, struct ieee80211_hdr *hdr, u16 fc, /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS) * or own non-standard frame with 4th address after payload */ - if (memcmp(hdr->addr1, local->dev->dev_addr, ETH_ALEN) != 0 && + if (!ether_addr_equal(hdr->addr1, local->dev->dev_addr) && (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff || hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { @@ -622,12 +622,12 @@ static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb) /* check that the frame is unicast frame to us */ if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_TODS && - memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && - memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + ether_addr_equal(hdr->addr1, dev->dev_addr) && + ether_addr_equal(hdr->addr3, dev->dev_addr)) { /* ToDS frame with own addr BSSID and DA */ } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS && - memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + ether_addr_equal(hdr->addr1, dev->dev_addr)) { /* FromDS frame with own addr as DA */ } else return 0; diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c index 344a981a052..8bde7768946 100644 --- a/drivers/net/wireless/hostap/hostap_80211_tx.c +++ b/drivers/net/wireless/hostap/hostap_80211_tx.c @@ -1,5 +1,6 @@ #include <linux/slab.h> #include <linux/export.h> +#include <linux/etherdevice.h> #include "hostap_80211.h" #include "hostap_common.h" @@ -103,8 +104,7 @@ netdev_tx_t hostap_data_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } else if (local->iw_mode == IW_MODE_INFRA && (local->wds_type & HOSTAP_WDS_AP_CLIENT) && - memcmp(skb->data + ETH_ALEN, dev->dev_addr, - ETH_ALEN) != 0) { + !ether_addr_equal(skb->data + ETH_ALEN, dev->dev_addr)) { /* AP client mode: send frames with foreign src addr * using 4-addr WDS frames */ use_wds = WDS_COMPLIANT_FRAME; diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index d6033a8e5de..596525528f5 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/export.h> #include <linux/moduleparam.h> +#include <linux/etherdevice.h> #include "hostap_wlan.h" #include "hostap.h" @@ -106,13 +107,12 @@ static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta) s = ap->sta_hash[STA_HASH(sta->addr)]; if (s == NULL) return; - if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { + if (ether_addr_equal(s->addr, sta->addr)) { ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; return; } - while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) - != 0) + while (s->hnext != NULL && !ether_addr_equal(s->hnext->addr, sta->addr)) s = s->hnext; if (s->hnext != NULL) s->hnext = s->hnext->hnext; @@ -147,7 +147,7 @@ static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) if (!sta->ap && sta->u.sta.challenge) kfree(sta->u.sta.challenge); - del_timer(&sta->timer); + del_timer_sync(&sta->timer); #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ kfree(sta); @@ -435,7 +435,7 @@ int ap_control_del_mac(struct mac_restrictions *mac_restrictions, u8 *mac) ptr != &mac_restrictions->mac_list; ptr = ptr->next) { entry = list_entry(ptr, struct mac_entry, list); - if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + if (ether_addr_equal(entry->addr, mac)) { list_del(ptr); kfree(entry); mac_restrictions->entries--; @@ -459,7 +459,7 @@ static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, spin_lock_bh(&mac_restrictions->lock); list_for_each_entry(entry, &mac_restrictions->mac_list, list) { - if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + if (ether_addr_equal(entry->addr, mac)) { found = 1; break; } @@ -957,7 +957,7 @@ static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta) struct sta_info *s; s = ap->sta_hash[STA_HASH(sta)]; - while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0) + while (s != NULL && !ether_addr_equal(s->addr, sta)) s = s->hnext; return s; } @@ -1391,7 +1391,7 @@ static void handle_authen(local_info_t *local, struct sk_buff *skb, status_code = __le16_to_cpu(*pos); pos++; - if (memcmp(dev->dev_addr, hdr->addr2, ETH_ALEN) == 0 || + if (ether_addr_equal(dev->dev_addr, hdr->addr2) || ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) { txt = "authentication denied"; resp = WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1935,7 +1935,7 @@ static void handle_pspoll(local_info_t *local, PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=%pM, TA=%pM PWRMGT=%d\n", hdr->addr1, hdr->addr2, !!ieee80211_has_pm(hdr->frame_control)); - if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=%pM not own MAC\n", hdr->addr1); @@ -2230,7 +2230,7 @@ static void handle_ap_item(local_info_t *local, struct sk_buff *skb, goto done; } - if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=%pM" " not own MAC\n", hdr->addr1); goto done; @@ -2267,13 +2267,13 @@ static void handle_ap_item(local_info_t *local, struct sk_buff *skb, goto done; } - if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + if (!ether_addr_equal(hdr->addr1, dev->dev_addr)) { PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=%pM" " not own MAC\n", hdr->addr1); goto done; } - if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) { + if (!ether_addr_equal(hdr->addr3, dev->dev_addr)) { PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=%pM" " not own MAC\n", hdr->addr3); goto done; @@ -3035,7 +3035,7 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, if (!wds) { /* FromDS frame - not for us; probably * broadcast/multicast in another BSS - drop */ - if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + if (ether_addr_equal(hdr->addr1, dev->dev_addr)) { printk(KERN_DEBUG "Odd.. FromDS packet " "received with own BSSID\n"); hostap_dump_rx_80211(dev->name, skb, rx_stats); @@ -3044,7 +3044,7 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, goto out; } } else if (stype == IEEE80211_STYPE_NULLFUNC && sta == NULL && - memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + ether_addr_equal(hdr->addr1, dev->dev_addr)) { if (local->hostapd) { prism2_rx_80211(local->apdev, skb, rx_stats, @@ -3073,7 +3073,7 @@ ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, /* If BSSID (Addr3) is foreign, this frame is a normal * broadcast frame from an IBSS network. Drop it silently. * If BSSID is own, report the dropping of this frame. */ - if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + if (ether_addr_equal(hdr->addr3, dev->dev_addr)) { printk(KERN_DEBUG "%s: dropped received packet from %pM" " with no ToDS flag " "(type=0x%02x, subtype=0x%02x)\n", dev->name, diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 56cd01ca8ad..b6ec51923b2 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -1,7 +1,6 @@ #define PRISM2_PCCARD #include <linux/module.h> -#include <linux/init.h> #include <linux/if.h> #include <linux/slab.h> #include <linux/wait.h> @@ -678,6 +677,8 @@ static const struct pcmcia_device_id hostap_cs_ids[] = { PCMCIA_DEVICE_PROD_ID12( "ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee), + PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", + 0xa37434e9, 0x9762e8f1), PCMCIA_DEVICE_PROD_ID123( "Pretec", "CompactWLAN Card 802.11b", "2.5", 0x1cadd3e5, 0xe697636c, 0x7a5bfcf1), diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index c275dc1623f..6df3ee561d5 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -2175,7 +2175,7 @@ static void hostap_tx_callback(local_info_t *local, struct hostap_tx_callback_info *cb; /* Make sure that frame was from us. */ - if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) { + if (!ether_addr_equal(txdesc->addr2, local->dev->dev_addr)) { printk(KERN_DEBUG "%s: TX callback - foreign frame\n", local->dev->name); return; diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c index 970a48baaf8..de7c4ffec30 100644 --- a/drivers/net/wireless/hostap/hostap_info.c +++ b/drivers/net/wireless/hostap/hostap_info.c @@ -217,7 +217,7 @@ static void prism2_host_roaming(local_info_t *local) } } - memcpy(req.bssid, selected->bssid, 6); + memcpy(req.bssid, selected->bssid, ETH_ALEN); req.channel = selected->chid; spin_unlock_irqrestore(&local->lock, flags); diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c index e5090309824..3e5fa7872b6 100644 --- a/drivers/net/wireless/hostap/hostap_ioctl.c +++ b/drivers/net/wireless/hostap/hostap_ioctl.c @@ -655,7 +655,7 @@ static int hostap_join_ap(struct net_device *dev) if (!local->last_scan_results) break; entry = &local->last_scan_results[i]; - if (memcmp(local->preferred_ap, entry->bssid, ETH_ALEN) == 0) { + if (ether_addr_equal(local->preferred_ap, entry->bssid)) { req.channel = entry->chid; break; } @@ -1978,7 +1978,7 @@ static inline int prism2_translate_scan(local_info_t *local, list_for_each(ptr, &local->bss_list) { struct hostap_bss_info *bss; bss = list_entry(ptr, struct hostap_bss_info, list); - if (memcmp(bss->bssid, scan->bssid, ETH_ALEN) == 0) { + if (ether_addr_equal(bss->bssid, scan->bssid)) { bss->included = 1; current_ev = __prism2_translate_scan( local, info, scan, bss, current_ev, @@ -2567,7 +2567,7 @@ static int prism2_ioctl_priv_prism2_param(struct net_device *dev, local->passive_scan_interval = value; if (timer_pending(&local->passive_scan_timer)) del_timer(&local->passive_scan_timer); - if (value > 0) { + if (value > 0 && value < INT_MAX / HZ) { local->passive_scan_timer.expires = jiffies + local->passive_scan_interval * HZ; add_timer(&local->passive_scan_timer); diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index a1257c92afc..52919ad4272 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -155,8 +155,7 @@ int prism2_wds_add(local_info_t *local, u8 *remote_addr, if (prism2_wds_special_addr(iface->u.wds.remote_addr)) empty = iface; - else if (memcmp(iface->u.wds.remote_addr, remote_addr, - ETH_ALEN) == 0) { + else if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { match = iface; break; } @@ -214,8 +213,7 @@ int prism2_wds_del(local_info_t *local, u8 *remote_addr, if (iface->type != HOSTAP_INTERFACE_WDS) continue; - if (memcmp(iface->u.wds.remote_addr, remote_addr, - ETH_ALEN) == 0) { + if (ether_addr_equal(iface->u.wds.remote_addr, remote_addr)) { selected = iface; break; } @@ -884,7 +882,7 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local, dev->mtu = local->mtu; - SET_ETHTOOL_OPS(dev, &prism2_ethtool_ops); + dev->ethtool_ops = &prism2_ethtool_ops; } @@ -1085,7 +1083,7 @@ int prism2_sta_deauth(local_info_t *local, u16 reason) if (local->iw_mode != IW_MODE_INFRA || is_zero_ether_addr(local->bssid) || - memcmp(local->bssid, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0) + ether_addr_equal(local->bssid, "\x44\x44\x44\x44\x44\x44")) return 0; ret = prism2_sta_send_mgmt(local, local->bssid, IEEE80211_STYPE_DEAUTH, diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c index 05ca3402dca..91158e2e961 100644 --- a/drivers/net/wireless/hostap/hostap_pci.c +++ b/drivers/net/wireless/hostap/hostap_pci.c @@ -5,7 +5,6 @@ * Andy Warner <andyw@pobox.com> */ #include <linux/module.h> -#include <linux/init.h> #include <linux/if.h> #include <linux/skbuff.h> #include <linux/netdevice.h> diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c index c3d067ee4db..3bf530d9a40 100644 --- a/drivers/net/wireless/hostap/hostap_plx.c +++ b/drivers/net/wireless/hostap/hostap_plx.c @@ -8,7 +8,6 @@ #include <linux/module.h> -#include <linux/init.h> #include <linux/if.h> #include <linux/skbuff.h> #include <linux/netdevice.h> diff --git a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c index aa7ad3a7a69..4e5c0f8c949 100644 --- a/drivers/net/wireless/hostap/hostap_proc.c +++ b/drivers/net/wireless/hostap/hostap_proc.c @@ -496,7 +496,7 @@ void hostap_init_proc(local_info_t *local) void hostap_remove_proc(local_info_t *local) { - remove_proc_subtree(local->ddev->name, hostap_proc); + proc_remove(local->proc); } diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index f8ab193009c..dfc6dfc56d5 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -1930,10 +1930,10 @@ static int ipw2100_wdev_init(struct net_device *dev) bg_band->channels[i].max_power = geo->bg[i].max_power; if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) bg_band->channels[i].flags |= - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR; if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IBSS; + IEEE80211_CHAN_NO_IR; if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) bg_band->channels[i].flags |= IEEE80211_CHAN_RADAR; @@ -6362,7 +6362,6 @@ out: &ipw2100_attribute_group); free_libipw(dev, 0); - pci_set_drvdata(pci_dev, NULL); } pci_iounmap(pci_dev, ioaddr); @@ -7066,7 +7065,7 @@ static int ipw2100_wx_set_nick(struct net_device *dev, if (wrqu->data.length > IW_ESSID_MAX_SIZE) return -E2BIG; - wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick)); + wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick)); memset(priv->nick, 0, sizeof(priv->nick)); memcpy(priv->nick, extra, wrqu->data.length); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 6b823a1ab78..c5aa404069f 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -2698,7 +2698,7 @@ static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr) /* data's copy of the eeprom data */ static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) { - memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6); + memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN); } static void ipw_read_eeprom(struct ipw_priv *priv) @@ -3012,7 +3012,7 @@ static void ipw_remove_current_network(struct ipw_priv *priv) spin_lock_irqsave(&priv->ieee->lock, flags); list_for_each_safe(element, safe, &priv->ieee->network_list) { network = list_entry(element, struct libipw_network, list); - if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) { + if (ether_addr_equal(network->bssid, priv->bssid)) { list_del(element); list_add_tail(&network->list, &priv->ieee->network_free_list); @@ -3921,7 +3921,7 @@ static u8 ipw_add_station(struct ipw_priv *priv, u8 * bssid) int i; for (i = 0; i < priv->num_stations; i++) { - if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) { + if (ether_addr_equal(priv->stations[i], bssid)) { /* Another node is active in network */ priv->missed_adhoc_beacons = 0; if (!(priv->config & CFG_STATIC_CHANNEL)) @@ -3953,7 +3953,7 @@ static u8 ipw_find_station(struct ipw_priv *priv, u8 * bssid) int i; for (i = 0; i < priv->num_stations; i++) - if (!memcmp(priv->stations[i], bssid, ETH_ALEN)) + if (ether_addr_equal(priv->stations[i], bssid)) return i; return IPW_INVALID_STATION; @@ -5622,7 +5622,7 @@ static int ipw_find_adhoc_network(struct ipw_priv *priv, return 0; } - if (!memcmp(network->bssid, priv->bssid, ETH_ALEN)) { + if (ether_addr_equal(network->bssid, priv->bssid)) { IPW_DEBUG_MERGE("Network '%s (%pM)' excluded " "because of the same BSSID match: %pM" ".\n", print_ssid(ssid, network->ssid, @@ -5849,7 +5849,7 @@ static int ipw_best_network(struct ipw_priv *priv, } if ((priv->config & CFG_STATIC_BSSID) && - memcmp(network->bssid, priv->bssid, ETH_ALEN)) { + !ether_addr_equal(network->bssid, priv->bssid)) { IPW_DEBUG_ASSOC("Network '%s (%pM)' excluded " "because of BSSID mismatch: %pM.\n", print_ssid(ssid, network->ssid, @@ -6988,7 +6988,7 @@ static int ipw_qos_handle_probe_response(struct ipw_priv *priv, } if ((priv->status & STATUS_ASSOCIATED) && (priv->ieee->iw_mode == IW_MODE_ADHOC) && (active_network == 0)) { - if (memcmp(network->bssid, priv->bssid, ETH_ALEN)) + if (!ether_addr_equal(network->bssid, priv->bssid)) if (network->capability & WLAN_CAPABILITY_IBSS) if ((network->ssid_len == priv->assoc_network->ssid_len) && @@ -8210,29 +8210,29 @@ static int is_network_packet(struct ipw_priv *priv, switch (priv->ieee->iw_mode) { case IW_MODE_ADHOC: /* Header: Dest. | Source | BSSID */ /* packets from our adapter are dropped (echo) */ - if (!memcmp(header->addr2, priv->net_dev->dev_addr, ETH_ALEN)) + if (ether_addr_equal(header->addr2, priv->net_dev->dev_addr)) return 0; /* {broad,multi}cast packets to our BSSID go through */ if (is_multicast_ether_addr(header->addr1)) - return !memcmp(header->addr3, priv->bssid, ETH_ALEN); + return ether_addr_equal(header->addr3, priv->bssid); /* packets to our adapter go through */ - return !memcmp(header->addr1, priv->net_dev->dev_addr, - ETH_ALEN); + return ether_addr_equal(header->addr1, + priv->net_dev->dev_addr); case IW_MODE_INFRA: /* Header: Dest. | BSSID | Source */ /* packets from our adapter are dropped (echo) */ - if (!memcmp(header->addr3, priv->net_dev->dev_addr, ETH_ALEN)) + if (ether_addr_equal(header->addr3, priv->net_dev->dev_addr)) return 0; /* {broad,multi}cast packets to our BSS go through */ if (is_multicast_ether_addr(header->addr1)) - return !memcmp(header->addr2, priv->bssid, ETH_ALEN); + return ether_addr_equal(header->addr2, priv->bssid); /* packets to our adapter go through */ - return !memcmp(header->addr1, priv->net_dev->dev_addr, - ETH_ALEN); + return ether_addr_equal(header->addr1, + priv->net_dev->dev_addr); } return 1; @@ -8260,7 +8260,7 @@ static int is_duplicate_packet(struct ipw_priv *priv, list_for_each(p, &priv->ibss_mac_hash[index]) { entry = list_entry(p, struct ipw_ibss_seq, list); - if (!memcmp(entry->mac, mac, ETH_ALEN)) + if (ether_addr_equal(entry->mac, mac)) break; } if (p == &priv->ibss_mac_hash[index]) { @@ -8329,7 +8329,7 @@ static void ipw_handle_mgmt_packet(struct ipw_priv *priv, IEEE80211_STYPE_PROBE_RESP) || (WLAN_FC_GET_STYPE(le16_to_cpu(header->frame_ctl)) == IEEE80211_STYPE_BEACON))) { - if (!memcmp(header->addr3, priv->bssid, ETH_ALEN)) + if (ether_addr_equal(header->addr3, priv->bssid)) ipw_add_station(priv, header->addr2); } @@ -9045,7 +9045,7 @@ static int ipw_wx_set_wap(struct net_device *dev, } priv->config |= CFG_STATIC_BSSID; - if (!memcmp(priv->bssid, wrqu->ap_addr.sa_data, ETH_ALEN)) { + if (ether_addr_equal(priv->bssid, wrqu->ap_addr.sa_data)) { IPW_DEBUG_WX("BSSID set to current BSSID.\n"); mutex_unlock(&priv->mutex); return 0; @@ -9169,7 +9169,7 @@ static int ipw_wx_set_nick(struct net_device *dev, if (wrqu->data.length > IW_ESSID_MAX_SIZE) return -E2BIG; mutex_lock(&priv->mutex); - wrqu->data.length = min((size_t) wrqu->data.length, sizeof(priv->nick)); + wrqu->data.length = min_t(size_t, wrqu->data.length, sizeof(priv->nick)); memset(priv->nick, 0, sizeof(priv->nick)); memcpy(priv->nick, extra, wrqu->data.length); IPW_DEBUG_TRACE("<<\n"); @@ -11472,10 +11472,10 @@ static int ipw_wdev_init(struct net_device *dev) bg_band->channels[i].max_power = geo->bg[i].max_power; if (geo->bg[i].flags & LIBIPW_CH_PASSIVE_ONLY) bg_band->channels[i].flags |= - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR; if (geo->bg[i].flags & LIBIPW_CH_NO_IBSS) bg_band->channels[i].flags |= - IEEE80211_CHAN_NO_IBSS; + IEEE80211_CHAN_NO_IR; if (geo->bg[i].flags & LIBIPW_CH_RADAR_DETECT) bg_band->channels[i].flags |= IEEE80211_CHAN_RADAR; @@ -11511,10 +11511,10 @@ static int ipw_wdev_init(struct net_device *dev) a_band->channels[i].max_power = geo->a[i].max_power; if (geo->a[i].flags & LIBIPW_CH_PASSIVE_ONLY) a_band->channels[i].flags |= - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR; if (geo->a[i].flags & LIBIPW_CH_NO_IBSS) a_band->channels[i].flags |= - IEEE80211_CHAN_NO_IBSS; + IEEE80211_CHAN_NO_IR; if (geo->a[i].flags & LIBIPW_CH_RADAR_DETECT) a_band->channels[i].flags |= IEEE80211_CHAN_RADAR; @@ -11885,7 +11885,6 @@ static int ipw_pci_probe(struct pci_dev *pdev, pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); out_free_libipw: free_libipw(priv->net_dev, 0); out: @@ -11966,7 +11965,6 @@ static void ipw_pci_remove(struct pci_dev *pdev) iounmap(priv->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); /* wiphy_unregister needs to be here, before free_libipw */ wiphy_unregister(priv->ieee->wdev.wiphy); kfree(priv->ieee->a_band.channels); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.h b/drivers/net/wireless/ipw2x00/ipw2200.h index 570d6fb8896..aa301d1eee3 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.h +++ b/drivers/net/wireless/ipw2x00/ipw2200.h @@ -29,7 +29,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/mutex.h> diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h index 6eede52ad8c..5ce2f59d337 100644 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -950,66 +950,55 @@ static inline int libipw_is_cck_rate(u8 rate) } /* libipw.c */ -extern void free_libipw(struct net_device *dev, int monitor); -extern struct net_device *alloc_libipw(int sizeof_priv, int monitor); -extern int libipw_change_mtu(struct net_device *dev, int new_mtu); +void free_libipw(struct net_device *dev, int monitor); +struct net_device *alloc_libipw(int sizeof_priv, int monitor); +int libipw_change_mtu(struct net_device *dev, int new_mtu); -extern void libipw_networks_age(struct libipw_device *ieee, - unsigned long age_secs); +void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); -extern int libipw_set_encryption(struct libipw_device *ieee); +int libipw_set_encryption(struct libipw_device *ieee); /* libipw_tx.c */ -extern netdev_tx_t libipw_xmit(struct sk_buff *skb, - struct net_device *dev); -extern void libipw_txb_free(struct libipw_txb *); +netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev); +void libipw_txb_free(struct libipw_txb *); /* libipw_rx.c */ -extern void libipw_rx_any(struct libipw_device *ieee, - struct sk_buff *skb, struct libipw_rx_stats *stats); -extern int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *rx_stats); +void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *stats); +int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats); /* make sure to set stats->len */ -extern void libipw_rx_mgt(struct libipw_device *ieee, - struct libipw_hdr_4addr *header, - struct libipw_rx_stats *stats); -extern void libipw_network_reset(struct libipw_network *network); +void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header, + struct libipw_rx_stats *stats); +void libipw_network_reset(struct libipw_network *network); /* libipw_geo.c */ -extern const struct libipw_geo *libipw_get_geo(struct libipw_device - *ieee); -extern void libipw_set_geo(struct libipw_device *ieee, - const struct libipw_geo *geo); - -extern int libipw_is_valid_channel(struct libipw_device *ieee, - u8 channel); -extern int libipw_channel_to_index(struct libipw_device *ieee, - u8 channel); -extern u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); -extern u8 libipw_get_channel_flags(struct libipw_device *ieee, - u8 channel); -extern const struct libipw_channel *libipw_get_channel(struct - libipw_device - *ieee, u8 channel); -extern u32 libipw_channel_to_freq(struct libipw_device * ieee, - u8 channel); +const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); +void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo); + +int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel); +int libipw_channel_to_index(struct libipw_device *ieee, u8 channel); +u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); +u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel); +const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, + u8 channel); +u32 libipw_channel_to_freq(struct libipw_device *ieee, u8 channel); /* libipw_wx.c */ -extern int libipw_wx_get_scan(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int libipw_wx_set_encode(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int libipw_wx_get_encode(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int libipw_wx_set_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); -extern int libipw_wx_get_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); +int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +int libipw_wx_set_encode(struct libipw_device *ieee, + struct iw_request_info *info, union iwreq_data *wrqu, + char *key); +int libipw_wx_get_encode(struct libipw_device *ieee, + struct iw_request_info *info, union iwreq_data *wrqu, + char *key); +int libipw_wx_set_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int libipw_wx_get_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); static inline void libipw_increment_scans(struct libipw_device *ieee) { diff --git a/drivers/net/wireless/ipw2x00/libipw_rx.c b/drivers/net/wireless/ipw2x00/libipw_rx.c index 9ffe65931b2..a586a85bfcf 100644 --- a/drivers/net/wireless/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/ipw2x00/libipw_rx.c @@ -874,13 +874,13 @@ void libipw_rx_any(struct libipw_device *ieee, switch (ieee->iw_mode) { case IW_MODE_ADHOC: /* our BSS and not from/to DS */ - if (memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) == 0) + if (ether_addr_equal(hdr->addr3, ieee->bssid)) if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == 0) { /* promisc: get all */ if (ieee->dev->flags & IFF_PROMISC) is_packet_for_us = 1; /* to us */ - else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0) + else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) is_packet_for_us = 1; /* mcast */ else if (is_multicast_ether_addr(hdr->addr1)) @@ -889,18 +889,18 @@ void libipw_rx_any(struct libipw_device *ieee, break; case IW_MODE_INFRA: /* our BSS (== from our AP) and from DS */ - if (memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) == 0) + if (ether_addr_equal(hdr->addr2, ieee->bssid)) if ((fc & (IEEE80211_FCTL_TODS+IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS) { /* promisc: get all */ if (ieee->dev->flags & IFF_PROMISC) is_packet_for_us = 1; /* to us */ - else if (memcmp(hdr->addr1, ieee->dev->dev_addr, ETH_ALEN) == 0) + else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) is_packet_for_us = 1; /* mcast */ else if (is_multicast_ether_addr(hdr->addr1)) { /* not our own packet bcasted from AP */ - if (memcmp(hdr->addr3, ieee->dev->dev_addr, ETH_ALEN)) + if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr)) is_packet_for_us = 1; } } @@ -1468,7 +1468,7 @@ static inline int is_same_network(struct libipw_network *src, * as one network */ return ((src->ssid_len == dst->ssid_len) && (src->channel == dst->channel) && - ether_addr_equal(src->bssid, dst->bssid) && + ether_addr_equal_64bits(src->bssid, dst->bssid) && !memcmp(src->ssid, dst->ssid, src->ssid_len)); } diff --git a/drivers/net/wireless/iwlegacy/3945-debug.c b/drivers/net/wireless/iwlegacy/3945-debug.c index f767dd106b0..c1b4441fb8b 100644 --- a/drivers/net/wireless/iwlegacy/3945-debug.c +++ b/drivers/net/wireless/iwlegacy/3945-debug.c @@ -48,7 +48,7 @@ il3945_stats_flag(struct il_priv *il, char *buf, int bufsz) return p; } -ssize_t +static ssize_t il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -313,7 +313,7 @@ il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf, return ret; } -ssize_t +static ssize_t il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -403,7 +403,7 @@ il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf, return ret; } -ssize_t +static ssize_t il3945_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index 9581d07a424..dc1d20cf64e 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -1248,14 +1248,7 @@ il3945_rx_handle(struct il_priv *il) len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; len += sizeof(u32); /* account for status word */ - /* Reclaim a command buffer only if this packet is a response - * to a (driver-originated) command. - * If the packet (e.g. Rx frame) originated from uCode, - * there is no command buffer to reclaim. - * Ucode should set SEQ_RX_FRAME bit if ucode-originated, - * but apparently a few don't get set; catch them here. */ - reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && - pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX; + reclaim = il_need_reclaim(il, pkt); /* Based on type of command response or notification, * handle those that need handling via function in @@ -1495,12 +1488,14 @@ il3945_irq_tasklet(struct il_priv *il) if (inta & CSR_INT_BIT_WAKEUP) { D_ISR("Wakeup interrupt\n"); il_rx_queue_update_write_ptr(il, &il->rxq); + + spin_lock_irqsave(&il->lock, flags); il_txq_update_write_ptr(il, &il->txq[0]); il_txq_update_write_ptr(il, &il->txq[1]); il_txq_update_write_ptr(il, &il->txq[2]); il_txq_update_write_ptr(il, &il->txq[3]); il_txq_update_write_ptr(il, &il->txq[4]); - il_txq_update_write_ptr(il, &il->txq[5]); + spin_unlock_irqrestore(&il->lock, flags); il->isr_stats.wakeup++; handled |= CSR_INT_BIT_WAKEUP; @@ -1595,7 +1590,7 @@ il3945_get_channels_for_scan(struct il_priv *il, enum ieee80211_band band, * and use long active_dwell time. */ if (!is_active || il_is_channel_passive(ch_info) || - (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) { + (chan->flags & IEEE80211_CHAN_NO_IR)) { scan_ch->type = 0; /* passive */ if (IL_UCODE_API(il->ucode_ver) == 1) scan_ch->active_dwell = @@ -2396,8 +2391,7 @@ __il3945_up(struct il_priv *il) clear_bit(S_RFKILL, &il->status); else { set_bit(S_RFKILL, &il->status); - IL_WARN("Radio disabled by HW RF Kill switch\n"); - return -ENODEV; + return -ERFKILL; } _il_wr(il, CSR_INT, 0xFFFFFFFF); @@ -3575,9 +3569,9 @@ il3945_setup_mac(struct il_priv *il) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->flags |= - WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -3811,7 +3805,6 @@ out_iounmap: out_pci_release_regions: pci_release_regions(pdev); out_pci_disable_device: - pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(il->hw); @@ -3888,7 +3881,6 @@ il3945_pci_remove(struct pci_dev *pdev) iounmap(il->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); il_free_channel_map(il); il_free_geos(il); diff --git a/drivers/net/wireless/iwlegacy/3945-rs.c b/drivers/net/wireless/iwlegacy/3945-rs.c index aea667b430c..76b0729ade1 100644 --- a/drivers/net/wireless/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/iwlegacy/3945-rs.c @@ -25,7 +25,6 @@ *****************************************************************************/ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <net/mac80211.h> @@ -892,8 +891,7 @@ il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, { } -static struct rate_control_ops rs_ops = { - .module = NULL, +static const struct rate_control_ops rs_ops = { .name = RS_NAME, .tx_status = il3945_rs_tx_status, .get_rate = il3945_rs_get_rate, diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index f09e257759d..b598e280350 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/pci.h> #include <linux/dma-mapping.h> @@ -466,10 +465,10 @@ il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header) switch (il->iw_mode) { case NL80211_IFTYPE_ADHOC: /* Header: Dest. | Source | BSSID */ /* packets to our IBSS update information */ - return ether_addr_equal(header->addr3, il->bssid); + return ether_addr_equal_64bits(header->addr3, il->bssid); case NL80211_IFTYPE_STATION: /* Header: Dest. | AP{BSSID} | Source */ /* packets to our IBSS update information */ - return ether_addr_equal(header->addr2, il->bssid); + return ether_addr_equal_64bits(header->addr2, il->bssid); default: return 1; } @@ -574,7 +573,7 @@ il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) rx_status.flag |= RX_FLAG_SHORTPRE; if ((unlikely(rx_stats->phy_count > 20))) { - D_DROP("dsp size out of range [0,20]: %d/n", + D_DROP("dsp size out of range [0,20]: %d\n", rx_stats->phy_count); return; } diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/iwlegacy/3945.h index 9a8703def0b..00030d43a19 100644 --- a/drivers/net/wireless/iwlegacy/3945.h +++ b/drivers/net/wireless/iwlegacy/3945.h @@ -189,15 +189,14 @@ struct il3945_ibss_seq { * for use by iwl-*.c * *****************************************************************************/ -extern int il3945_calc_db_from_ratio(int sig_ratio); -extern void il3945_rx_replenish(void *data); -extern void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); -extern unsigned int il3945_fill_beacon_frame(struct il_priv *il, - struct ieee80211_hdr *hdr, - int left); -extern int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, - char **buf, bool display); -extern void il3945_dump_nic_error_log(struct il_priv *il); +int il3945_calc_db_from_ratio(int sig_ratio); +void il3945_rx_replenish(void *data); +void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); +unsigned int il3945_fill_beacon_frame(struct il_priv *il, + struct ieee80211_hdr *hdr, int left); +int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, char **buf, + bool display); +void il3945_dump_nic_error_log(struct il_priv *il); /****************************************************************************** * @@ -215,39 +214,36 @@ extern void il3945_dump_nic_error_log(struct il_priv *il); * il3945_mac_ <-- mac80211 callback * ****************************************************************************/ -extern void il3945_hw_handler_setup(struct il_priv *il); -extern void il3945_hw_setup_deferred_work(struct il_priv *il); -extern void il3945_hw_cancel_deferred_work(struct il_priv *il); -extern int il3945_hw_rxq_stop(struct il_priv *il); -extern int il3945_hw_set_hw_params(struct il_priv *il); -extern int il3945_hw_nic_init(struct il_priv *il); -extern int il3945_hw_nic_stop_master(struct il_priv *il); -extern void il3945_hw_txq_ctx_free(struct il_priv *il); -extern void il3945_hw_txq_ctx_stop(struct il_priv *il); -extern int il3945_hw_nic_reset(struct il_priv *il); -extern int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, - struct il_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset, - u8 pad); -extern void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); -extern int il3945_hw_get_temperature(struct il_priv *il); -extern int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); -extern unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, - struct il3945_frame *frame, - u8 rate); +void il3945_hw_handler_setup(struct il_priv *il); +void il3945_hw_setup_deferred_work(struct il_priv *il); +void il3945_hw_cancel_deferred_work(struct il_priv *il); +int il3945_hw_rxq_stop(struct il_priv *il); +int il3945_hw_set_hw_params(struct il_priv *il); +int il3945_hw_nic_init(struct il_priv *il); +int il3945_hw_nic_stop_master(struct il_priv *il); +void il3945_hw_txq_ctx_free(struct il_priv *il); +void il3945_hw_txq_ctx_stop(struct il_priv *il); +int il3945_hw_nic_reset(struct il_priv *il); +int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad); +void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); +int il3945_hw_get_temperature(struct il_priv *il); +int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); +unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, + struct il3945_frame *frame, u8 rate); void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, int sta_id); -extern int il3945_hw_reg_send_txpower(struct il_priv *il); -extern int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); -extern void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); +int il3945_hw_reg_send_txpower(struct il_priv *il); +int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); +void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb); -extern void il3945_disable_events(struct il_priv *il); -extern int il4965_get_temperature(const struct il_priv *il); -extern void il3945_post_associate(struct il_priv *il); -extern void il3945_config_ap(struct il_priv *il); +void il3945_disable_events(struct il_priv *il); +int il4965_get_temperature(const struct il_priv *il); +void il3945_post_associate(struct il_priv *il); +void il3945_config_ap(struct il_priv *il); -extern int il3945_commit_rxon(struct il_priv *il); +int il3945_commit_rxon(struct il_priv *il); /** * il3945_hw_find_station - Find station id for a given BSSID @@ -257,14 +253,14 @@ extern int il3945_commit_rxon(struct il_priv *il); * not yet been merged into a single common layer for managing the * station tables. */ -extern u8 il3945_hw_find_station(struct il_priv *il, const u8 * bssid); +u8 il3945_hw_find_station(struct il_priv *il, const u8 *bssid); -extern __le32 il3945_get_antenna_flags(const struct il_priv *il); -extern int il3945_init_hw_rate_table(struct il_priv *il); -extern void il3945_reg_txpower_periodic(struct il_priv *il); -extern int il3945_txpower_set_from_eeprom(struct il_priv *il); +__le32 il3945_get_antenna_flags(const struct il_priv *il); +int il3945_init_hw_rate_table(struct il_priv *il); +void il3945_reg_txpower_periodic(struct il_priv *il); +int il3945_txpower_set_from_eeprom(struct il_priv *il); -extern int il3945_rs_next_rate(struct il_priv *il, int rate); +int il3945_rs_next_rate(struct il_priv *il, int rate); /* scanning */ int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlegacy/4965-debug.c b/drivers/net/wireless/iwlegacy/4965-debug.c index c8153fc64f7..e0597bfdddb 100644 --- a/drivers/net/wireless/iwlegacy/4965-debug.c +++ b/drivers/net/wireless/iwlegacy/4965-debug.c @@ -55,7 +55,7 @@ il4965_stats_flag(struct il_priv *il, char *buf, int bufsz) return p; } -ssize_t +static ssize_t il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -467,7 +467,7 @@ il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, return ret; } -ssize_t +static ssize_t il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -633,7 +633,7 @@ il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, return ret; } -ssize_t +static ssize_t il4965_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 5ab50a5b48b..c159c05db6e 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -92,7 +92,6 @@ il4965_check_abort_status(struct il_priv *il, u8 frame_count, u32 status) * EEPROM */ struct il_mod_params il4965_mod_params = { - .amsdu_size_8K = 1, .restart_fw = 1, /* the rest are 0 by default */ }; @@ -671,7 +670,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) } if ((unlikely(phy_res->cfg_phy_cnt > 20))) { - D_DROP("dsp size out of range [0,20]: %d/n", + D_DROP("dsp size out of range [0,20]: %d\n", phy_res->cfg_phy_cnt); return; } @@ -805,7 +804,7 @@ il4965_get_channels_for_scan(struct il_priv *il, struct ieee80211_vif *vif, } if (!is_active || il_is_channel_passive(ch_info) || - (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) + (chan->flags & IEEE80211_CHAN_NO_IR)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; @@ -4274,17 +4273,7 @@ il4965_rx_handle(struct il_priv *il) len = le32_to_cpu(pkt->len_n_flags) & IL_RX_FRAME_SIZE_MSK; len += sizeof(u32); /* account for status word */ - /* Reclaim a command buffer only if this packet is a response - * to a (driver-originated) command. - * If the packet (e.g. Rx frame) originated from uCode, - * there is no command buffer to reclaim. - * Ucode should set SEQ_RX_FRAME bit if ucode-originated, - * but apparently a few don't get set; catch them here. */ - reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && - (pkt->hdr.cmd != N_RX_PHY) && (pkt->hdr.cmd != N_RX) && - (pkt->hdr.cmd != N_RX_MPDU) && - (pkt->hdr.cmd != N_COMPRESSED_BA) && - (pkt->hdr.cmd != N_STATS) && (pkt->hdr.cmd != C_TX); + reclaim = il_need_reclaim(il, pkt); /* Based on type of command response or notification, * handle those that need handling via function in @@ -5778,9 +5767,9 @@ il4965_mac_setup_register(struct il_priv *il, u32 max_probe_length) hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->flags |= - WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; /* * For now, disable PS by default because it affects @@ -6706,7 +6695,6 @@ out_free_eeprom: out_iounmap: iounmap(il->hw_base); out_pci_release_regions: - pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); @@ -6787,7 +6775,6 @@ il4965_pci_remove(struct pci_dev *pdev) iounmap(il->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); il4965_uninit_drv(il); @@ -6878,6 +6865,6 @@ module_param_named(11n_disable, il4965_mod_params.disable_11n, int, S_IRUGO); MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); module_param_named(amsdu_size_8K, il4965_mod_params.amsdu_size_8K, int, S_IRUGO); -MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0 [disabled])"); module_param_named(fw_restart, il4965_mod_params.restart_fw, int, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwlegacy/4965-rs.c b/drivers/net/wireless/iwlegacy/4965-rs.c index 3ccbaf791b4..eaaeea19d8c 100644 --- a/drivers/net/wireless/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/iwlegacy/4965-rs.c @@ -24,7 +24,6 @@ * *****************************************************************************/ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <net/mac80211.h> @@ -2808,8 +2807,7 @@ il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband, { } -static struct rate_control_ops rs_4965_ops = { - .module = NULL, +static const struct rate_control_ops rs_4965_ops = { .name = IL4965_RS_NAME, .tx_status = il4965_rs_tx_status, .get_rate = il4965_rs_get_rate, diff --git a/drivers/net/wireless/iwlegacy/4965.c b/drivers/net/wireless/iwlegacy/4965.c index 777a578294b..fe47db9c20c 100644 --- a/drivers/net/wireless/iwlegacy/4965.c +++ b/drivers/net/wireless/iwlegacy/4965.c @@ -26,7 +26,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h index 1b15b0b2292..337dfcf3bbd 100644 --- a/drivers/net/wireless/iwlegacy/4965.h +++ b/drivers/net/wireless/iwlegacy/4965.h @@ -272,7 +272,7 @@ il4965_hw_valid_rtc_data_addr(u32 addr) ((t) < IL_TX_POWER_TEMPERATURE_MIN || \ (t) > IL_TX_POWER_TEMPERATURE_MAX) -extern void il4965_temperature_calib(struct il_priv *il); +void il4965_temperature_calib(struct il_priv *il); /********************* END TEMPERATURE ***************************************/ /********************* START TXPOWER *****************************************/ diff --git a/drivers/net/wireless/iwlegacy/commands.h b/drivers/net/wireless/iwlegacy/commands.h index 04842151198..dd744135c95 100644 --- a/drivers/net/wireless/iwlegacy/commands.h +++ b/drivers/net/wireless/iwlegacy/commands.h @@ -2270,7 +2270,8 @@ struct il_spectrum_notification { */ #define IL_POWER_VEC_SIZE 5 -#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) +#define IL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) +#define IL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) #define IL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) struct il3945_powertable_cmd { diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c index b03e22ef546..ecc674627e6 100644 --- a/drivers/net/wireless/iwlegacy/common.c +++ b/drivers/net/wireless/iwlegacy/common.c @@ -33,7 +33,6 @@ #include <linux/slab.h> #include <linux/types.h> #include <linux/lockdep.h> -#include <linux/init.h> #include <linux/pci.h> #include <linux/dma-mapping.h> #include <linux/delay.h> @@ -1079,29 +1078,82 @@ EXPORT_SYMBOL(il_get_channel_info); * Setting power level allows the card to go to sleep when not busy. * * We calculate a sleep command based on the required latency, which - * we get from mac80211. In order to handle thermal throttling, we can - * also use pre-defined power levels. + * we get from mac80211. */ -/* - * This defines the old power levels. They are still used by default - * (level 1) and for thermal throttle (levels 3 through 5) - */ - -struct il_power_vec_entry { - struct il_powertable_cmd cmd; - u8 no_dtim; /* number of skip dtim */ -}; +#define SLP_VEC(X0, X1, X2, X3, X4) { \ + cpu_to_le32(X0), \ + cpu_to_le32(X1), \ + cpu_to_le32(X2), \ + cpu_to_le32(X3), \ + cpu_to_le32(X4) \ +} static void -il_power_sleep_cam_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) +il_build_powertable_cmd(struct il_priv *il, struct il_powertable_cmd *cmd) { + const __le32 interval[3][IL_POWER_VEC_SIZE] = { + SLP_VEC(2, 2, 4, 6, 0xFF), + SLP_VEC(2, 4, 7, 10, 10), + SLP_VEC(4, 7, 10, 10, 0xFF) + }; + int i, dtim_period, no_dtim; + u32 max_sleep; + bool skip; + memset(cmd, 0, sizeof(*cmd)); if (il->power_data.pci_pm) cmd->flags |= IL_POWER_PCI_PM_MSK; - D_POWER("Sleep command for CAM\n"); + /* if no Power Save, we are done */ + if (il->power_data.ps_disabled) + return; + + cmd->flags = IL_POWER_DRIVER_ALLOW_SLEEP_MSK; + cmd->keep_alive_seconds = 0; + cmd->debug_flags = 0; + cmd->rx_data_timeout = cpu_to_le32(25 * 1024); + cmd->tx_data_timeout = cpu_to_le32(25 * 1024); + cmd->keep_alive_beacons = 0; + + dtim_period = il->vif ? il->vif->bss_conf.dtim_period : 0; + + if (dtim_period <= 2) { + memcpy(cmd->sleep_interval, interval[0], sizeof(interval[0])); + no_dtim = 2; + } else if (dtim_period <= 10) { + memcpy(cmd->sleep_interval, interval[1], sizeof(interval[1])); + no_dtim = 2; + } else { + memcpy(cmd->sleep_interval, interval[2], sizeof(interval[2])); + no_dtim = 0; + } + + if (dtim_period == 0) { + dtim_period = 1; + skip = false; + } else { + skip = !!no_dtim; + } + + if (skip) { + __le32 tmp = cmd->sleep_interval[IL_POWER_VEC_SIZE - 1]; + + max_sleep = le32_to_cpu(tmp); + if (max_sleep == 0xFF) + max_sleep = dtim_period * (skip + 1); + else if (max_sleep > dtim_period) + max_sleep = (max_sleep / dtim_period) * dtim_period; + cmd->flags |= IL_POWER_SLEEP_OVER_DTIM_MSK; + } else { + max_sleep = dtim_period; + cmd->flags &= ~IL_POWER_SLEEP_OVER_DTIM_MSK; + } + + for (i = 0; i < IL_POWER_VEC_SIZE; i++) + if (le32_to_cpu(cmd->sleep_interval[i]) > max_sleep) + cmd->sleep_interval[i] = cpu_to_le32(max_sleep); } static int @@ -1174,7 +1226,8 @@ il_power_update_mode(struct il_priv *il, bool force) { struct il_powertable_cmd cmd; - il_power_sleep_cam_cmd(il, &cmd); + il_build_powertable_cmd(il, &cmd); + return il_power_set_mode(il, &cmd, force); } EXPORT_SYMBOL(il_power_update_mode); @@ -3445,10 +3498,10 @@ il_init_geos(struct il_priv *il) if (il_is_channel_valid(ch)) { if (!(ch->flags & EEPROM_CHANNEL_IBSS)) - geo_ch->flags |= IEEE80211_CHAN_NO_IBSS; + geo_ch->flags |= IEEE80211_CHAN_NO_IR; if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) - geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN; + geo_ch->flags |= IEEE80211_CHAN_NO_IR; if (ch->flags & EEPROM_CHANNEL_RADAR) geo_ch->flags |= IEEE80211_CHAN_RADAR; @@ -3746,10 +3799,10 @@ il_full_rxon_required(struct il_priv *il) /* These items are only settable from the full RXON command */ CHK(!il_is_associated(il)); - CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr)); - CHK(!ether_addr_equal(staging->node_addr, active->node_addr)); - CHK(!ether_addr_equal(staging->wlap_bssid_addr, - active->wlap_bssid_addr)); + CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr)); + CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr)); + CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr, + active->wlap_bssid_addr)); CHK_NEQ(staging->dev_type, active->dev_type); CHK_NEQ(staging->channel, active->channel); CHK_NEQ(staging->air_propagation, active->air_propagation); @@ -4702,7 +4755,8 @@ out: } EXPORT_SYMBOL(il_mac_change_interface); -void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct il_priv *il = hw->priv; unsigned long timeout = jiffies + msecs_to_jiffies(500); @@ -5082,6 +5136,7 @@ set_ch_out: } if (changed & (IEEE80211_CONF_CHANGE_PS | IEEE80211_CONF_CHANGE_IDLE)) { + il->power_data.ps_disabled = !(conf->flags & IEEE80211_CONF_PS); ret = il_power_update_mode(il, false); if (ret) D_MAC80211("Error setting sleep level\n"); diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 83f8ed8a552..ea5c0f863c4 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -858,9 +858,9 @@ struct il_hw_params { * il4965_mac_ <-- mac80211 callback * ****************************************************************************/ -extern void il4965_update_chain_flags(struct il_priv *il); +void il4965_update_chain_flags(struct il_priv *il); extern const u8 il_bcast_addr[ETH_ALEN]; -extern int il_queue_space(const struct il_queue *q); +int il_queue_space(const struct il_queue *q); static inline int il_queue_used(const struct il_queue *q, int i) { @@ -1123,6 +1123,7 @@ struct il_power_mgr { struct il_powertable_cmd sleep_cmd_next; int debug_sleep_level_override; bool pci_pm; + bool ps_disabled; }; struct il_priv { @@ -1597,7 +1598,7 @@ struct il_mod_params { int disable_hw_scan; /* def: 0 = use h/w scan */ int num_of_queues; /* def: HW dependent */ int disable_11n; /* def: 0 = 11n capabilities enabled */ - int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */ + int amsdu_size_8K; /* def: 0 = disable 8K amsdu size */ int antenna; /* def: 0 = both antennas (use diversity) */ int restart_fw; /* def: 1 = restart firmware */ }; @@ -1722,12 +1723,13 @@ void il_mac_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int il_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum nl80211_iftype newtype, bool newp2p); -void il_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop); +void il_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); int il_alloc_txq_mem(struct il_priv *il); void il_free_txq_mem(struct il_priv *il); #ifdef CONFIG_IWLEGACY_DEBUGFS -extern void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); +void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); #else static inline void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) @@ -1760,12 +1762,12 @@ void il_chswitch_done(struct il_priv *il, bool is_success); /***************************************************** * TX ******************************************************/ -extern void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); -extern int il_tx_queue_init(struct il_priv *il, u32 txq_id); -extern void il_tx_queue_reset(struct il_priv *il, u32 txq_id); -extern void il_tx_queue_unmap(struct il_priv *il, int txq_id); -extern void il_tx_queue_free(struct il_priv *il, int txq_id); -extern void il_setup_watchdog(struct il_priv *il); +void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); +int il_tx_queue_init(struct il_priv *il, u32 txq_id); +void il_tx_queue_reset(struct il_priv *il, u32 txq_id); +void il_tx_queue_unmap(struct il_priv *il, int txq_id); +void il_tx_queue_free(struct il_priv *il, int txq_id); +void il_setup_watchdog(struct il_priv *il); /***************************************************** * TX power ****************************************************/ @@ -1931,10 +1933,10 @@ il_is_ready_rf(struct il_priv *il) return il_is_ready(il); } -extern void il_send_bt_config(struct il_priv *il); -extern int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); -extern void il_apm_stop(struct il_priv *il); -extern void _il_apm_stop(struct il_priv *il); +void il_send_bt_config(struct il_priv *il); +int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); +void il_apm_stop(struct il_priv *il); +void _il_apm_stop(struct il_priv *il); int il_apm_init(struct il_priv *il); @@ -1968,15 +1970,29 @@ void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, irqreturn_t il_isr(int irq, void *data); -extern void il_set_bit(struct il_priv *p, u32 r, u32 m); -extern void il_clear_bit(struct il_priv *p, u32 r, u32 m); -extern bool _il_grab_nic_access(struct il_priv *il); -extern int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); -extern int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); -extern u32 il_rd_prph(struct il_priv *il, u32 reg); -extern void il_wr_prph(struct il_priv *il, u32 addr, u32 val); -extern u32 il_read_targ_mem(struct il_priv *il, u32 addr); -extern void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); +void il_set_bit(struct il_priv *p, u32 r, u32 m); +void il_clear_bit(struct il_priv *p, u32 r, u32 m); +bool _il_grab_nic_access(struct il_priv *il); +int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); +int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); +u32 il_rd_prph(struct il_priv *il, u32 reg); +void il_wr_prph(struct il_priv *il, u32 addr, u32 val); +u32 il_read_targ_mem(struct il_priv *il, u32 addr); +void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); + +static inline bool il_need_reclaim(struct il_priv *il, struct il_rx_pkt *pkt) +{ + /* Reclaim a command buffer only if this packet is a response + * to a (driver-originated) command. If the packet (e.g. Rx frame) + * originated from uCode, there is no command buffer to reclaim. + * Ucode should set SEQ_RX_FRAME bit if ucode-originated, but + * apparently a few don't get set; catch them here. + */ + return !(pkt->hdr.sequence & SEQ_RX_FRAME) && + pkt->hdr.cmd != N_STATS && pkt->hdr.cmd != C_TX && + pkt->hdr.cmd != N_RX_PHY && pkt->hdr.cmd != N_RX && + pkt->hdr.cmd != N_RX_MPDU && pkt->hdr.cmd != N_COMPRESSED_BA; +} static inline void _il_write8(struct il_priv *il, u32 ofs, u8 val) @@ -2868,13 +2884,13 @@ il4965_first_antenna(u8 mask) * The specific throughput table used is based on the type of network * the associated with, including A, B, G, and G w/ TGG protection */ -extern void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); +void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); /* Initialize station's rate scaling information after adding station */ -extern void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, - u8 sta_id); -extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, - u8 sta_id); +void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, + u8 sta_id); +void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, + u8 sta_id); /** * il_rate_control_register - Register the rate control algorithm callbacks @@ -2886,8 +2902,8 @@ extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, * ieee80211_register_hw * */ -extern int il4965_rate_control_register(void); -extern int il3945_rate_control_register(void); +int il4965_rate_control_register(void); +int il3945_rate_control_register(void); /** * il_rate_control_unregister - Unregister the rate control callbacks @@ -2895,11 +2911,11 @@ extern int il3945_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void il4965_rate_control_unregister(void); -extern void il3945_rate_control_unregister(void); +void il4965_rate_control_unregister(void); +void il3945_rate_control_unregister(void); -extern int il_power_update_mode(struct il_priv *il, bool force); -extern void il_power_initialize(struct il_priv *il); +int il_power_update_mode(struct il_priv *il, bool force); +void il_power_initialize(struct il_priv *il); extern u32 il_debug_level; diff --git a/drivers/net/wireless/iwlegacy/debug.c b/drivers/net/wireless/iwlegacy/debug.c index eff26501d60..34401015319 100644 --- a/drivers/net/wireless/iwlegacy/debug.c +++ b/drivers/net/wireless/iwlegacy/debug.c @@ -31,7 +31,7 @@ #include "common.h" -void +static void il_clear_traffic_stats(struct il_priv *il) { memset(&il->tx_stats, 0, sizeof(struct traffic_stats)); @@ -567,12 +567,12 @@ il_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", ((channels[i]. - flags & IEEE80211_CHAN_NO_IBSS) || + flags & IEEE80211_CHAN_NO_IR) || (channels[i]. flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i]. - flags & IEEE80211_CHAN_PASSIVE_SCAN ? + flags & IEEE80211_CHAN_NO_IR ? "passive only" : "active/passive"); } supp_band = il_get_hw_mode(il, IEEE80211_BAND_5GHZ); @@ -594,12 +594,12 @@ il_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", ((channels[i]. - flags & IEEE80211_CHAN_NO_IBSS) || + flags & IEEE80211_CHAN_NO_IR) || (channels[i]. flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i]. - flags & IEEE80211_CHAN_PASSIVE_SCAN ? + flags & IEEE80211_CHAN_NO_IR ? "passive only" : "active/passive"); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 3eb2102ce23..7fd50428b93 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -2,10 +2,6 @@ config IWLWIFI tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlwifi) " depends on PCI && MAC80211 && HAS_IOMEM select FW_LOADER - select NEW_LEDS - select LEDS_CLASS - select LEDS_TRIGGERS - select MAC80211_LEDS ---help--- Select to build the driver supporting the: @@ -43,6 +39,14 @@ config IWLWIFI say M here and read <file:Documentation/kbuild/modules.txt>. The module will be called iwlwifi. +config IWLWIFI_LEDS + bool + depends on IWLWIFI + depends on LEDS_CLASS=y || LEDS_CLASS=IWLWIFI + select LEDS_TRIGGERS + select MAC80211_LEDS + default y + config IWLDVM tristate "Intel Wireless WiFi DVM Firmware support" depends on IWLWIFI @@ -68,6 +72,19 @@ config IWLWIFI_OPMODE_MODULAR comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM" depends on IWLWIFI && IWLDVM=n && IWLMVM=n +config IWLWIFI_BCAST_FILTERING + bool "Enable broadcast filtering" + depends on IWLMVM + help + Say Y here to enable default bcast filtering configuration. + + Enabling broadcast filtering will drop any incoming wireless + broadcast frames, except some very specific predefined + patterns (e.g. incoming arp requests). + + If unsure, don't enable this option, as some programs might + expect incoming broadcasts for their normal operations. + menu "Debugging Options" depends on IWLWIFI diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 1fa64429bcc..3d32f412017 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -8,7 +8,7 @@ iwlwifi-objs += iwl-eeprom-read.o iwl-eeprom-parse.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o iwlwifi-$(CONFIG_IWLDVM) += iwl-1000.o iwl-2000.o iwl-5000.o iwl-6000.o -iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o +iwlwifi-$(CONFIG_IWLMVM) += iwl-7000.o iwl-8000.o iwlwifi-objs += $(iwlwifi-m) diff --git a/drivers/net/wireless/iwlwifi/dvm/Makefile b/drivers/net/wireless/iwlwifi/dvm/Makefile index dce7ab2e0c4..4d19685f31c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/Makefile +++ b/drivers/net/wireless/iwlwifi/dvm/Makefile @@ -4,9 +4,10 @@ iwldvm-objs += main.o rs.o mac80211.o ucode.o tx.o iwldvm-objs += lib.o calib.o tt.o sta.o rx.o iwldvm-objs += power.o -iwldvm-objs += scan.o led.o +iwldvm-objs += scan.o iwldvm-objs += rxon.o devices.o +iwldvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwldvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index f2a86ffc3b4..c160dad0303 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -109,7 +109,7 @@ extern const struct iwl_dvm_cfg iwl_dvm_6030_cfg; struct iwl_ucode_capabilities; -extern struct ieee80211_ops iwlagn_hw_ops; +extern const struct ieee80211_ops iwlagn_hw_ops; static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) { @@ -397,7 +397,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) return cpu_to_le32(flags|(u32)rate); } -extern int iwl_alive_start(struct iwl_priv *priv); +int iwl_alive_start(struct iwl_priv *priv); #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, @@ -480,7 +480,7 @@ do { \ } while (0) #endif /* CONFIG_IWLWIFI_DEBUG */ -extern const char *iwl_dvm_cmd_strings[REPLY_MAX]; +extern const char *const iwl_dvm_cmd_strings[REPLY_MAX]; static inline const char *iwl_dvm_get_cmd_string(u8 cmd) { diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.c b/drivers/net/wireless/iwlwifi/dvm/calib.c index 1b0f0d50256..20e6aa91070 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.c +++ b/drivers/net/wireless/iwlwifi/dvm/calib.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -94,7 +94,6 @@ int iwl_send_calib_results(struct iwl_priv *priv) { struct iwl_host_cmd hcmd = { .id = REPLY_PHY_CALIBRATION_CMD, - .flags = CMD_SYNC, }; struct iwl_calib_result *res; diff --git a/drivers/net/wireless/iwlwifi/dvm/calib.h b/drivers/net/wireless/iwlwifi/dvm/calib.h index cfddde19494..aeae4e80ea4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/calib.h +++ b/drivers/net/wireless/iwlwifi/dvm/calib.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index ebdac909f0c..751ae1d10b7 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index d94f8ab1500..0ffb6ff1a25 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -352,12 +352,12 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, channels[i].max_power, channels[i].flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + ((channels[i].flags & IEEE80211_CHAN_NO_IR) || (channels[i].flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i].flags & - IEEE80211_CHAN_PASSIVE_SCAN ? + IEEE80211_CHAN_NO_IR ? "passive only" : "active/passive"); } supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); @@ -375,12 +375,12 @@ static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, channels[i].max_power, channels[i].flags & IEEE80211_CHAN_RADAR ? " (IEEE 802.11h required)" : "", - ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + ((channels[i].flags & IEEE80211_CHAN_NO_IR) || (channels[i].flags & IEEE80211_CHAN_RADAR)) ? "" : ", IBSS", channels[i].flags & - IEEE80211_CHAN_PASSIVE_SCAN ? + IEEE80211_CHAN_NO_IR ? "passive only" : "active/passive"); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); @@ -1481,7 +1481,7 @@ static ssize_t iwl_dbgfs_ucode_bt_stats_read(struct file *file, /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - ret = iwl_send_statistics_request(priv, CMD_SYNC, false); + ret = iwl_send_statistics_request(priv, 0, false); mutex_unlock(&priv->mutex); if (ret) @@ -1868,7 +1868,7 @@ static ssize_t iwl_dbgfs_clear_ucode_statistics_write(struct file *file, /* make request to uCode to retrieve statistics information */ mutex_lock(&priv->mutex); - iwl_send_statistics_request(priv, CMD_SYNC, true); + iwl_send_statistics_request(priv, 0, true); mutex_unlock(&priv->mutex); return count; @@ -2188,7 +2188,6 @@ static int iwl_cmd_echo_test(struct iwl_priv *priv) struct iwl_host_cmd cmd = { .id = REPLY_ECHO, .len = { 0 }, - .flags = CMD_SYNC, }; ret = iwl_dvm_send_cmd(priv, &cmd); @@ -2320,7 +2319,7 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, mutex_lock(&priv->mutex); /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, CMD_SYNC, 0, NULL); + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_ERROR, 0, 0, NULL); mutex_unlock(&priv->mutex); diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index a79fdd137f9..a6f22c32a27 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 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 @@ -270,7 +270,7 @@ struct iwl_sensitivity_ranges { * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * ****************************************************************************/ -extern void iwl_update_chain_flags(struct iwl_priv *priv); +void iwl_update_chain_flags(struct iwl_priv *priv); extern const u8 iwl_bcast_addr[ETH_ALEN]; #define IWL_OPERATION_MODE_AUTO 0 @@ -888,9 +888,11 @@ struct iwl_priv { struct iwl_event_log event_log; +#ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; unsigned long blink_on, blink_off; bool led_registered; +#endif /* WoWLAN GTK rekey data */ u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN]; diff --git a/drivers/net/wireless/iwlwifi/dvm/devices.c b/drivers/net/wireless/iwlwifi/dvm/devices.c index 352c6cb7b4f..34b41e5f7cf 100644 --- a/drivers/net/wireless/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/iwlwifi/dvm/devices.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -317,7 +317,7 @@ static const struct iwl_sensitivity_ranges iwl5000_sensitivity = { .nrg_th_cca = 62, }; -static struct iwl_sensitivity_ranges iwl5150_sensitivity = { +static const struct iwl_sensitivity_ranges iwl5150_sensitivity = { .min_nrg_cck = 95, .auto_corr_min_ofdm = 90, .auto_corr_min_ofdm_mrc = 170, @@ -417,7 +417,6 @@ static int iwl5000_hw_channel_switch(struct iwl_priv *priv, struct iwl_host_cmd hcmd = { .id = REPLY_CHANNEL_SWITCH, .len = { sizeof(cmd), }, - .flags = CMD_SYNC, .data = { &cmd, }, }; @@ -579,7 +578,6 @@ static int iwl6000_hw_channel_switch(struct iwl_priv *priv, struct iwl_host_cmd hcmd = { .id = REPLY_CHANNEL_SWITCH, .len = { sizeof(*cmd), }, - .flags = CMD_SYNC, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; int err; diff --git a/drivers/net/wireless/iwlwifi/dvm/led.c b/drivers/net/wireless/iwlwifi/dvm/led.c index 33c7e15d24f..ca4d6692cc4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/iwlwifi/dvm/led.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 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 @@ -27,7 +27,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/delay.h> #include <linux/skbuff.h> #include <linux/netdevice.h> diff --git a/drivers/net/wireless/iwlwifi/dvm/led.h b/drivers/net/wireless/iwlwifi/dvm/led.h index 8749dcfe695..1c6b2252d0f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/led.h +++ b/drivers/net/wireless/iwlwifi/dvm/led.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 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 @@ -36,8 +36,20 @@ struct iwl_priv; #define IWL_LED_ACTIVITY (0<<1) #define IWL_LED_LINK (1<<1) +#ifdef CONFIG_IWLWIFI_LEDS void iwlagn_led_enable(struct iwl_priv *priv); void iwl_leds_init(struct iwl_priv *priv); void iwl_leds_exit(struct iwl_priv *priv); +#else +static inline void iwlagn_led_enable(struct iwl_priv *priv) +{ +} +static inline void iwl_leds_init(struct iwl_priv *priv) +{ +} +static inline void iwl_leds_exit(struct iwl_priv *priv) +{ +} +#endif #endif /* __iwl_leds_h__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index 3d5bdc4217a..2191621d69c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -29,7 +29,6 @@ #include <linux/etherdevice.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/sched.h> #include <net/mac80211.h> @@ -82,7 +81,7 @@ int iwlagn_send_tx_power(struct iwl_priv *priv) else tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; - return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0, sizeof(tx_power_cmd), &tx_power_cmd); } @@ -142,7 +141,6 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk) struct iwl_host_cmd cmd = { .id = REPLY_TXFIFO_FLUSH, .len = { sizeof(struct iwl_txfifo_flush_cmd), }, - .flags = CMD_SYNC, .data = { &flush_cmd, }, }; @@ -181,7 +179,7 @@ void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) goto done; } IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans); + iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); done: ieee80211_wake_queues(priv->hw); mutex_unlock(&priv->mutex); @@ -334,12 +332,12 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) memcpy(&bt_cmd_v2.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(bt_cmd_v2), &bt_cmd_v2); + 0, sizeof(bt_cmd_v2), &bt_cmd_v2); } else { memcpy(&bt_cmd_v1.basic, &basic, sizeof(basic)); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(bt_cmd_v1), &bt_cmd_v1); + 0, sizeof(bt_cmd_v1), &bt_cmd_v1); } if (ret) IWL_ERR(priv, "failed to send BT Coex Config\n"); @@ -1045,7 +1043,6 @@ int iwlagn_send_patterns(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_WOWLAN_PATTERNS, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .flags = CMD_SYNC, }; int i, err; @@ -1202,7 +1199,6 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) if (key_data.use_rsc_tsc) { struct iwl_host_cmd rsc_tsc_cmd = { .id = REPLY_WOWLAN_TSC_RSC_PARAMS, - .flags = CMD_SYNC, .data[0] = key_data.rsc_tsc, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .len[0] = sizeof(*key_data.rsc_tsc), @@ -1216,7 +1212,7 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) if (key_data.use_tkip) { ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_TKIP_PARAMS, - CMD_SYNC, sizeof(tkip_cmd), + 0, sizeof(tkip_cmd), &tkip_cmd); if (ret) goto out; @@ -1232,20 +1228,20 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_KEK_KCK_MATERIAL, - CMD_SYNC, sizeof(kek_kck_cmd), + 0, sizeof(kek_kck_cmd), &kek_kck_cmd); if (ret) goto out; } } - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0, sizeof(d3_cfg_cmd), &d3_cfg_cmd); if (ret) goto out; ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, - CMD_SYNC, sizeof(wakeup_filter_cmd), + 0, sizeof(wakeup_filter_cmd), &wakeup_filter_cmd); if (ret) goto out; diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index cae4d3182e3..29af7b51e37 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -28,7 +28,6 @@ *****************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/dma-mapping.h> #include <linux/delay.h> @@ -155,9 +154,9 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, ARRAY_SIZE(iwlagn_iface_combinations_dualmode); } - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; #ifdef CONFIG_PM_SLEEP if (priv->fw->img[IWL_UCODE_WOWLAN].sec[0].len && @@ -322,12 +321,6 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw) flush_workqueue(priv->workqueue); - /* User space software may expect getting rfkill changes - * even if interface is down, trans->down will leave the RF - * kill interrupt enabled - */ - iwl_trans_stop_hw(priv->trans, false); - IWL_DEBUG_MAC80211(priv, "leave\n"); } @@ -413,9 +406,8 @@ static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait, { struct iwl_resume_data *resume_data = data; struct iwl_priv *priv = resume_data->priv; - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - 4 != sizeof(*resume_data->cmd)) { + if (iwl_rx_packet_payload_len(pkt) != sizeof(*resume_data->cmd)) { IWL_ERR(priv, "rx wrong size data\n"); return true; } @@ -704,6 +696,24 @@ static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return ret; } +static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + return false; + return true; +} + +static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + return false; + if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) + return true; + + /* disabled by default */ + return false; +} + static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, @@ -725,7 +735,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_RX_START: - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + if (!iwl_enable_rx_ampdu(priv->cfg)) break; IWL_DEBUG_HT(priv, "start Rx\n"); ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); @@ -737,7 +747,7 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_START: if (!priv->trans->ops->txq_enable) break; - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + if (!iwl_enable_tx_ampdu(priv->cfg)) break; IWL_DEBUG_HT(priv, "start Tx\n"); ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); @@ -1081,7 +1091,8 @@ static void iwlagn_configure_filter(struct ieee80211_hw *hw, FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; } -static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1109,7 +1120,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) } } IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(priv->trans); + iwl_trans_wait_tx_queue_empty(priv->trans, 0xffffffff); done: mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); @@ -1572,7 +1583,7 @@ static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "leave\n"); } -struct ieee80211_ops iwlagn_hw_ops = { +const struct ieee80211_ops iwlagn_hw_ops = { .tx = iwlagn_mac_tx, .start = iwlagn_mac_start, .stop = iwlagn_mac_stop, diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 7aad766865c..0b7f46f0b07 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -128,7 +128,6 @@ int iwlagn_send_beacon_cmd(struct iwl_priv *priv) struct iwl_tx_beacon_cmd *tx_beacon_cmd; struct iwl_host_cmd cmd = { .id = REPLY_TX_BEACON, - .flags = CMD_SYNC, }; struct ieee80211_tx_info *info; u32 frame_size; @@ -252,13 +251,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) @@ -307,8 +310,7 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) sizeof(struct iwl_statistics_cmd), &statistics_cmd); else - return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, - CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, 0, sizeof(struct iwl_statistics_cmd), &statistics_cmd); } @@ -618,7 +620,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) ret = iwl_dvm_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, - CMD_SYNC, sizeof(adv_cmd), &adv_cmd); + 0, sizeof(adv_cmd), &adv_cmd); if (ret) IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); else @@ -633,7 +635,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) ret = iwl_dvm_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, - CMD_SYNC, sizeof(cmd), &cmd); + 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); else @@ -669,9 +671,7 @@ static int iwlagn_send_tx_ant_config(struct iwl_priv *priv, u8 valid_tx_ant) if (IWL_UCODE_API(priv->fw->ucode_ver) > 1) { IWL_DEBUG_HC(priv, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_dvm_send_cmd_pdu(priv, - TX_ANT_CONFIGURATION_CMD, - CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, TX_ANT_CONFIGURATION_CMD, 0, sizeof(struct iwl_tx_ant_config_cmd), &tx_ant_cmd); } else { @@ -699,7 +699,7 @@ static void iwl_send_bt_config(struct iwl_priv *priv) (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); if (iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, - CMD_SYNC, sizeof(struct iwl_bt_cmd), &bt_cmd)) + 0, sizeof(struct iwl_bt_cmd), &bt_cmd)) IWL_ERR(priv, "failed to send BT Coex Config\n"); } @@ -983,7 +983,7 @@ static void iwl_bg_restart(struct work_struct *data) ieee80211_restart_hw(priv->hw); else IWL_ERR(priv, - "Cannot request restart before registrating with mac80211"); + "Cannot request restart before registrating with mac80211\n"); } else { WARN_ON(1); } @@ -1123,7 +1123,6 @@ static void iwl_option_config(struct iwl_priv *priv) static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) { struct iwl_nvm_data *data = priv->nvm_data; - char *debug_msg; if (data->sku_cap_11n_enable && !priv->cfg->ht_params) { @@ -1137,8 +1136,8 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) return -EINVAL; } - debug_msg = "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n"; - IWL_DEBUG_INFO(priv, debug_msg, + IWL_DEBUG_INFO(priv, + "Device SKU: 24GHz %s %s, 52GHz %s %s, 11.n %s %s\n", data->sku_cap_band_24GHz_enable ? "" : "NOT", "enabled", data->sku_cap_band_52GHz_enable ? "" : "NOT", "enabled", data->sku_cap_11n_enable ? "" : "NOT", "enabled"); @@ -1313,7 +1312,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, } /* Reset chip to save power until we load uCode during "up". */ - iwl_trans_stop_hw(priv->trans, false); + iwl_trans_stop_device(priv->trans); priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg, priv->eeprom_blob, @@ -1346,7 +1345,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, iwl_set_hw_params(priv); if (!(priv->nvm_data->sku_cap_ipan_enable)) { - IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN"); + IWL_DEBUG_INFO(priv, "Your EEPROM disabled PAN\n"); ucode_flags &= ~IWL_UCODE_TLV_FLAGS_PAN; /* * if not PAN, then don't support P2P -- might be a uCode @@ -1458,7 +1457,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) dev_kfree_skb(priv->beacon_skb); - iwl_trans_stop_hw(priv->trans, true); + iwl_trans_op_mode_leave(priv->trans); ieee80211_free_hw(priv->hw); } @@ -2015,10 +2014,10 @@ void iwlagn_lift_passive_no_rx(struct iwl_priv *priv) for (mq = 0; mq < IWLAGN_FIRST_AMPDU_QUEUE; mq++) { if (!test_bit(mq, &priv->transport_queue_stop)) { - IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d", mq); + IWL_DEBUG_TX_QUEUES(priv, "Wake queue %d\n", mq); ieee80211_wake_queue(priv->hw, mq); } else { - IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d", mq); + IWL_DEBUG_TX_QUEUES(priv, "Don't wake queue %d\n", mq); } } @@ -2035,7 +2034,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 +2044,19 @@ 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 void iwl_napi_add(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct net_device *napi_dev, + int (*poll)(struct napi_struct *, int), + int weight) +{ + struct iwl_priv *priv = IWL_OP_MODE_GET_DVM(op_mode); + + ieee80211_napi_add(priv->hw, napi, napi_dev, poll, weight); } static const struct iwl_op_mode_ops iwl_dvm_ops = { @@ -2059,6 +2071,7 @@ static const struct iwl_op_mode_ops iwl_dvm_ops = { .cmd_queue_full = iwl_cmd_queue_full, .nic_config = iwl_nic_config, .wimax_active = iwl_wimax_active, + .napi_add = iwl_napi_add, }; /***************************************************************************** diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c index 77cb5971223..f2c1439566b 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/iwlwifi/dvm/power.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -30,7 +30,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/init.h> #include <net/mac80211.h> #include "iwl-io.h" #include "iwl-debug.h" @@ -279,7 +278,7 @@ static int iwl_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) le32_to_cpu(cmd->sleep_interval[3]), le32_to_cpu(cmd->sleep_interval[4])); - return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, CMD_SYNC, + return iwl_dvm_send_cmd_pdu(priv, POWER_TABLE_CMD, 0, sizeof(struct iwl_powertable_cmd), cmd); } @@ -362,7 +361,7 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); } else - IWL_ERR(priv, "set power fail, ret = %d", ret); + IWL_ERR(priv, "set power fail, ret = %d\n", ret); return ret; } diff --git a/drivers/net/wireless/iwlwifi/dvm/power.h b/drivers/net/wireless/iwlwifi/dvm/power.h index 7b03e1342d4..570d3a5e467 100644 --- a/drivers/net/wireless/iwlwifi/dvm/power.h +++ b/drivers/net/wireless/iwlwifi/dvm/power.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c index b647e506564..32b78a66536 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.c +++ b/drivers/net/wireless/iwlwifi/dvm/rs.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 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 @@ -24,7 +24,6 @@ * *****************************************************************************/ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <net/mac80211.h> @@ -177,46 +176,46 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, * (2.4 GHz) band. */ -static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { +static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 }; -static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { +static const u16 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ }; -static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { +static const u16 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ }; -static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { +static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ }; -static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { +static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ }; -static s32 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { +static const u16 expected_tpt_mimo3_20MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 99, 0, 153, 186, 208, 239, 256, 263, 268}, /* Norm */ {0, 0, 0, 0, 106, 0, 162, 194, 215, 246, 262, 268, 273}, /* SGI */ {0, 0, 0, 0, 134, 0, 249, 346, 431, 574, 685, 732, 775}, /* AGG */ {0, 0, 0, 0, 148, 0, 272, 376, 465, 614, 727, 775, 818}, /* AGG+SGI */ }; -static s32 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { +static const u16 expected_tpt_mimo3_40MHz[4][IWL_RATE_COUNT] = { {0, 0, 0, 0, 152, 0, 211, 239, 255, 279, 290, 294, 297}, /* Norm */ {0, 0, 0, 0, 160, 0, 219, 245, 261, 284, 294, 297, 300}, /* SGI */ {0, 0, 0, 0, 254, 0, 443, 584, 695, 868, 984, 1030, 1070}, /* AGG */ @@ -1112,7 +1111,7 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl) { /* Used to choose among HT tables */ - s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; + const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; /* Check for invalid LQ type */ if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { @@ -1174,9 +1173,8 @@ static s32 rs_get_best_rate(struct iwl_priv *priv, &(lq_sta->lq_info[lq_sta->active_tbl]); s32 active_sr = active_tbl->win[index].success_ratio; s32 active_tpt = active_tbl->expected_tpt[index]; - /* expected "search" throughput */ - s32 *tpt_tbl = tbl->expected_tpt; + const u16 *tpt_tbl = tbl->expected_tpt; s32 new_rate, high, low, start_hi; u16 high_low; @@ -1455,7 +1453,7 @@ static int rs_move_legacy_other(struct iwl_priv *priv, tbl->action = IWL_LEGACY_SWITCH_SISO; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -1630,7 +1628,7 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, tbl->action = IWL_SISO_SWITCH_ANTENNA1; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -1801,7 +1799,7 @@ static int rs_move_mimo2_to_other(struct iwl_priv *priv, tbl->action = IWL_MIMO2_SWITCH_SISO_A; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -1971,7 +1969,7 @@ static int rs_move_mimo3_to_other(struct iwl_priv *priv, tbl->action = IWL_MIMO3_SWITCH_SISO_A; break; default: - IWL_ERR(priv, "Invalid BT load %d", priv->bt_traffic_load); + IWL_ERR(priv, "Invalid BT load %d\n", priv->bt_traffic_load); break; } @@ -2711,7 +2709,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_link_cmd(NULL, lq_sta, rate); priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; - iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true); + iwl_send_lq_cmd(priv, ctx, &lq_sta->lq, 0, true); } static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, @@ -3320,8 +3318,8 @@ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sba struct ieee80211_sta *sta, void *priv_sta) { } -static struct rate_control_ops rs_ops = { - .module = NULL, + +static const struct rate_control_ops rs_ops = { .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h index 5d83cab22d6..f6bd25cad20 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.h +++ b/drivers/net/wireless/iwlwifi/dvm/rs.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 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 @@ -315,7 +315,7 @@ struct iwl_scale_tbl_info { u8 is_dup; /* 1 = duplicated data streams */ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ u8 max_search; /* maximun number of tables we can search */ - s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ u32 current_rate; /* rate_n_flags, uCode API format */ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ }; @@ -389,13 +389,6 @@ struct iwl_lq_sta { u8 last_bt_traffic; }; -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - static inline u8 first_antenna(u8 mask) { if (mask & ANT_A) @@ -407,8 +400,8 @@ static inline u8 first_antenna(u8 mask) /* Initialize station's rate scaling information after adding station */ -extern void iwl_rs_rate_init(struct iwl_priv *priv, - struct ieee80211_sta *sta, u8 sta_id); +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, + u8 sta_id); /** * iwl_rate_control_register - Register the rate control algorithm callbacks @@ -420,7 +413,7 @@ extern void iwl_rs_rate_init(struct iwl_priv *priv, * ieee80211_register_hw * */ -extern int iwlagn_rate_control_register(void); +int iwlagn_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks @@ -428,6 +421,6 @@ extern int iwlagn_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void iwlagn_rate_control_unregister(void); +void iwlagn_rate_control_unregister(void); #endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index d71776dd1e6..debec963c61 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portionhelp of the ieee80211 subsystem header files. @@ -39,7 +39,7 @@ #define IWL_CMD_ENTRY(x) [x] = #x -const char *iwl_dvm_cmd_strings[REPLY_MAX] = { +const char *const iwl_dvm_cmd_strings[REPLY_MAX] = { IWL_CMD_ENTRY(REPLY_ALIVE), IWL_CMD_ENTRY(REPLY_ERROR), IWL_CMD_ENTRY(REPLY_ECHO), @@ -205,8 +205,7 @@ static int iwlagn_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 __maybe_unused len = - le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + u32 __maybe_unused len = iwl_rx_packet_len(pkt); IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " "notification for PM_DEBUG_STATISTIC_NOTIFIC:\n", len); iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->data, len); @@ -457,7 +456,7 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, const int reg_recalib_period = 60; int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); - u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + u32 len = iwl_rx_packet_payload_len(pkt); __le32 *flag; struct statistics_general_common *common; struct statistics_rx_non_phy *rx_non_phy; @@ -467,8 +466,6 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, struct statistics_tx *tx; struct statistics_bt_activity *bt_activity; - len -= sizeof(struct iwl_cmd_header); /* skip header */ - IWL_DEBUG_RX(priv, "Statistics notification received (%d bytes).\n", len); @@ -789,7 +786,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - ieee80211_rx_ni(priv->hw, skb); + ieee80211_rx(priv->hw, skb); } static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c index d7ce2f12a90..6dc5dd3ced4 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 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 @@ -104,7 +104,7 @@ static int iwlagn_disable_bss(struct iwl_priv *priv, send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - CMD_SYNC, sizeof(*send), send); + 0, sizeof(*send), send); send->filter_flags = old_filter; @@ -134,7 +134,7 @@ static int iwlagn_disable_pan(struct iwl_priv *priv, send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; send->dev_type = RXON_DEV_TYPE_P2P; ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, - CMD_SYNC, sizeof(*send), send); + 0, sizeof(*send), send); send->filter_flags = old_filter; send->dev_type = old_dev_type; @@ -160,7 +160,7 @@ static int iwlagn_disconn_pan(struct iwl_priv *priv, int ret; send->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, sizeof(*send), send); send->filter_flags = old_filter; @@ -189,7 +189,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv, ctx->qos_data.qos_active, ctx->qos_data.def_qos_parm.qos_flags); - ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, ctx->qos_cmd, 0, sizeof(struct iwl_qosparam_cmd), &ctx->qos_data.def_qos_parm); if (ret) @@ -353,7 +353,7 @@ static int iwl_send_rxon_timing(struct iwl_priv *priv, le16_to_cpu(ctx->timing.atim_window)); return iwl_dvm_send_cmd_pdu(priv, ctx->rxon_timing_cmd, - CMD_SYNC, sizeof(ctx->timing), &ctx->timing); + 0, sizeof(ctx->timing), &ctx->timing); } static int iwlagn_rxon_disconn(struct iwl_priv *priv, @@ -495,7 +495,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv, * Associated RXON doesn't clear the station table in uCode, * so we don't need to restore stations etc. after this. */ - ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, ctx->rxon_cmd, 0, sizeof(struct iwl_rxon_cmd), &ctx->staging); if (ret) { IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); @@ -610,7 +610,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv) cmd.slots[0].width = cpu_to_le16(slot0); cmd.slots[1].width = cpu_to_le16(slot1); - ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, CMD_SYNC, + ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WIPAN_PARAMS, 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "Error setting PAN parameters (%d)\n", ret); @@ -823,7 +823,7 @@ static int iwl_check_rxon_cmd(struct iwl_priv *priv, if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { - IWL_WARN(priv, "CCK and auto detect"); + IWL_WARN(priv, "CCK and auto detect\n"); errors |= BIT(8); } @@ -1068,13 +1068,6 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) /* recalculate basic rates */ iwl_calc_basic_rates(priv, ctx); - /* - * force CTS-to-self frames protection if RTS-CTS is not preferred - * one aggregation protection method - */ - if (!priv->hw_params.use_rts_for_aggregation) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || !(ctx->staging.flags & RXON_FLG_BAND_24G_MSK)) ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; @@ -1395,7 +1388,7 @@ static void iwlagn_chain_noise_reset(struct iwl_priv *priv) priv->phy_calib_chain_noise_reset_cmd); ret = iwl_dvm_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, - CMD_SYNC, sizeof(cmd), &cmd); + 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(priv, "Could not send REPLY_PHY_CALIBRATION_CMD\n"); @@ -1480,11 +1473,6 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, else ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - if (bss_conf->use_cts_prot) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); if (vif->type == NL80211_IFTYPE_AP || diff --git a/drivers/net/wireless/iwlwifi/dvm/scan.c b/drivers/net/wireless/iwlwifi/dvm/scan.c index 35e0ee8b4e5..43bef901e8f 100644 --- a/drivers/net/wireless/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/iwlwifi/dvm/scan.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -59,7 +59,7 @@ static int iwl_send_scan_abort(struct iwl_priv *priv) int ret; struct iwl_host_cmd cmd = { .id = REPLY_SCAN_ABORT_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; __le32 *status; @@ -544,7 +544,7 @@ static int iwl_get_channels_for_scan(struct iwl_priv *priv, channel = chan->hw_value; scan_ch->channel = cpu_to_le16(channel); - if (!is_active || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) + if (!is_active || (chan->flags & IEEE80211_CHAN_NO_IR)) scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; else scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; @@ -639,7 +639,6 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) struct iwl_host_cmd cmd = { .id = REPLY_SCAN_CMD, .len = { sizeof(struct iwl_scan_cmd), }, - .flags = CMD_SYNC, }; struct iwl_scan_cmd *scan; struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; diff --git a/drivers/net/wireless/iwlwifi/dvm/sta.c b/drivers/net/wireless/iwlwifi/dvm/sta.c index c3c13ce96eb..6ec86adbe4a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/sta.c +++ b/drivers/net/wireless/iwlwifi/dvm/sta.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -39,7 +39,7 @@ static int iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) lockdep_assert_held(&priv->sta_lock); if (sta_id >= IWLAGN_STATION_COUNT) { - IWL_ERR(priv, "invalid sta_id %u", sta_id); + IWL_ERR(priv, "invalid sta_id %u\n", sta_id); return -EINVAL; } if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) @@ -165,7 +165,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, iwl_free_resp(&cmd); if (cmd.handler_status) - IWL_ERR(priv, "%s - error in the CMD response %d", __func__, + IWL_ERR(priv, "%s - error in the CMD response %d\n", __func__, cmd.handler_status); return cmd.handler_status; @@ -261,7 +261,7 @@ int iwl_sta_update_ht(struct iwl_priv *priv, struct iwl_rxon_context *ctx, cmd.station_flags = flags; cmd.sta.sta_id = sta_id; - return iwl_send_add_sta(priv, &cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &cmd, 0); } static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, @@ -413,7 +413,7 @@ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, spin_unlock_bh(&priv->sta_lock); /* Add station to device's station table */ - ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + ret = iwl_send_add_sta(priv, &sta_cmd, 0); if (ret) { spin_lock_bh(&priv->sta_lock); IWL_ERR(priv, "Adding station %pM failed.\n", @@ -456,7 +456,6 @@ static int iwl_send_remove_station(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = { sizeof(struct iwl_rem_sta_cmd), }, - .flags = CMD_SYNC, .data = { &rm_sta_cmd, }, }; @@ -590,6 +589,7 @@ void iwl_deactivate_station(struct iwl_priv *priv, const u8 sta_id, sizeof(priv->tid_data[sta_id][tid])); priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; priv->num_stations--; @@ -739,7 +739,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) send_lq = true; } spin_unlock_bh(&priv->sta_lock); - ret = iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + ret = iwl_send_add_sta(priv, &sta_cmd, 0); if (ret) { spin_lock_bh(&priv->sta_lock); IWL_ERR(priv, "Adding station %pM failed.\n", @@ -755,8 +755,7 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) * current LQ command */ if (send_lq) - iwl_send_lq_cmd(priv, ctx, &lq, - CMD_SYNC, true); + iwl_send_lq_cmd(priv, ctx, &lq, 0, true); spin_lock_bh(&priv->sta_lock); priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; } @@ -967,7 +966,7 @@ int iwlagn_add_bssid_station(struct iwl_priv *priv, return -ENOMEM; } - ret = iwl_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true); + ret = iwl_send_lq_cmd(priv, ctx, link_cmd, 0, true); if (ret) IWL_ERR(priv, "Link quality command failed (%d)\n", ret); @@ -998,7 +997,6 @@ static int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, struct iwl_host_cmd cmd = { .id = ctx->wep_key_cmd, .data = { wep_cmd, }, - .flags = CMD_SYNC, }; might_sleep(); @@ -1247,7 +1245,7 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv, sta_cmd.sta.modify_mask = STA_MODIFY_KEY_MASK; sta_cmd.mode = STA_CONTROL_MODIFY_MSK; - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } int iwl_set_dynamic_key(struct iwl_priv *priv, @@ -1283,13 +1281,13 @@ int iwl_set_dynamic_key(struct iwl_priv *priv, ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - seq.tkip.iv32, p1k, CMD_SYNC); + seq.tkip.iv32, p1k, 0); break; case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ret = iwlagn_send_sta_key(priv, keyconf, sta_id, - 0, NULL, CMD_SYNC); + 0, NULL, 0); break; default: IWL_ERR(priv, "Unknown cipher %x\n", keyconf->cipher); @@ -1408,7 +1406,7 @@ int iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, @@ -1432,7 +1430,7 @@ int iwl_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, @@ -1457,7 +1455,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); spin_unlock_bh(&priv->sta_lock); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_send_add_sta(priv, &sta_cmd, 0); } diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index fbeee081ee2..acb981a0a0a 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -30,7 +30,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/init.h> #include <net/mac80211.h> #include "iwl-io.h" #include "iwl-modparams.h" @@ -237,7 +236,7 @@ static void iwl_prepare_ct_kill_task(struct iwl_priv *priv) { IWL_DEBUG_TEMP(priv, "Prepare to enter IWL_TI_CT_KILL\n"); /* make request to retrieve statistics information */ - iwl_send_statistics_request(priv, CMD_SYNC, false); + iwl_send_statistics_request(priv, 0, false); /* Reschedule the ct_kill wait timer */ mod_timer(&priv->thermal_throttle.ct_kill_waiting_tm, jiffies + msecs_to_jiffies(CT_KILL_WAITING_DURATION)); diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.h b/drivers/net/wireless/iwlwifi/dvm/tt.h index 9356c4b908c..507726534b8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.h +++ b/drivers/net/wireless/iwlwifi/dvm/tt.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index da442b81370..3255a1723d1 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -29,7 +29,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/sched.h> #include <linux/ieee80211.h> #include "iwl-io.h" @@ -368,6 +367,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, goto drop_unlock_priv; memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = REPLY_TX; tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; /* Total # bytes to be transmitted */ @@ -402,10 +402,10 @@ int iwlagn_tx_skb(struct iwl_priv *priv, /* aggregation is on for this <sta,tid> */ if (info->flags & IEEE80211_TX_CTL_AMPDU && tid_data->agg.state != IWL_AGG_ON) { - IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:" - " Tx flags = 0x%08x, agg.state = %d", + IWL_ERR(priv, + "TX_CTL_AMPDU while not in AGG: Tx flags = 0x%08x, agg.state = %d\n", info->flags, tid_data->agg.state); - IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", + IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d\n", sta_id, tid, IEEE80211_SEQ_TO_SN(tid_data->seq_number)); goto drop_unlock_sta; @@ -416,7 +416,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, */ if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && tid_data->agg.state != IWL_AGG_OFF, - "Tx while agg.state = %d", tid_data->agg.state)) + "Tx while agg.state = %d\n", tid_data->agg.state)) goto drop_unlock_sta; seq_number = tid_data->seq_number; @@ -433,27 +433,19 @@ int iwlagn_tx_skb(struct iwl_priv *priv, /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdr_len); + txq_id = info->hw_queue; + if (is_agg) txq_id = priv->tid_data[sta_id][tid].agg.txq_id; else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { /* - * Send this frame after DTIM -- there's a special queue - * reserved for this for contexts that support AP mode. - */ - txq_id = ctx->mcast_queue; - - /* * The microcode will clear the more data * bit in the last frame it transmits. */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - txq_id = IWL_AUX_QUEUE; - else - txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)]; + } - WARN_ON_ONCE(!is_agg && txq_id != info->hw_queue); WARN_ON_ONCE(is_agg && priv->queue_to_mac80211[txq_id] != info->hw_queue); @@ -786,8 +778,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, - "Can continue DELBA flow ssn = next_recl =" - " %d", tid_data->next_reclaimed); + "Can continue DELBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); iwl_trans_txq_disable(priv->trans, tid_data->agg.txq_id); iwlagn_dealloc_agg_txq(priv, tid_data->agg.txq_id); @@ -799,8 +791,8 @@ static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) /* There are no packets for this RA / TID in the HW any more */ if (tid_data->agg.ssn == tid_data->next_reclaimed) { IWL_DEBUG_TX_QUEUES(priv, - "Can continue ADDBA flow ssn = next_recl =" - " %d", tid_data->next_reclaimed); + "Can continue ADDBA flow ssn = next_recl = %d\n", + tid_data->next_reclaimed); tid_data->agg.state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); } @@ -1224,8 +1216,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, ctx->vif->type == NL80211_IFTYPE_STATION) { /* block and stop all queues */ priv->passive_no_rx = true; - IWL_DEBUG_TX_QUEUES(priv, "stop all queues: " - "passive channel"); + IWL_DEBUG_TX_QUEUES(priv, + "stop all queues: passive channel\n"); ieee80211_stop_queues(priv->hw); IWL_DEBUG_TX_REPLY(priv, @@ -1279,7 +1271,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); - ieee80211_tx_status_ni(priv->hw, skb); + ieee80211_tx_status(priv->hw, skb); } return 0; @@ -1299,8 +1291,6 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, struct iwl_compressed_ba_resp *ba_resp = (void *)pkt->data; struct iwl_ht_agg *agg; struct sk_buff_head reclaimed_skbs; - struct ieee80211_tx_info *info; - struct ieee80211_hdr *hdr; struct sk_buff *skb; int sta_id; int tid; @@ -1387,22 +1377,28 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, freed = 0; skb_queue_walk(&reclaimed_skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (ieee80211_is_data_qos(hdr->frame_control)) freed++; else WARN_ON_ONCE(1); - info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(priv->trans, info->driver_data[1]); + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ info = IEEE80211_SKB_CB(skb); memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_resp->txed_2_done; info->status.ampdu_len = ba_resp->txed; @@ -1415,7 +1411,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status_ni(priv->hw, skb); + ieee80211_tx_status(priv->hw, skb); } return 0; diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 86270b69cd0..d5cee153059 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -28,7 +28,6 @@ *****************************************************************************/ #include <linux/kernel.h> -#include <linux/init.h> #include "iwl-io.h" #include "iwl-agn-hw.h" @@ -173,7 +172,7 @@ static int iwl_send_wimax_coex(struct iwl_priv *priv) memset(&coex_cmd, 0, sizeof(coex_cmd)); return iwl_dvm_send_cmd_pdu(priv, - COEX_PRIORITY_TABLE_CMD, CMD_SYNC, + COEX_PRIORITY_TABLE_CMD, 0, sizeof(coex_cmd), &coex_cmd); } @@ -206,7 +205,7 @@ void iwl_send_prio_tbl(struct iwl_priv *priv) memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, sizeof(iwl_bt_prio_tbl)); if (iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC, + REPLY_BT_COEX_PRIO_TABLE, 0, sizeof(prio_tbl_cmd), &prio_tbl_cmd)) IWL_ERR(priv, "failed to send BT prio tbl command\n"); } @@ -219,7 +218,7 @@ int iwl_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) env_cmd.action = action; env_cmd.type = type; ret = iwl_dvm_send_cmd_pdu(priv, - REPLY_BT_COEX_PROT_ENV, CMD_SYNC, + REPLY_BT_COEX_PROT_ENV, 0, sizeof(env_cmd), &env_cmd); if (ret) IWL_ERR(priv, "failed to send BT env command\n"); @@ -330,15 +329,14 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv, enum iwl_ucode_type old_type; static const u8 alive_cmd[] = { REPLY_ALIVE }; - old_type = priv->cur_ucode; - priv->cur_ucode = ucode_type; fw = iwl_get_ucode_image(priv, ucode_type); + if (WARN_ON(!fw)) + return -EINVAL; + old_type = priv->cur_ucode; + priv->cur_ucode = ucode_type; priv->ucode_loaded = false; - if (!fw) - return -EINVAL; - iwl_init_notification_wait(&priv->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), iwl_alive_fn, &alive_data); @@ -390,7 +388,6 @@ static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, { struct iwl_priv *priv = data; struct iwl_calib_hdr *hdr; - int len; if (pkt->hdr.cmd != CALIBRATION_RES_NOTIFICATION) { WARN_ON(pkt->hdr.cmd != CALIBRATION_COMPLETE_NOTIFICATION); @@ -398,12 +395,8 @@ static bool iwlagn_wait_calib(struct iwl_notif_wait_data *notif_wait, } hdr = (struct iwl_calib_hdr *)pkt->data; - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - - /* reduce the size by the length field itself */ - len -= sizeof(__le32); - if (iwl_calib_set(priv, hdr, len)) + if (iwl_calib_set(priv, hdr, iwl_rx_packet_payload_len(pkt))) IWL_ERR(priv, "Failed to record calibration data %d\n", hdr->op_code); diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 0d2afe098af..c3817fae16c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -62,6 +62,7 @@ static const struct iwl_base_params iwl1000_base_params = { .led_compensation = 51, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 128, + .scd_chain_ext_wa = true, }; static const struct iwl_ht_params iwl1000_ht_params = { diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index c727ec7c90a..21e5d0843a6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -75,6 +75,7 @@ static const struct iwl_base_params iwl2000_base_params = { .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, }; @@ -88,6 +89,7 @@ static const struct iwl_base_params iwl2030_base_params = { .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, }; static const struct iwl_ht_params iwl2000_ht_params = { diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index ecc01e1a61a..332bbede39e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -61,6 +61,7 @@ static const struct iwl_base_params iwl5000_base_params = { .led_compensation = 51, .wd_timeout = IWL_WATCHDOG_DISABLED, .max_event_log_size = 512, + .scd_chain_ext_wa = true, }; static const struct iwl_ht_params iwl5000_ht_params = { diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 8ac305be68f..8f2c3c8c6b8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -85,6 +85,7 @@ static const struct iwl_base_params iwl6000_base_params = { .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, }; static const struct iwl_base_params iwl6050_base_params = { @@ -97,6 +98,7 @@ static const struct iwl_base_params iwl6050_base_params = { .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 1024, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, }; static const struct iwl_base_params iwl6000_g2_base_params = { @@ -109,6 +111,7 @@ static const struct iwl_base_params iwl6000_g2_base_params = { .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .shadow_reg_enable = false, /* TODO: fix bugs using this feature */ + .scd_chain_ext_wa = true, }; static const struct iwl_ht_params iwl6000_ht_params = { diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 76e14c046d9..48730064da7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,22 +67,24 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 7 -#define IWL3160_UCODE_API_MAX 7 +#define IWL7260_UCODE_API_MAX 9 +#define IWL3160_UCODE_API_MAX 9 /* Oldest version we won't warn about */ -#define IWL7260_UCODE_API_OK 7 -#define IWL3160_UCODE_API_OK 7 +#define IWL7260_UCODE_API_OK 9 +#define IWL3160_UCODE_API_OK 9 /* Lowest firmware API version supported */ -#define IWL7260_UCODE_API_MIN 7 -#define IWL3160_UCODE_API_MIN 7 +#define IWL7260_UCODE_API_MIN 8 +#define IWL3160_UCODE_API_MIN 8 /* NVM versions */ #define IWL7260_NVM_VERSION 0x0a1d #define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL3160_NVM_VERSION 0x709 #define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL7265_NVM_VERSION 0x0a1d +#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL7260_FW_PRE "iwlwifi-7260-" #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" @@ -90,8 +92,13 @@ #define IWL3160_FW_PRE "iwlwifi-3160-" #define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode" +#define IWL7265_FW_PRE "iwlwifi-7265-" +#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" + +#define NVM_HW_SECTION_NUM_FAMILY_7000 0 + static const struct iwl_base_params iwl7000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE, + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_7000, .num_of_queues = IWLAGN_NUM_QUEUES, .pll_cfg_val = 0, .shadow_ram_support = true, @@ -100,10 +107,11 @@ static const struct iwl_base_params iwl7000_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, + .apmg_wake_up_wa = true, }; static const struct iwl_ht_params iwl7000_ht_params = { - .use_rts_for_aggregation = true, /* use rts/cts protection */ + .stbc = true, .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; @@ -115,7 +123,8 @@ static const struct iwl_ht_params iwl7000_ht_params = { .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .base_params = &iwl7000_base_params, \ - .led_mode = IWL_LED_RF_STATE + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000 const struct iwl_cfg iwl7260_2ac_cfg = { @@ -125,6 +134,8 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .ht_params = &iwl7000_ht_params, .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 = { @@ -135,6 +146,8 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { .nvm_ver = IWL7260_NVM_VERSION, .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 = { @@ -144,6 +157,8 @@ const struct iwl_cfg iwl7260_2n_cfg = { .ht_params = &iwl7000_ht_params, .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 = { @@ -153,6 +168,8 @@ const struct iwl_cfg iwl7260_n_cfg = { .ht_params = &iwl7000_ht_params, .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 = { @@ -162,6 +179,7 @@ const struct iwl_cfg iwl3160_2ac_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl3160_2n_cfg = { @@ -171,6 +189,7 @@ const struct iwl_cfg iwl3160_2n_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, }; const struct iwl_cfg iwl3160_n_cfg = { @@ -180,7 +199,50 @@ const struct iwl_cfg iwl3160_n_cfg = { .ht_params = &iwl7000_ht_params, .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, + .host_interrupt_operation_mode = true, +}; + +static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = { + {.pwr = 1600, .backoff = 0}, + {.pwr = 1300, .backoff = 467}, + {.pwr = 900, .backoff = 1900}, + {.pwr = 800, .backoff = 2630}, + {.pwr = 700, .backoff = 3720}, + {.pwr = 600, .backoff = 5550}, + {.pwr = 500, .backoff = 9350}, + {0}, +}; + +const struct iwl_cfg iwl7265_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, +}; + +const struct iwl_cfg iwl7265_2n_cfg = { + .name = "Intel(R) Dual Band Wireless N 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, +}; + +const struct iwl_cfg iwl7265_n_cfg = { + .name = "Intel(R) Wireless N 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, + .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, }; MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK)); +MODULE_FIRMWARE(IWL7265_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c new file mode 100644 index 00000000000..51c41531d81 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -0,0 +1,135 @@ +/****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#include <linux/module.h> +#include <linux/stringify.h> +#include "iwl-config.h" +#include "iwl-agn-hw.h" + +/* Highest firmware API version supported */ +#define IWL8000_UCODE_API_MAX 8 + +/* Oldest version we won't warn about */ +#define IWL8000_UCODE_API_OK 8 + +/* Lowest firmware API version supported */ +#define IWL8000_UCODE_API_MIN 8 + +/* NVM versions */ +#define IWL8000_NVM_VERSION 0x0a1d +#define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */ + +#define IWL8000_FW_PRE "iwlwifi-8000-" +#define IWL8000_MODULE_FIRMWARE(api) IWL8000_FW_PRE __stringify(api) ".ucode" + +#define NVM_HW_SECTION_NUM_FAMILY_8000 10 +#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000.bin" + +static const struct iwl_base_params iwl8000_base_params = { + .eeprom_size = OTP_LOW_IMAGE_SIZE_FAMILY_8000, + .num_of_queues = IWLAGN_NUM_QUEUES, + .pll_cfg_val = 0, + .shadow_ram_support = true, + .led_compensation = 57, + .wd_timeout = IWL_LONG_WD_TIMEOUT, + .max_event_log_size = 512, + .shadow_reg_enable = true, + .pcie_l1_allowed = true, +}; + +static const struct iwl_ht_params iwl8000_ht_params = { + .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), +}; + +#define IWL_DEVICE_8000 \ + .ucode_api_max = IWL8000_UCODE_API_MAX, \ + .ucode_api_ok = IWL8000_UCODE_API_OK, \ + .ucode_api_min = IWL8000_UCODE_API_MIN, \ + .device_family = IWL_DEVICE_FAMILY_8000, \ + .max_inst_size = IWL60_RTC_INST_SIZE, \ + .max_data_size = IWL60_RTC_DATA_SIZE, \ + .base_params = &iwl8000_base_params, \ + .led_mode = IWL_LED_RF_STATE, \ + .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000 + +const struct iwl_cfg iwl8260_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 8260", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, +}; + +const struct iwl_cfg iwl8260_n_cfg = { + .name = "Intel(R) Dual Band Wireless-AC 8260", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, +}; + +MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h index 6d73f943cef..04a483d3865 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -102,9 +102,7 @@ /* EEPROM */ #define IWLAGN_EEPROM_IMG_SIZE 2048 -/* OTP */ -/* lower blocks contain EEPROM image and calibration data */ -#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ + /* high blocks contain PAPD data */ #define OTP_HIGH_IMAGE_SIZE_6x00 (6 * 512 * sizeof(u16)) /* 6 KB */ #define OTP_HIGH_IMAGE_SIZE_1000 (0x200 * sizeof(u16)) /* 1024 bytes */ diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index b03c25e1490..b7047905f41 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -84,6 +84,7 @@ enum iwl_device_family { IWL_DEVICE_FAMILY_6050, IWL_DEVICE_FAMILY_6150, IWL_DEVICE_FAMILY_7000, + IWL_DEVICE_FAMILY_8000, }; /* @@ -129,6 +130,12 @@ enum iwl_led_mode { #define ANT_BC (ANT_B | ANT_C) #define ANT_ABC (ANT_A | ANT_B | ANT_C) +static inline u8 num_of_ant(u8 mask) +{ + return !!((mask) & ANT_A) + + !!((mask) & ANT_B) + + !!((mask) & ANT_C); +} /* * @max_ll_items: max number of OTP blocks @@ -139,6 +146,9 @@ enum iwl_led_mode { * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadow register support + * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command + * is in flight. This is due to a HW bug in 7260, 3160 and 7265. + * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled. */ struct iwl_base_params { int eeprom_size; @@ -153,15 +163,19 @@ struct iwl_base_params { u32 max_event_log_size; const bool shadow_reg_enable; const bool pcie_l1_allowed; + const bool apmg_wake_up_wa; + const bool scd_chain_ext_wa; }; /* + * @stbc: support Tx STBC and 1*SS Rx STBC * @use_rts_for_aggregation: use rts/cts protection for HT traffic * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40 */ struct iwl_ht_params { enum ieee80211_smps_mode smps_mode; const bool ht_greenfield_support; /* if used set to true */ + const bool stbc; bool use_rts_for_aggregation; u8 ht40_bands; }; @@ -179,11 +193,25 @@ struct iwl_ht_params { #define EEPROM_6000_REG_BAND_24_HT40_CHANNELS 0x80 #define EEPROM_REGULATORY_BAND_NO_HT40 0 +/* lower blocks contain EEPROM image and calibration data */ +#define OTP_LOW_IMAGE_SIZE (2 * 512 * sizeof(u16)) /* 2 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(u16)) /* 16 KB */ +#define OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(u16)) /* 32 KB */ + struct iwl_eeprom_params { const u8 regulatory_bands[7]; bool enhanced_txpower; }; +/* Tx-backoff power threshold + * @pwr: The power limit in mw + * @backoff: The tx-backoff in uSec + */ +struct iwl_pwr_tx_backoff { + u32 pwr; + u32 backoff; +}; + /** * struct iwl_cfg * @name: Offical name of the device @@ -207,6 +235,11 @@ struct iwl_eeprom_params { * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device * @high_temp: Is this NIC is designated to be in high temperature. + * @host_interrupt_operation_mode: device needs host interrupt operation + * mode set + * @d0i3: device uses d0i3 instead of d3 + * @nvm_hw_section_num: the ID of the HW NVM section + * @pwr_tx_backoffs: translation table between power limits and backoffs * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -235,7 +268,14 @@ struct iwl_cfg { enum iwl_led_mode led_mode; const bool rx_with_siso_diversity; const bool internal_wimax_coex; + const bool host_interrupt_operation_mode; bool high_temp; + bool d0i3; + u8 nvm_hw_section_num; + bool lp_xtal_workaround; + const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; + bool no_power_up_nic_in_init; + const char *default_nvm_file; }; /* @@ -293,6 +333,11 @@ extern const struct iwl_cfg iwl7260_n_cfg; extern const struct iwl_cfg iwl3160_2ac_cfg; extern const struct iwl_cfg iwl3160_2n_cfg; extern const struct iwl_cfg iwl3160_n_cfg; +extern const struct iwl_cfg iwl7265_2ac_cfg; +extern const struct iwl_cfg iwl7265_2n_cfg; +extern const struct iwl_cfg iwl7265_n_cfg; +extern const struct iwl_cfg iwl8260_2ac_cfg; +extern const struct iwl_cfg iwl8260_n_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index a276af476e2..fe129c94ae3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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*/ @@ -198,7 +206,8 @@ CSR_INT_BIT_RF_KILL | \ CSR_INT_BIT_SW_RX | \ CSR_INT_BIT_WAKEUP | \ - CSR_INT_BIT_ALIVE) + CSR_INT_BIT_ALIVE | \ + CSR_INT_BIT_RX_PERIODIC) /* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ #define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ @@ -239,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. @@ -270,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) @@ -395,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 @@ -463,14 +502,11 @@ * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit * * default interrupt coalescing timer is 64 x 32 = 2048 usecs - * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs */ #define IWL_HOST_INT_TIMEOUT_MAX (0xFF) #define IWL_HOST_INT_TIMEOUT_DEF (0x40) #define IWL_HOST_INT_TIMEOUT_MIN (0x0) -#define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) -#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) -#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) +#define IWL_HOST_INT_OPER_MODE BIT(31) /***************************************************************************** * 7000/3000 series SHR DTS addresses * diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.c b/drivers/net/wireless/iwlwifi/iwl-debug.c index 8a44f594528..09feff4fa22 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.c +++ b/drivers/net/wireless/iwlwifi/iwl-debug.c @@ -61,8 +61,6 @@ * *****************************************************************************/ -#define DEBUG - #include <linux/device.h> #include <linux/interrupt.h> #include <linux/export.h> @@ -128,8 +126,8 @@ void __iwl_dbg(struct device *dev, #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_have_debug_level(level) && (!limit || net_ratelimit())) - dev_dbg(dev, "%c %s %pV", in_interrupt() ? 'I' : 'U', - function, &vaf); + dev_printk(KERN_DEBUG, dev, "%c %s %pV", + in_interrupt() ? 'I' : 'U', function, &vaf); #endif trace_iwlwifi_dbg(level, in_interrupt(), function, &vaf); va_end(args); diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index b2bb32a781d..295083510e7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * @@ -47,12 +47,32 @@ void __iwl_warn(struct device *dev, const char *fmt, ...) __printf(2, 3); void __iwl_info(struct device *dev, const char *fmt, ...) __printf(2, 3); void __iwl_crit(struct device *dev, const char *fmt, ...) __printf(2, 3); +/* not all compilers can evaluate strlen() at compile time, so use sizeof() */ +#define CHECK_FOR_NEWLINE(f) BUILD_BUG_ON(f[sizeof(f) - 2] != '\n') + /* No matter what is m (priv, bus, trans), this will work */ -#define IWL_ERR(m, f, a...) __iwl_err((m)->dev, false, false, f, ## a) -#define IWL_ERR_DEV(d, f, a...) __iwl_err((d), false, false, f, ## a) -#define IWL_WARN(m, f, a...) __iwl_warn((m)->dev, f, ## a) -#define IWL_INFO(m, f, a...) __iwl_info((m)->dev, f, ## a) -#define IWL_CRIT(m, f, a...) __iwl_crit((m)->dev, f, ## a) +#define IWL_ERR_DEV(d, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_err((d), false, false, f, ## a); \ + } while (0) +#define IWL_ERR(m, f, a...) \ + IWL_ERR_DEV((m)->dev, f, ## a) +#define IWL_WARN(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_warn((m)->dev, f, ## a); \ + } while (0) +#define IWL_INFO(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_info((m)->dev, f, ## a); \ + } while (0) +#define IWL_CRIT(m, f, a...) \ + do { \ + CHECK_FOR_NEWLINE(f); \ + __iwl_crit((m)->dev, f, ## a); \ + } while (0) #if defined(CONFIG_IWLWIFI_DEBUG) || defined(CONFIG_IWLWIFI_DEVICE_TRACING) void __iwl_dbg(struct device *dev, @@ -72,12 +92,17 @@ do { \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) +#define __IWL_DEBUG_DEV(dev, level, limit, fmt, args...) \ + do { \ + CHECK_FOR_NEWLINE(fmt); \ + __iwl_dbg(dev, level, limit, __func__, fmt, ##args); \ + } while (0) #define IWL_DEBUG(m, level, fmt, args...) \ - __iwl_dbg((m)->dev, level, false, __func__, fmt, ##args) + __IWL_DEBUG_DEV((m)->dev, level, false, fmt, ##args) #define IWL_DEBUG_DEV(dev, level, fmt, args...) \ - __iwl_dbg((dev), level, false, __func__, fmt, ##args) + __IWL_DEBUG_DEV(dev, level, false, fmt, ##args) #define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ - __iwl_dbg((m)->dev, level, true, __func__, fmt, ##args) + __IWL_DEBUG_DEV((m)->dev, level, true, fmt, ##args) #ifdef CONFIG_IWLWIFI_DEBUG #define iwl_print_hex_dump(m, level, p, len) \ @@ -126,6 +151,7 @@ do { \ /* 0x00000F00 - 0x00000100 */ #define IWL_DL_POWER 0x00000100 #define IWL_DL_TEMP 0x00000200 +#define IWL_DL_RPM 0x00000400 #define IWL_DL_SCAN 0x00000800 /* 0x0000F000 - 0x00001000 */ #define IWL_DL_ASSOC 0x00001000 @@ -189,5 +215,6 @@ do { \ #define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a) #define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) #define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) +#define IWL_DEBUG_RPM(p, f, a...) IWL_DEBUG(p, IWL_DL_RPM, f, ## a) #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c index 8f61c717f61..23e7351e02d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2009 - 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 diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 684c416d349..78bd41bf34b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2009 - 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 diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 99e1da3123c..f2a5c12269a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -128,7 +128,7 @@ struct iwl_drv { const struct iwl_cfg *cfg; int fw_index; /* firmware we're trying to load */ - char firmware_name[25]; /* name of firmware file to load */ + char firmware_name[32]; /* name of firmware file to load */ struct completion request_firmware_complete; @@ -237,7 +237,8 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) return -ENOENT; } - sprintf(drv->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); + snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s.ucode", + name_pre, tag); IWL_DEBUG_INFO(drv, "attempting to load firmware %s'%s'\n", (drv->fw_index == UCODE_EXPERIMENTAL_INDEX) @@ -322,6 +323,41 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces, pieces->img[type].sec[sec].offset = offset; } +static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) +{ + int i, j; + struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data; + struct iwl_fw_cipher_scheme *fwcs; + struct ieee80211_cipher_scheme *cs; + u32 cipher; + + if (len < sizeof(*l) || + len < sizeof(l->size) + l->size * sizeof(l->cs[0])) + return -EINVAL; + + for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) { + fwcs = &l->cs[j]; + cipher = le32_to_cpu(fwcs->cipher); + + /* we skip schemes with zero cipher suite selector */ + if (!cipher) + continue; + + cs = &fw->cs[j++]; + cs->cipher = cipher; + cs->iftype = BIT(NL80211_IFTYPE_STATION); + cs->hdr_len = fwcs->hdr_len; + cs->pn_len = fwcs->pn_len; + cs->pn_off = fwcs->pn_off; + cs->key_idx_off = fwcs->key_idx_off; + cs->key_idx_mask = fwcs->key_idx_mask; + cs->key_idx_shift = fwcs->key_idx_shift; + cs->mic_len = fwcs->mic_len; + } + + return 0; +} + /* * Gets uCode section from tlv. */ @@ -368,6 +404,38 @@ static int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) return 0; } +static int iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, + struct iwl_ucode_capabilities *capa) +{ + const struct iwl_ucode_api *ucode_api = (void *)data; + u32 api_index = le32_to_cpu(ucode_api->api_index); + + if (api_index >= IWL_API_ARRAY_SIZE) { + IWL_ERR(drv, "api_index larger than supported by driver\n"); + return -EINVAL; + } + + capa->api[api_index] = le32_to_cpu(ucode_api->api_flags); + + return 0; +} + +static int iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, + struct iwl_ucode_capabilities *capa) +{ + const struct iwl_ucode_capa *ucode_capa = (void *)data; + u32 api_index = le32_to_cpu(ucode_capa->api_index); + + if (api_index >= IWL_CAPABILITIES_ARRAY_SIZE) { + IWL_ERR(drv, "api_index larger than supported by driver\n"); + return -EINVAL; + } + + capa->capa[api_index] = le32_to_cpu(ucode_capa->api_capa); + + return 0; +} + static int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, const struct firmware *ucode_raw, struct iwl_firmware_pieces *pieces) @@ -483,6 +551,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, const u8 *tlv_data; char buildstr[25]; u32 build; + int num_of_cpus; if (len < sizeof(*ucode)) { IWL_ERR(drv, "uCode has invalid length: %zd\n", len); @@ -601,6 +670,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, */ capa->flags = le32_to_cpup((__le32 *)tlv_data); break; + case IWL_UCODE_TLV_API_CHANGES_SET: + if (tlv_len != sizeof(struct iwl_ucode_api)) + goto invalid_tlv_len; + if (iwl_set_ucode_api_flags(drv, tlv_data, capa)) + goto tlv_error; + break; + case IWL_UCODE_TLV_ENABLED_CAPABILITIES: + if (tlv_len != sizeof(struct iwl_ucode_capa)) + goto invalid_tlv_len; + if (iwl_set_ucode_capabilities(drv, tlv_data, capa)) + goto tlv_error; + break; case IWL_UCODE_TLV_INIT_EVTLOG_PTR: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; @@ -691,6 +772,52 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(u32)) goto invalid_tlv_len; drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); + drv->fw.valid_tx_ant = (drv->fw.phy_config & + FW_PHY_CFG_TX_CHAIN) >> + FW_PHY_CFG_TX_CHAIN_POS; + drv->fw.valid_rx_ant = (drv->fw.phy_config & + FW_PHY_CFG_RX_CHAIN) >> + FW_PHY_CFG_RX_CHAIN_POS; + break; + case IWL_UCODE_TLV_SECURE_SEC_RT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, + tlv_len); + drv->fw.mvm_fw = true; + drv->fw.img[IWL_UCODE_REGULAR].is_secure = true; + break; + case IWL_UCODE_TLV_SECURE_SEC_INIT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, + tlv_len); + drv->fw.mvm_fw = true; + drv->fw.img[IWL_UCODE_INIT].is_secure = true; + break; + case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, + tlv_len); + drv->fw.mvm_fw = true; + drv->fw.img[IWL_UCODE_WOWLAN].is_secure = true; + break; + case IWL_UCODE_TLV_NUM_OF_CPU: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + num_of_cpus = + le32_to_cpup((__le32 *)tlv_data); + + if (num_of_cpus == 2) { + drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus = + true; + drv->fw.img[IWL_UCODE_INIT].is_dual_cpus = + true; + drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus = + true; + } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { + IWL_ERR(drv, "Driver support upto 2 CPUs\n"); + return -EINVAL; + } + break; + case IWL_UCODE_TLV_CSCHEME: + if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) + goto invalid_tlv_len; break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); @@ -1116,6 +1243,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, .wd_disable = true, + .uapsd_disable = false, /* the rest are 0 by default */ }; IWL_EXPORT_SYMBOL(iwlwifi_mod_params); @@ -1210,7 +1338,7 @@ module_param_named(swcrypto, iwlwifi_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, S_IRUGO); MODULE_PARM_DESC(11n_disable, - "disable 11n functionality, bitmap: 1: full, 2: agg TX, 4: agg RX"); + "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX"); module_param_named(amsdu_size_8K, iwlwifi_mod_params.amsdu_size_8K, int, S_IRUGO); MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size (default 0)"); @@ -1224,12 +1352,15 @@ MODULE_PARM_DESC(antenna_coupling, module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO); MODULE_PARM_DESC(wd_disable, - "Disable stuck queue watchdog timer 0=system default, " - "1=disable, 2=enable (default: 0)"); + "Disable stuck queue watchdog timer 0=system default, 1=disable (default: 1)"); module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); MODULE_PARM_DESC(nvm_file, "NVM file name"); +module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, + bool, S_IRUGO); +MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)"); + /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index 429337a2b9a..3c72cb710b0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,9 +67,23 @@ /* for all modules */ #define DRV_NAME "iwlwifi" #define IWLWIFI_VERSION "in-tree:" -#define DRV_COPYRIGHT "Copyright(c) 2003-2013 Intel Corporation" +#define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" +/* radio config bits (actual values from NVM definition) */ +#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ +#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + +#define NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(x) (x & 0xF) +#define NVM_RF_CFG_DASH_MSK_FAMILY_8000(x) ((x >> 4) & 0xF) +#define NVM_RF_CFG_STEP_MSK_FAMILY_8000(x) ((x >> 8) & 0xF) +#define NVM_RF_CFG_TYPE_MSK_FAMILY_8000(x) ((x >> 12) & 0xFFF) +#define NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(x) ((x >> 24) & 0xF) +#define NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(x) ((x >> 28) & 0xF) /** * DOC: Driver system flows - drv component diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index 4c887f36590..c44cf114964 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -614,10 +614,10 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel->flags = IEEE80211_CHAN_NO_HT40; if (!(eeprom_ch->flags & EEPROM_CHANNEL_IBSS)) - channel->flags |= IEEE80211_CHAN_NO_IBSS; + channel->flags |= IEEE80211_CHAN_NO_IR; if (!(eeprom_ch->flags & EEPROM_CHANNEL_ACTIVE)) - channel->flags |= IEEE80211_CHAN_PASSIVE_SCAN; + channel->flags |= IEEE80211_CHAN_NO_IR; if (eeprom_ch->flags & EEPROM_CHANNEL_RADAR) channel->flags |= IEEE80211_CHAN_RADAR; @@ -751,6 +751,13 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, ht_info->ht_supported = true; ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; + if (cfg->ht_params->stbc) { + ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + + if (tx_chains > 1) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + } + if (iwlwifi_mod_params.amsdu_size_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h index d73304a23ec..f0548b8a64b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,16 +81,17 @@ struct iwl_nvm_data { bool sku_cap_band_24GHz_enable; bool sku_cap_band_52GHz_enable; bool sku_cap_11n_enable; + bool sku_cap_11ac_enable; bool sku_cap_amt_enable; bool sku_cap_ipan_enable; - u8 radio_cfg_type; + u16 radio_cfg_type; u8 radio_cfg_step; u8 radio_cfg_dash; u8 radio_cfg_pnum; u8 valid_tx_ant, valid_rx_ant; - u16 nvm_version; + u32 nvm_version; s8 max_tx_pwr_half_dbm; struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c index e5f2e362ab0..25d0105741d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h index 8e941f8bd7d..a6d3bdf82cd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-read.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 484d318245f..9564ae173d0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h new file mode 100644 index 00000000000..2953ffceda3 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -0,0 +1,134 @@ +/****************************************************************************** + * + * 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: + * @IWL_FW_ERROR_DUMP_RXF: + * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as + * &struct iwl_fw_error_dump_txcmd packets + */ +enum iwl_fw_error_dump_type { + IWL_FW_ERROR_DUMP_SRAM = 0, + IWL_FW_ERROR_DUMP_REG = 1, + IWL_FW_ERROR_DUMP_RXF = 2, + IWL_FW_ERROR_DUMP_TXCMD = 3, + + 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; + +/** + * 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; + +/** + * struct iwl_fw_error_dump_txcmd - TX command data + * @cmdlen: original length of command + * @caplen: captured length of command (may be less) + * @data: captured command data, @caplen bytes + */ +struct iwl_fw_error_dump_txcmd { + __le32 cmdlen; + __le32 caplen; + u8 data[]; +} __packed; + +/** + * iwl_mvm_fw_error_next_data - advance fw error dump data pointer + * @data: previous data block + * Returns: next data block + */ +static inline struct iwl_fw_error_dump_data * +iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data) +{ + return (void *)(data->data + le32_to_cpu(data->len)); +} + +#endif /* __fw_error_dump_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 8b6c6fd95ed..b45e576a4b5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -121,6 +121,13 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_SEC_WOWLAN = 21, IWL_UCODE_TLV_DEF_CALIB = 22, IWL_UCODE_TLV_PHY_SKU = 23, + IWL_UCODE_TLV_SECURE_SEC_RT = 24, + IWL_UCODE_TLV_SECURE_SEC_INIT = 25, + IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26, + IWL_UCODE_TLV_NUM_OF_CPU = 27, + IWL_UCODE_TLV_CSCHEME = 28, + IWL_UCODE_TLV_API_CHANGES_SET = 29, + IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, }; struct iwl_ucode_tlv { @@ -153,4 +160,19 @@ struct iwl_tlv_ucode_header { u8 data[0]; }; +/* + * ucode TLVs + * + * ability to get extension for: flags & capabilities from ucode binaries files + */ +struct iwl_ucode_api { + __le32 api_index; + __le32 api_flags; +} __packed; + +struct iwl_ucode_capa { + __le32 api_index; + __le32 api_capa; +} __packed; + #endif /* __iwl_fw_file_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index a1223680bc7..b1a33322b9b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -74,12 +74,25 @@ * @IWL_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS - * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD - * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api - * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API. + * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: This uCode image supports uAPSD + * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan + * offload profile config command. * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six * (rather than two) IPv6 addresses - * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API + * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element + * from the probe request template. + * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) + * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) + * @IWL_UCODE_TLV_FLAGS_P2P_PM: P2P client supports PM as a stand alone MAC + * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_DCM: support power save on BSS station and + * P2P client interfaces simultaneously if they are in different bindings. + * @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and + * P2P client interfaces simultaneously if they are in same bindings. + * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD + * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save + * @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering. + * @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients + * @IWL_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS. */ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_PAN = BIT(0), @@ -87,11 +100,37 @@ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_MFP = BIT(2), IWL_UCODE_TLV_FLAGS_P2P = BIT(3), IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), - IWL_UCODE_TLV_FLAGS_UAPSD = BIT(6), - IWL_UCODE_TLV_FLAGS_RX_ENERGY_API = BIT(8), - IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2 = BIT(9), + IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7), IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), - IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11), + IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), + IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15), + IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16), + IWL_UCODE_TLV_FLAGS_P2P_PM = BIT(21), + IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = BIT(22), + IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = BIT(23), + IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), + IWL_UCODE_TLV_FLAGS_EBS_SUPPORT = BIT(25), + IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), + IWL_UCODE_TLV_FLAGS_BCAST_FILTERING = BIT(29), + 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. + * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. + */ +enum iwl_ucode_tlv_api { + IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), + IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), +}; + +/** + * 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 */ @@ -133,12 +172,17 @@ enum iwl_ucode_sec { * For 16.0 uCode and above, there is no differentiation between sections, * just an offset to the HW address. */ -#define IWL_UCODE_SECTION_MAX 4 +#define IWL_UCODE_SECTION_MAX 12 +#define IWL_API_ARRAY_SIZE 1 +#define IWL_CAPABILITIES_ARRAY_SIZE 1 +#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC struct iwl_ucode_capabilities { u32 max_probe_length; u32 standard_phy_calibration_size; u32 flags; + u32 api[IWL_API_ARRAY_SIZE]; + u32 capa[IWL_CAPABILITIES_ARRAY_SIZE]; }; /* one for each uCode image (inst/data, init/runtime/wowlan) */ @@ -150,6 +194,13 @@ struct fw_desc { struct fw_img { struct fw_desc sec[IWL_UCODE_SECTION_MAX]; + bool is_secure; + bool is_dual_cpus; +}; + +struct iwl_sf_region { + u32 addr; + u32 size; }; /* uCode version contains 4 values: Major/Minor/API/Serial */ @@ -184,6 +235,44 @@ enum iwl_fw_phy_cfg { FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS, }; +#define IWL_UCODE_MAX_CS 1 + +/** + * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW. + * @cipher: a cipher suite selector + * @flags: cipher scheme flags (currently reserved for a future use) + * @hdr_len: a size of MPDU security header + * @pn_len: a size of PN + * @pn_off: an offset of pn from the beginning of the security header + * @key_idx_off: an offset of key index byte in the security header + * @key_idx_mask: a bit mask of key_idx bits + * @key_idx_shift: bit shift needed to get key_idx + * @mic_len: mic length in bytes + * @hw_cipher: a HW cipher index used in host commands + */ +struct iwl_fw_cipher_scheme { + __le32 cipher; + u8 flags; + u8 hdr_len; + u8 pn_len; + u8 pn_off; + u8 key_idx_off; + u8 key_idx_mask; + u8 key_idx_shift; + u8 mic_len; + u8 hw_cipher; +} __packed; + +/** + * struct iwl_fw_cscheme_list - a cipher scheme list + * @size: a number of entries + * @cs: cipher scheme entries + */ +struct iwl_fw_cscheme_list { + u8 size; + struct iwl_fw_cipher_scheme cs[]; +} __packed; + /** * struct iwl_fw - variables associated with the firmware * @@ -199,6 +288,7 @@ enum iwl_fw_phy_cfg { * @inst_evtlog_size: event log size for runtime ucode. * @inst_errlog_ptr: error log offfset for runtime ucode. * @mvm_fw: indicates this is MVM firmware + * @cipher_scheme: optional external cipher scheme. */ struct iwl_fw { u32 ucode_ver; @@ -216,20 +306,12 @@ struct iwl_fw { struct iwl_tlv_calib_ctrl default_calib[IWL_UCODE_TYPE_MAX]; u32 phy_config; + u8 valid_tx_ant; + u8 valid_rx_ant; bool mvm_fw; -}; -static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw) -{ - return (fw->phy_config & FW_PHY_CFG_TX_CHAIN) >> - FW_PHY_CFG_TX_CHAIN_POS; -} - -static inline u8 iwl_fw_valid_rx_ant(const struct iwl_fw *fw) -{ - return (fw->phy_config & FW_PHY_CFG_RX_CHAIN) >> - FW_PHY_CFG_RX_CHAIN_POS; -} + struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; +}; #endif /* __iwl_fw_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index dfa4d2e3aaa..5eef4ae7333 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * @@ -33,8 +33,8 @@ #include "iwl-io.h" #include "iwl-csr.h" #include "iwl-debug.h" +#include "iwl-prph.h" #include "iwl-fh.h" -#include "iwl-csr.h" #define IWL_POLL_INTERVAL 10 /* microseconds */ @@ -94,14 +94,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); @@ -131,6 +131,21 @@ void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) } IWL_EXPORT_SYMBOL(iwl_write_prph); +int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int t = 0; + + do { + if ((iwl_read_prph(trans, addr) & mask) == (bits & mask)) + return t; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} + void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) { unsigned long flags; @@ -169,6 +184,23 @@ void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) } IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); +void iwl_force_nmi(struct iwl_trans *trans) +{ + /* + * In HW previous to the 8000 HW family, and in the 8000 HW family + * itself when the revision step==0, the DEVICE_SET_NMI_REG is used + * to force an NMI. Otherwise, a different register - + * DEVICE_SET_NMI_8000B_REG - is used. + */ + if ((trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) || + ((trans->hw_rev & 0xc) == 0x0)) + iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL); + else + iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG, + DEVICE_SET_NMI_8000B_VAL); +} +IWL_EXPORT_SYMBOL(iwl_force_nmi); + static const char *get_fh_string(int cmd) { #define IWL_CMD(x) case x: return #x diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index 63d10ec08db..705d12c079e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project. * @@ -70,12 +70,17 @@ 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); void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, u32 bits, u32 mask); void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); +void iwl_force_nmi(struct iwl_trans *trans); /* Error handling */ int iwl_dump_fh(struct iwl_trans *trans, char **buf); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index a1f580c0c6c..d051857729a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -79,9 +79,12 @@ enum iwl_power_level { IWL_POWER_NUM }; -#define IWL_DISABLE_HT_ALL BIT(0) -#define IWL_DISABLE_HT_TXAGG BIT(1) -#define IWL_DISABLE_HT_RXAGG BIT(2) +enum iwl_disable_11n { + IWL_DISABLE_HT_ALL = BIT(0), + IWL_DISABLE_HT_TXAGG = BIT(1), + IWL_DISABLE_HT_RXAGG = BIT(2), + IWL_ENABLE_HT_TXAGG = BIT(3), +}; /** * struct iwl_mod_params @@ -90,10 +93,10 @@ enum iwl_power_level { * * @sw_crypto: using hardware encryption, default = 0 * @disable_11n: disable 11n capabilities, default = 0, - * use IWL_DISABLE_HT_* constants + * use IWL_[DIS,EN]ABLE_HT_* constants * @amsdu_size_8K: enable 8K amsdu size, default = 0 * @restart_fw: restart firmware, default = 1 - * @wd_disable: enable stuck queue check, default = 0 + * @wd_disable: disable stuck queue check, default = 1 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 * @power_save: disable power save, default = false @@ -116,6 +119,7 @@ struct iwl_mod_params { #endif int ant_coupling; char *nvm_file; + bool uapsd_disable; }; #endif /* #__iwl_modparams_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c index 940b8a9d528..b5bc959b1df 100644 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.c +++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h index 2e2f1c8c99f..95af97a6c2c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-notif-wait.h +++ b/drivers/net/wireless/iwlwifi/iwl-notif-wait.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index b76a9a8fc0b..85eee79c495 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -62,6 +62,7 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/etherdevice.h> #include "iwl-drv.h" #include "iwl-modparams.h" #include "iwl-nvm-parse.h" @@ -71,7 +72,7 @@ enum wkp_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ HW_ADDR = 0x15, -/* NVM SW-Section offset (in words) definitions */ + /* NVM SW-Section offset (in words) definitions */ NVM_SW_SECTION = 0x1C0, NVM_VERSION = 0, RADIO_CFG = 1, @@ -79,11 +80,32 @@ enum wkp_nvm_offsets { N_HW_ADDRS = 3, NVM_CHANNELS = 0x1E0 - NVM_SW_SECTION, -/* NVM calibration section offset (in words) definitions */ + /* NVM calibration section offset (in words) definitions */ NVM_CALIB_SECTION = 0x2B8, XTAL_CALIB = 0x316 - NVM_CALIB_SECTION }; +enum family_8000_nvm_offsets { + /* NVM HW-Section offset (in words) definitions */ + HW_ADDR0_FAMILY_8000 = 0x12, + HW_ADDR1_FAMILY_8000 = 0x16, + MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1, + + /* NVM SW-Section offset (in words) definitions */ + NVM_SW_SECTION_FAMILY_8000 = 0x1C0, + NVM_VERSION_FAMILY_8000 = 0, + RADIO_CFG_FAMILY_8000 = 2, + SKU_FAMILY_8000 = 4, + N_HW_ADDRS_FAMILY_8000 = 5, + + /* NVM REGULATORY -Section offset (in words) definitions */ + NVM_CHANNELS_FAMILY_8000 = 0, + + /* NVM calibration section offset (in words) definitions */ + NVM_CALIB_SECTION_FAMILY_8000 = 0x2B8, + XTAL_CALIB_FAMILY_8000 = 0x316 - NVM_CALIB_SECTION_FAMILY_8000 +}; + /* SKU Capabilities (actual values from NVM definition) */ enum nvm_sku_bits { NVM_SKU_CAP_BAND_24GHZ = BIT(0), @@ -92,14 +114,6 @@ enum nvm_sku_bits { NVM_SKU_CAP_11AC_ENABLE = BIT(3), }; -/* radio config bits (actual values from NVM definition) */ -#define NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ -#define NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ -#define NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ -#define NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ -#define NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ -#define NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ - /* * These are the channel numbers in the order that they are stored in the NVM */ @@ -112,11 +126,22 @@ static const u8 iwl_nvm_channels[] = { 149, 153, 157, 161, 165 }; -#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) -#define NUM_2GHZ_CHANNELS 14 -#define FIRST_2GHZ_HT_MINUS 5 -#define LAST_2GHZ_HT_PLUS 9 -#define LAST_5GHZ_HT 161 +static const u8 iwl_nvm_channels_family_8000[] = { + /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 5 GHz */ + 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, + 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, + 149, 153, 157, 161, 165, 169, 173, 177, 181 +}; + +#define IWL_NUM_CHANNELS ARRAY_SIZE(iwl_nvm_channels) +#define IWL_NUM_CHANNELS_FAMILY_8000 ARRAY_SIZE(iwl_nvm_channels_family_8000) +#define NUM_2GHZ_CHANNELS 14 +#define NUM_2GHZ_CHANNELS_FAMILY_8000 14 +#define FIRST_2GHZ_HT_MINUS 5 +#define LAST_2GHZ_HT_PLUS 9 +#define LAST_5GHZ_HT 161 #define DEFAULT_MAX_TX_POWER 16 @@ -179,15 +204,32 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, struct ieee80211_channel *channel; u16 ch_flags; bool is_5ghz; + int num_of_ch, num_2ghz_channels; + const u8 *nvm_chan; + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + num_of_ch = IWL_NUM_CHANNELS; + nvm_chan = &iwl_nvm_channels[0]; + num_2ghz_channels = NUM_2GHZ_CHANNELS; + } else { + num_of_ch = IWL_NUM_CHANNELS_FAMILY_8000; + nvm_chan = &iwl_nvm_channels_family_8000[0]; + num_2ghz_channels = NUM_2GHZ_CHANNELS_FAMILY_8000; + } - for (ch_idx = 0; ch_idx < IWL_NUM_CHANNELS; ch_idx++) { + for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) { ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx); + + if (ch_idx >= num_2ghz_channels && + !data->sku_cap_band_52GHz_enable) + ch_flags &= ~NVM_CHANNEL_VALID; + if (!(ch_flags & NVM_CHANNEL_VALID)) { IWL_DEBUG_EEPROM(dev, "Ch. %d Flags %x [%sGHz] - No traffic\n", - iwl_nvm_channels[ch_idx], + nvm_chan[ch_idx], ch_flags, - (ch_idx >= NUM_2GHZ_CHANNELS) ? + (ch_idx >= num_2ghz_channels) ? "5.2" : "2.4"); continue; } @@ -195,8 +237,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel = &data->channels[n_channels]; n_channels++; - channel->hw_value = iwl_nvm_channels[ch_idx]; - channel->band = (ch_idx < NUM_2GHZ_CHANNELS) ? + channel->hw_value = nvm_chan[ch_idx]; + channel->band = (ch_idx < num_2ghz_channels) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; channel->center_freq = ieee80211_channel_to_frequency( @@ -204,15 +246,15 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, /* TODO: Need to be dependent to the NVM */ channel->flags = IEEE80211_CHAN_NO_HT40; - if (ch_idx < NUM_2GHZ_CHANNELS && + if (ch_idx < num_2ghz_channels && (ch_flags & NVM_CHANNEL_40MHZ)) { - if (iwl_nvm_channels[ch_idx] <= LAST_2GHZ_HT_PLUS) + if (nvm_chan[ch_idx] <= LAST_2GHZ_HT_PLUS) channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; - if (iwl_nvm_channels[ch_idx] >= FIRST_2GHZ_HT_MINUS) + if (nvm_chan[ch_idx] >= FIRST_2GHZ_HT_MINUS) channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; - } else if (iwl_nvm_channels[ch_idx] <= LAST_5GHZ_HT && + } else if (nvm_chan[ch_idx] <= LAST_5GHZ_HT && (ch_flags & NVM_CHANNEL_40MHZ)) { - if ((ch_idx - NUM_2GHZ_CHANNELS) % 2 == 0) + if ((ch_idx - num_2ghz_channels) % 2 == 0) channel->flags &= ~IEEE80211_CHAN_NO_HT40PLUS; else channel->flags &= ~IEEE80211_CHAN_NO_HT40MINUS; @@ -223,10 +265,10 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, channel->flags |= IEEE80211_CHAN_NO_160MHZ; if (!(ch_flags & NVM_CHANNEL_IBSS)) - channel->flags |= IEEE80211_CHAN_NO_IBSS; + channel->flags |= IEEE80211_CHAN_NO_IR; if (!(ch_flags & NVM_CHANNEL_ACTIVE)) - channel->flags |= IEEE80211_CHAN_PASSIVE_SCAN; + channel->flags |= IEEE80211_CHAN_NO_IR; if (ch_flags & NVM_CHANNEL_RADAR) channel->flags |= IEEE80211_CHAN_RADAR; @@ -261,15 +303,25 @@ 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_rx_ants = num_of_ant(rx_chains); + int num_tx_ants = num_of_ant(tx_chains); + vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | IEEE80211_VHT_CAP_RXSTBC_1 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + 3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + 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; @@ -283,9 +335,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 (data->valid_rx_ant == 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); @@ -295,14 +346,23 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, - struct iwl_nvm_data *data, const __le16 *nvm_sw, - bool enable_vht, u8 tx_chains, u8 rx_chains) + struct iwl_nvm_data *data, + const __le16 *ch_section, bool enable_vht, + u8 tx_chains, u8 rx_chains) { - int n_channels = iwl_init_channel_map(dev, cfg, data, - &nvm_sw[NVM_CHANNELS]); + int n_channels; int n_used = 0; struct ieee80211_supported_band *sband; + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + n_channels = iwl_init_channel_map( + dev, cfg, data, + &ch_section[NVM_CHANNELS]); + else + n_channels = iwl_init_channel_map( + dev, cfg, data, + &ch_section[NVM_CHANNELS_FAMILY_8000]); + sband = &data->bands[IEEE80211_BAND_2GHZ]; sband->band = IEEE80211_BAND_2GHZ; sband->bitrates = &iwl_cfg80211_rates[RATES_24_OFFS]; @@ -321,80 +381,189 @@ 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", n_used, n_channels); } +static int iwl_get_sku(const struct iwl_cfg *cfg, + const __le16 *nvm_sw) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + SKU); + else + return le32_to_cpup((__le32 *)(nvm_sw + SKU_FAMILY_8000)); +} + +static int iwl_get_nvm_version(const struct iwl_cfg *cfg, + const __le16 *nvm_sw) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + NVM_VERSION); + else + return le32_to_cpup((__le32 *)(nvm_sw + + NVM_VERSION_FAMILY_8000)); +} + +static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, + const __le16 *nvm_sw) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + RADIO_CFG); + else + return le32_to_cpup((__le32 *)(nvm_sw + RADIO_CFG_FAMILY_8000)); +} + +#define N_HW_ADDRS_MASK_FAMILY_8000 0xF +static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, + const __le16 *nvm_sw) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + N_HW_ADDRS); + else + return le32_to_cpup((__le32 *)(nvm_sw + N_HW_ADDRS_FAMILY_8000)) + & N_HW_ADDRS_MASK_FAMILY_8000; +} + +static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + u32 radio_cfg) +{ + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); + data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); + data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); + data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); + return; + } + + /* set the radio configuration for family 8000 */ + data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK_FAMILY_8000(radio_cfg); + data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg); + data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg); + data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg); +} + +static void iwl_set_hw_address(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *nvm_sec) +{ + const u8 *hw_addr = (const u8 *)(nvm_sec + HW_ADDR); + + /* The byte order is little endian 16 bit, meaning 214365 */ + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; +} + +static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg, + struct iwl_nvm_data *data, + const __le16 *mac_override, + const __le16 *nvm_hw) +{ + const u8 *hw_addr; + + if (mac_override) { + hw_addr = (const u8 *)(mac_override + + MAC_ADDRESS_OVERRIDE_FAMILY_8000); + + /* The byte order is little endian 16 bit, meaning 214365 */ + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; + + if (is_valid_ether_addr(hw_addr)) + return; + } + + /* take the MAC address from the OTP */ + hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000); + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000); + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; +} + struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains) + const __le16 *nvm_calib, const __le16 *regulatory, + const __le16 *mac_override, u8 tx_chains, u8 rx_chains) { struct iwl_nvm_data *data; - u8 hw_addr[ETH_ALEN]; - u16 radio_cfg, sku; - - data = kzalloc(sizeof(*data) + - sizeof(struct ieee80211_channel) * IWL_NUM_CHANNELS, - GFP_KERNEL); + u32 sku; + u32 radio_cfg; + + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) + data = kzalloc(sizeof(*data) + + sizeof(struct ieee80211_channel) * + IWL_NUM_CHANNELS, + GFP_KERNEL); + else + data = kzalloc(sizeof(*data) + + sizeof(struct ieee80211_channel) * + IWL_NUM_CHANNELS_FAMILY_8000, + GFP_KERNEL); if (!data) return NULL; - data->nvm_version = le16_to_cpup(nvm_sw + NVM_VERSION); + data->nvm_version = iwl_get_nvm_version(cfg, nvm_sw); - radio_cfg = le16_to_cpup(nvm_sw + RADIO_CFG); - data->radio_cfg_type = NVM_RF_CFG_TYPE_MSK(radio_cfg); - data->radio_cfg_step = NVM_RF_CFG_STEP_MSK(radio_cfg); - data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK(radio_cfg); - data->radio_cfg_pnum = NVM_RF_CFG_PNUM_MSK(radio_cfg); - data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK(radio_cfg); - data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK(radio_cfg); + radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw); + iwl_set_radio_cfg(cfg, data, radio_cfg); - sku = le16_to_cpup(nvm_sw + SKU); + sku = iwl_get_sku(cfg, nvm_sw); data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; data->sku_cap_band_52GHz_enable = sku & NVM_SKU_CAP_BAND_52GHZ; data->sku_cap_11n_enable = sku & NVM_SKU_CAP_11N_ENABLE; + data->sku_cap_11ac_enable = sku & NVM_SKU_CAP_11AC_ENABLE; if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) data->sku_cap_11n_enable = false; - /* check overrides (some devices have wrong NVM) */ - if (cfg->valid_tx_ant) - data->valid_tx_ant = cfg->valid_tx_ant; - if (cfg->valid_rx_ant) - data->valid_rx_ant = cfg->valid_rx_ant; + data->n_hw_addrs = iwl_get_n_hw_addrs(cfg, nvm_sw); - if (!data->valid_tx_ant || !data->valid_rx_ant) { - IWL_ERR_DEV(dev, "invalid antennas (0x%x, 0x%x)\n", - data->valid_tx_ant, data->valid_rx_ant); - kfree(data); - return NULL; + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + /* Checking for required sections */ + if (!nvm_calib) { + IWL_ERR_DEV(dev, + "Can't parse empty Calib NVM sections\n"); + kfree(data); + return NULL; + } + /* in family 8000 Xtal calibration values moved to OTP */ + data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); + data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); } - data->n_hw_addrs = le16_to_cpup(nvm_sw + N_HW_ADDRS); + if (cfg->device_family != IWL_DEVICE_FAMILY_8000) { + iwl_set_hw_address(cfg, data, nvm_hw); - data->xtal_calib[0] = *(nvm_calib + XTAL_CALIB); - data->xtal_calib[1] = *(nvm_calib + XTAL_CALIB + 1); + iwl_init_sbands(dev, cfg, data, nvm_sw, + sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, + rx_chains); + } else { + /* MAC address in family 8000 */ + iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw); - /* The byte order is little endian 16 bit, meaning 214365 */ - memcpy(hw_addr, nvm_hw + HW_ADDR, ETH_ALEN); - data->hw_addr[0] = hw_addr[1]; - data->hw_addr[1] = hw_addr[0]; - data->hw_addr[2] = hw_addr[3]; - data->hw_addr[3] = hw_addr[2]; - data->hw_addr[4] = hw_addr[5]; - data->hw_addr[5] = hw_addr[4]; - - iwl_init_sbands(dev, cfg, data, nvm_sw, sku & NVM_SKU_CAP_11AC_ENABLE, - tx_chains, rx_chains); + iwl_init_sbands(dev, cfg, data, regulatory, + sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains, + rx_chains); + } - data->calib_version = 255; /* TODO: - this value will prevent some checks from - failing, we need to check if this - field is still needed, and if it does, - where is it in the NVM*/ + data->calib_version = 255; return data; } diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h index 3325059c52d..c9c45a39d21 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -75,6 +75,7 @@ struct iwl_nvm_data * iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, const __le16 *nvm_hw, const __le16 *nvm_sw, - const __le16 *nvm_calib, u8 tx_chains, u8 rx_chains); + const __le16 *nvm_calib, const __le16 *regulatory, + const __le16 *mac_override, u8 tx_chains, u8 rx_chains); #endif /* __iwl_nvm_parse_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 976448a57d0..99785c892f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -63,6 +63,7 @@ #ifndef __iwl_op_mode_h__ #define __iwl_op_mode_h__ +#include <linux/netdevice.h> #include <linux/debugfs.h> struct iwl_op_mode; @@ -112,14 +113,18 @@ struct iwl_cfg; * @stop: stop the op_mode. Must free all the memory allocated. * May sleep * @rx: Rx notification to the op_mode. rxb is the Rx buffer itself. Cmd is the - * HCMD this Rx responds to. - * This callback may sleep, it is called from a threaded IRQ handler. + * HCMD this Rx responds to. Can't sleep. + * @napi_add: NAPI initialisation. The transport is fully responsible for NAPI, + * but the higher layers need to know about it (in particular mac80211 to + * to able to call the right NAPI RX functions); this function is needed + * to eventually call netif_napi_add() with higher layer involvement. * @queue_full: notifies that a HW queue is full. * Must be atomic and called with BH disabled. * @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. @@ -131,6 +136,8 @@ struct iwl_cfg; * @nic_config: configure NIC, called before firmware is started. * May sleep * @wimax_active: invoked when WiMax becomes active. May sleep + * @enter_d0i3: configure the fw to enter d0i3. May sleep. + * @exit_d0i3: configure the fw to exit d0i3. May sleep. */ struct iwl_op_mode_ops { struct iwl_op_mode *(*start)(struct iwl_trans *trans, @@ -140,14 +147,21 @@ struct iwl_op_mode_ops { void (*stop)(struct iwl_op_mode *op_mode); int (*rx)(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); + void (*napi_add)(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct net_device *napi_dev, + int (*poll)(struct napi_struct *, int), + int weight); 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); void (*nic_config)(struct iwl_op_mode *op_mode); void (*wimax_active)(struct iwl_op_mode *op_mode); + int (*enter_d0i3)(struct iwl_op_mode *op_mode); + int (*exit_d0i3)(struct iwl_op_mode *op_mode); }; int iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops); @@ -155,14 +169,12 @@ void iwl_opmode_deregister(const char *name); /** * struct iwl_op_mode - operational mode + * @ops: pointer to its own ops * * This holds an implementation of the mac80211 / fw API. - * - * @ops - pointer to its own ops */ struct iwl_op_mode { const struct iwl_op_mode_ops *ops; - const struct iwl_trans *trans; char op_mode_specific[0] __aligned(sizeof(void *)); }; @@ -177,7 +189,6 @@ static inline int iwl_op_mode_rx(struct iwl_op_mode *op_mode, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { - might_sleep(); return op_mode->ops->rx(op_mode, rxb, cmd); } @@ -193,11 +204,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, @@ -228,4 +239,33 @@ static inline void iwl_op_mode_wimax_active(struct iwl_op_mode *op_mode) op_mode->ops->wimax_active(op_mode); } +static inline int iwl_op_mode_enter_d0i3(struct iwl_op_mode *op_mode) +{ + might_sleep(); + + if (!op_mode->ops->enter_d0i3) + return 0; + return op_mode->ops->enter_d0i3(op_mode); +} + +static inline int iwl_op_mode_exit_d0i3(struct iwl_op_mode *op_mode) +{ + might_sleep(); + + if (!op_mode->ops->exit_d0i3) + return 0; + return op_mode->ops->exit_d0i3(op_mode); +} + +static inline void iwl_op_mode_napi_add(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct net_device *napi_dev, + int (*poll)(struct napi_struct *, int), + int weight) +{ + if (!op_mode->ops->napi_add) + return; + op_mode->ops->napi_add(op_mode, napi, napi_dev, poll, weight); +} + #endif /* __iwl_op_mode_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.c b/drivers/net/wireless/iwlwifi/iwl-phy-db.c index 1a405ae6a9c..d4fb5cad07e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.c +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -72,7 +72,7 @@ #include "iwl-trans.h" #define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */ -#define IWL_NUM_PAPD_CH_GROUPS 4 +#define IWL_NUM_PAPD_CH_GROUPS 7 #define IWL_NUM_TXP_CH_GROUPS 9 struct iwl_phy_db_entry { @@ -345,7 +345,6 @@ static int iwl_send_phy_db_cmd(struct iwl_phy_db *phy_db, u16 type, struct iwl_phy_db_cmd phy_db_cmd; struct iwl_host_cmd cmd = { .id = PHY_DB_CMD, - .flags = CMD_SYNC, }; IWL_DEBUG_INFO(phy_db->trans, @@ -383,7 +382,7 @@ static int iwl_phy_db_send_all_channel_groups( if (!entry) return -EINVAL; - if (WARN_ON_ONCE(!entry->size)) + if (!entry->size) continue; /* Send the requested PHY DB section */ @@ -393,13 +392,13 @@ static int iwl_phy_db_send_all_channel_groups( entry->data); if (err) { IWL_ERR(phy_db->trans, - "Can't SEND phy_db section %d (%d), err %d", + "Can't SEND phy_db section %d (%d), err %d\n", type, i, err); return err; } IWL_DEBUG_INFO(phy_db->trans, - "Sent PHY_DB HCMD, type = %d num = %d", + "Sent PHY_DB HCMD, type = %d num = %d\n", type, i); } @@ -451,7 +450,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) IWL_NUM_PAPD_CH_GROUPS); if (err) { IWL_ERR(phy_db->trans, - "Cannot send channel specific PAPD groups"); + "Cannot send channel specific PAPD groups\n"); return err; } @@ -461,7 +460,7 @@ int iwl_send_phy_db_data(struct iwl_phy_db *phy_db) IWL_NUM_TXP_CH_GROUPS); if (err) { IWL_ERR(phy_db->trans, - "Cannot send channel specific TX power groups"); + "Cannot send channel specific TX power groups\n"); return err; } diff --git a/drivers/net/wireless/iwlwifi/iwl-phy-db.h b/drivers/net/wireless/iwlwifi/iwl-phy-db.h index ce983af7964..9ee18d0d2d0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-phy-db.h +++ b/drivers/net/wireless/iwlwifi/iwl-phy-db.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index ff8cc75c189..4997e27672b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -95,11 +95,47 @@ #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) /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C +/* Device NMI register */ +#define DEVICE_SET_NMI_REG 0x00a01c30 +#define DEVICE_SET_NMI_VAL 0x1 +#define DEVICE_SET_NMI_8000B_REG 0x00a01c24 +#define DEVICE_SET_NMI_8000B_VAL 0x1000000 + +/* 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 +*/ +#define RELEASE_CPU_RESET (0x300C) +#define RELEASE_CPU_RESET_BIT BIT(24) + /***************************************************************************** * 7000/3000 series SHR DTS addresses * *****************************************************************************/ @@ -272,4 +308,55 @@ static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) /*********************** END TX SCHEDULER *************************************/ +/* Oscillator clock */ +#define OSC_CLK (0xa04068) +#define OSC_CLK_FORCE_CONTROL (0x8) + +/* SECURE boot registers */ +#define LMPM_SECURE_BOOT_CONFIG_ADDR (0x100) +enum secure_boot_config_reg { + LMPM_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001, + LMPM_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002, +}; + +#define LMPM_SECURE_BOOT_CPU1_STATUS_ADDR (0x1E30) +#define LMPM_SECURE_BOOT_CPU2_STATUS_ADDR (0x1E34) +enum secure_boot_status_reg { + LMPM_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000001, + LMPM_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002, + LMPM_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004, + LMPM_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008, + LMPM_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010, + LMPM_SECURE_BOOT_STATUS_SUCCESS = 0x00000003, +}; + +#define CSR_UCODE_LOAD_STATUS_ADDR (0x1E70) +enum secure_load_status_reg { + LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001, + LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003, + LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007, + LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, + LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, +}; + +#define LMPM_SECURE_INSPECTOR_CODE_ADDR (0x1E38) +#define LMPM_SECURE_INSPECTOR_DATA_ADDR (0x1E3C) +#define LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR (0x1E78) +#define LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR (0x1E7C) + +#define LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE (0x400000) +#define LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE (0x402000) +#define LMPM_SECURE_CPU1_HDR_MEM_SPACE (0x420000) +#define LMPM_SECURE_CPU2_HDR_MEM_SPACE (0x420400) + +#define LMPM_SECURE_TIME_OUT (100) + +/* Rx FIFO */ +#define RXF_SIZE_ADDR (0xa00c88) +#define RXF_SIZE_BYTE_CND_POS (7) +#define RXF_SIZE_BYTE_CNT_MSK (0x3ff << RXF_SIZE_BYTE_CND_POS) + +#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) +#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) + #endif /* __iwl_prph_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 80b47508647..34d49e171fb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -70,6 +70,7 @@ #include "iwl-debug.h" #include "iwl-config.h" #include "iwl-fw.h" +#include "iwl-op-mode.h" /** * DOC: Transport layer - what is it ? @@ -100,8 +101,7 @@ * start_fw * * 5) Then when finished (or reset): - * stop_fw (a.k.a. stop device for the moment) - * stop_hw + * stop_device * * 6) Eventually, the free function will be called. */ @@ -176,19 +176,38 @@ struct iwl_rx_packet { u8 data[]; } __packed; +static inline u32 iwl_rx_packet_len(const struct iwl_rx_packet *pkt) +{ + return le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; +} + +static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) +{ + return iwl_rx_packet_len(pkt) - sizeof(pkt->hdr); +} + /** * enum CMD_MODE - how to send the host commands ? * - * @CMD_SYNC: The caller will be stalled until the fw responds to the command * @CMD_ASYNC: Return right away and don't wait for the response - * @CMD_WANT_SKB: valid only with CMD_SYNC. The caller needs the buffer of the - * response. The caller needs to call iwl_free_resp when done. + * @CMD_WANT_SKB: Not valid with CMD_ASYNC. The caller needs the buffer of + * the response. The caller needs to call iwl_free_resp when done. + * @CMD_HIGH_PRIO: The command is high priority - it goes to the front of the + * command queue, but after other high priority commands. valid only + * with CMD_ASYNC. + * @CMD_SEND_IN_IDLE: The command should be sent even when the trans is idle. + * @CMD_MAKE_TRANS_IDLE: The command response should mark the trans as idle. + * @CMD_WAKE_UP_TRANS: The command response should wake up the trans + * (i.e. mark it as non-idle). */ enum CMD_MODE { - CMD_SYNC = 0, CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), CMD_SEND_IN_RFKILL = BIT(2), + CMD_HIGH_PRIO = BIT(3), + CMD_SEND_IN_IDLE = BIT(4), + CMD_MAKE_TRANS_IDLE = BIT(5), + CMD_WAKE_UP_TRANS = BIT(6), }; #define DEF_CMD_PAYLOAD_SIZE 320 @@ -318,6 +337,29 @@ enum iwl_d3_status { }; /** + * enum iwl_trans_status: transport status flags + * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed + * @STATUS_DEVICE_ENABLED: APM is enabled + * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) + * @STATUS_INT_ENABLED: interrupts are enabled + * @STATUS_RFKILL: the HW RFkill switch is in KILL position + * @STATUS_FW_ERROR: the fw is in error state + * @STATUS_TRANS_GOING_IDLE: shutting down the trans, only special commands + * are sent + * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent + */ +enum iwl_trans_status { + STATUS_SYNC_HCMD_ACTIVE, + STATUS_DEVICE_ENABLED, + STATUS_TPOWER_PMI, + STATUS_INT_ENABLED, + STATUS_RFKILL, + STATUS_FW_ERROR, + STATUS_TRANS_GOING_IDLE, + STATUS_TRANS_IDLE, +}; + +/** * struct iwl_trans_config - transport configuration * * @op_mode: pointer to the upper layer. @@ -344,12 +386,12 @@ struct iwl_trans_config { u8 cmd_queue; u8 cmd_fifo; const u8 *no_reclaim_cmds; - int n_no_reclaim_cmds; + unsigned int n_no_reclaim_cmds; bool rx_buf_size_8k; bool bc_table_dword; unsigned int queue_watchdog_timeout; - const char **command_names; + const char *const *command_names; }; struct iwl_trans; @@ -361,9 +403,7 @@ struct iwl_trans; * * @start_hw: starts the HW- from that point on, the HW can send interrupts * May sleep - * @stop_hw: stops the HW- from that point on, the HW will be in low power but - * will still issue interrupt if the HW RF kill is triggered unless - * op_mode_leaving is true. + * @op_mode_leave: Turn off the HW RF kill indication if on * May sleep * @start_fw: allocates and inits all the resources for the transport * layer. Also kick a fw image. @@ -371,8 +411,11 @@ struct iwl_trans; * @fw_alive: called when the fw sends alive notification. If the fw provides * the SCD base address in SRAM, then provide it here, or 0 otherwise. * May sleep - * @stop_device:stops the whole device (embedded CPU put to reset) - * May sleep + * @stop_device: stops the whole device (embedded CPU put to reset) and stops + * the HW. From that point on, the HW will be in low power but will still + * issue interrupt if the HW RF kill is triggered. This callback must do + * the right thing and not crash even if start_hw() was called but not + * start_fw(). May sleep * @d3_suspend: put the device into the correct mode for WoWLAN during * suspend. This is optional, if not implemented WoWLAN will not be * supported. This callback may sleep. @@ -382,7 +425,7 @@ struct iwl_trans; * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. * If RFkill is asserted in the middle of a SYNC host command, it must * return -ERFKILL straight away. - * May sleep only if CMD_SYNC is set + * May sleep only if CMD_ASYNC is not set * @tx: send an skb * Must be atomic * @reclaim: free packet until ssn. Returns a list of freed packets. @@ -392,8 +435,7 @@ struct iwl_trans; * this one. The op_mode must not configure the HCMD queue. May sleep. * @txq_disable: de-configure a Tx queue to send AMPDUs * Must be atomic - * @wait_tx_queue_empty: wait until all tx queues are empty - * May sleep + * @wait_tx_queue_empty: wait until tx queues are empty. May sleep. * @dbgfs_register: add the dbgfs files under this directory. Files will be * automatically deleted. * @write8: write a u8 to a register at offset ofs from the BAR @@ -414,13 +456,25 @@ struct iwl_trans; * @release_nic_access: let the NIC go to sleep. The "flags" parameter * must be the same one that was sent before to the grab_nic_access. * @set_bits_mask - set SRAM register according to value and mask. + * @ref: grab a reference to the transport/FW layers, disallowing + * certain low power states + * @unref: release a reference previously taken with @ref. Note that + * initially the reference count is 1, making an initial @unref + * necessary to allow low power states. + * @dump_data: fill a data dump with debug data, maybe containing last + * TX'ed commands and similar. When called with a NULL buffer and + * zero buffer length, provide only the (estimated) required buffer + * length. Return the used buffer length. + * Note that the transport must fill in the proper file headers. */ struct iwl_trans_ops { int (*start_hw)(struct iwl_trans *iwl_trans); - void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving); + void (*op_mode_leave)(struct iwl_trans *iwl_trans); int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill); + int (*update_sf)(struct iwl_trans *trans, + struct iwl_sf_region *st_fwrd_space); void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans); @@ -440,7 +494,7 @@ struct iwl_trans_ops { void (*txq_disable)(struct iwl_trans *trans, int queue); int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); - int (*wait_tx_queue_empty)(struct iwl_trans *trans); + int (*wait_tx_queue_empty)(struct iwl_trans *trans, u32 txq_bm); void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val); void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val); @@ -460,6 +514,12 @@ struct iwl_trans_ops { unsigned long *flags); void (*set_bits_mask)(struct iwl_trans *trans, u32 reg, u32 mask, u32 value); + void (*ref)(struct iwl_trans *trans); + void (*unref)(struct iwl_trans *trans); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + u32 (*dump_data)(struct iwl_trans *trans, void *buf, u32 buflen); +#endif }; /** @@ -479,6 +539,7 @@ enum iwl_trans_state { * @ops - pointer to iwl_trans_ops * @op_mode - pointer to the op_mode * @cfg - pointer to the configuration + * @status: a bit-mask of transport status flags * @dev - pointer to struct device * that represents the device * @hw_id: a u32 with the ID of the device / subdevice. * Set during transport allocation. @@ -493,12 +554,14 @@ enum iwl_trans_state { * starting the firmware, used for tracing * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the * start of the 802.11 header in the @rx_mpdu_cmd + * @dflt_pwr_limit: default power limit fetched from the platform (ACPI) */ struct iwl_trans { const struct iwl_trans_ops *ops; struct iwl_op_mode *op_mode; const struct iwl_cfg *cfg; enum iwl_trans_state state; + unsigned long status; struct device *dev; u32 hw_rev; @@ -520,6 +583,8 @@ struct iwl_trans { struct lockdep_map sync_cmd_lockdep_map; #endif + u64 dflt_pwr_limit; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); @@ -540,15 +605,14 @@ static inline int iwl_trans_start_hw(struct iwl_trans *trans) return trans->ops->start_hw(trans); } -static inline void iwl_trans_stop_hw(struct iwl_trans *trans, - bool op_mode_leaving) +static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans) { might_sleep(); - trans->ops->stop_hw(trans, op_mode_leaving); + if (trans->ops->op_mode_leave) + trans->ops->op_mode_leave(trans); - if (op_mode_leaving) - trans->op_mode = NULL; + trans->op_mode = NULL; trans->state = IWL_TRANS_NO_FW; } @@ -570,9 +634,21 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans, WARN_ON_ONCE(!trans->rx_mpdu_cmd); + clear_bit(STATUS_FW_ERROR, &trans->status); return trans->ops->start_fw(trans, fw, run_in_rfkill); } +static inline int iwl_trans_update_sf(struct iwl_trans *trans, + struct iwl_sf_region *st_fwrd_space) +{ + might_sleep(); + + if (trans->ops->update_sf) + return trans->ops->update_sf(trans, st_fwrd_space); + + return 0; +} + static inline void iwl_trans_stop_device(struct iwl_trans *trans) { might_sleep(); @@ -596,13 +672,42 @@ static inline int iwl_trans_d3_resume(struct iwl_trans *trans, return trans->ops->d3_resume(trans, status, test); } +static inline void iwl_trans_ref(struct iwl_trans *trans) +{ + if (trans->ops->ref) + trans->ops->ref(trans); +} + +static inline void iwl_trans_unref(struct iwl_trans *trans) +{ + if (trans->ops->unref) + trans->ops->unref(trans); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static inline u32 iwl_trans_dump_data(struct iwl_trans *trans, + void *buf, u32 buflen) +{ + if (!trans->ops->dump_data) + return 0; + return trans->ops->dump_data(trans, buf, buflen); +} +#endif + static inline int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { int ret; - if (trans->state != IWL_TRANS_FW_ALIVE) { - IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); + if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) && + test_bit(STATUS_RFKILL, &trans->status))) + return -ERFKILL; + + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return -EIO; } @@ -640,8 +745,11 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int queue) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); return trans->ops->tx(trans, skb, dev_cmd, queue); } @@ -649,17 +757,14 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); trans->ops->reclaim(trans, queue, ssn, skbs); } static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); - trans->ops->txq_disable(trans, queue); } @@ -669,8 +774,8 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, { might_sleep(); - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); trans->ops->txq_enable(trans, queue, fifo, sta_id, tid, frame_limit, ssn); @@ -683,12 +788,13 @@ static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, IWL_MAX_TID_COUNT, IWL_FRAME_LIMIT, 0); } -static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans) +static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, + u32 txq_bm) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - return trans->ops->wait_tx_queue_empty(trans); + return trans->ops->wait_tx_queue_empty(trans, txq_bm); } static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, @@ -760,7 +866,8 @@ static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr, static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state) { - trans->ops->set_pmi(trans, state); + if (trans->ops->set_pmi) + trans->ops->set_pmi(trans, state); } static inline void @@ -780,6 +887,16 @@ iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags) __release(nic_access); } +static inline void iwl_trans_fw_error(struct iwl_trans *trans) +{ + if (WARN_ON_ONCE(!trans->op_mode)) + return; + + /* prevent double restarts due to the same erroneous FW */ + if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status)) + iwl_op_mode_nic_error(trans->op_mode); +} + /***************************************************** * driver (transport) register/unregister functions ******************************************************/ diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 6d73817850c..c30d7f64ec1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -1,10 +1,11 @@ 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 +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 power_legacy.o bt-coex.o -iwlmvm-y += led.o tt.o -iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o +iwlmvm-y += power.o coex.o +iwlmvm-y += tt.o offloading.o +iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o +iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o ccflags-y += -D__CHECK_ENDIAN__ -I$(src)/../ diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c index 93fd1457954..a1376539d2d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/iwlwifi/mvm/binding.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; + /* + * Update SF - Disable if needed. if this fails, SF might still be on + * while many macs are bound, which is forbidden - so fail the binding. + */ + if (iwl_mvm_sf_update(mvm, vif, false)) + return -EINVAL; + return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); } int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; - return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); + ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); + + if (!ret) + if (iwl_mvm_sf_update(mvm, vif, true)) + IWL_ERR(mvm, "Failed to update SF state\n"); + + return ret; } diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c deleted file mode 100644 index 0fad98b85f6..00000000000 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ /dev/null @@ -1,644 +0,0 @@ -/****************************************************************************** - * - * 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) 2013 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) 2013 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/mac80211.h> - -#include "fw-api-bt-coex.h" -#include "iwl-modparams.h" -#include "mvm.h" -#include "iwl-debug.h" - -#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ - [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ - ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) - -static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, - BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, - BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, - BT_COEX_PRIO_TBL_PRIO_LOW, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, - BT_COEX_PRIO_TBL_PRIO_LOW, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, - BT_COEX_PRIO_TBL_PRIO_HIGH, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, - BT_COEX_PRIO_TBL_PRIO_HIGH, 1), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, - BT_COEX_PRIO_TBL_DISABLED, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, - BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, - BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), - EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, - BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), - 0, 0, 0, 0, 0, 0, -}; - -#undef EVENT_PRIO_ANT - -/* BT Antenna Coupling Threshold (dB) */ -#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) -#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD (3) - -#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62) -#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65) -#define BT_REDUCED_TX_POWER_BIT BIT(7) - -static inline bool is_loose_coex(void) -{ - return iwlwifi_mod_params.ant_coupling > - IWL_BT_ANTENNA_COUPLING_THRESHOLD; -} - -int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) -{ - return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC, - sizeof(struct iwl_bt_coex_prio_tbl_cmd), - &iwl_bt_prio_tbl); -} - -static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type) -{ - struct iwl_bt_coex_prot_env_cmd env_cmd; - int ret; - - env_cmd.action = action; - env_cmd.type = type; - ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC, - sizeof(env_cmd), &env_cmd); - if (ret) - IWL_ERR(mvm, "failed to send BT env command\n"); - return ret; -} - -enum iwl_bt_kill_msk { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_SCO_HID_A2DP, - BT_KILL_MSK_REDUCED_TXPOW, - BT_KILL_MSK_MAX, -}; - -static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = { - [BT_KILL_MSK_DEFAULT] = 0xffff0000, - [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, - [BT_KILL_MSK_REDUCED_TXPOW] = 0, -}; - -static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = { - [BT_KILL_MSK_DEFAULT] = 0xffff0000, - [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, - [BT_KILL_MSK_REDUCED_TXPOW] = 0, -}; - -#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0) - -/* Tight Coex */ -static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - -/* Loose Coex */ -static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), -}; - -/* Full concurrency */ -static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), -}; - -/* single shared antenna */ -static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xC0004000), - cpu_to_le32(0xF0005000), - cpu_to_le32(0xC0004000), - cpu_to_le32(0xF0005000), -}; - -int iwl_send_bt_init_conf(struct iwl_mvm *mvm) -{ - 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, - }; - int ret; - - /* go to CALIB state in internal BT-Coex state machine */ - ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - - ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - - bt_cmd->max_kill = 5; - bt_cmd->bt3_time_t7_value = 1; - bt_cmd->bt3_prio_sample_time = 2; - bt_cmd->bt3_timer_t2_value = 0xc; - - bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ? - BT_COEX_NW : BT_COEX_DISABLE; - bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE; - - bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE | - BT_VALID_BT_PRIO_BOOST | - BT_VALID_MAX_KILL | - BT_VALID_3W_TMRS | - BT_VALID_KILL_ACK | - BT_VALID_KILL_CTS | - BT_VALID_REDUCED_TX_POWER | - BT_VALID_LUT); - - if (mvm->cfg->bt_shared_single_ant) - memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup, - sizeof(iwl_single_shared_ant_lookup)); - else if (is_loose_coex()) - memcpy(&bt_cmd->decision_lut, iwl_loose_lookup, - sizeof(iwl_tight_lookup)); - else - memcpy(&bt_cmd->decision_lut, iwl_tight_lookup, - sizeof(iwl_tight_lookup)); - - bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST); - bt_cmd->kill_ack_msk = - cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); - bt_cmd->kill_cts_msk = - cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); - - memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, - bool reduced_tx_power) -{ - enum iwl_bt_kill_msk bt_kill_msk; - struct iwl_bt_coex_cmd *bt_cmd; - struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .data[0] = &bt_cmd, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_SYNC, - }; - int ret = 0; - - lockdep_assert_held(&mvm->mutex); - - if (reduced_tx_power) { - /* Reduced Tx power has precedence on the type of the profile */ - bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW; - } else { - /* Low latency BT profile is active: give higher prio to BT */ - if (BT_MBOX_MSG(notif, 3, SCO_STATE) || - BT_MBOX_MSG(notif, 3, A2DP_STATE) || - BT_MBOX_MSG(notif, 3, SNIFF_STATE)) - bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP; - else - bt_kill_msk = BT_KILL_MSK_DEFAULT; - } - - IWL_DEBUG_COEX(mvm, - "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n", - bt_kill_msk, - BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in", - BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in", - BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in"); - - /* Don't send HCMD if there is no update */ - if (bt_kill_msk == mvm->bt_kill_msk) - return 0; - - mvm->bt_kill_msk = bt_kill_msk; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - - bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); - bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); - bt_cmd->valid_bit_msk = - cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS); - - IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk); - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, - bool enable) -{ - struct iwl_bt_coex_cmd *bt_cmd; - /* Send ASYNC since this can be sent from an atomic context */ - struct iwl_host_cmd cmd = { - .id = BT_CONFIG, - .len = { sizeof(*bt_cmd), }, - .dataflags = { IWL_HCMD_DFL_DUP, }, - .flags = CMD_ASYNC, - }; - - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - int ret; - - /* This can happen if the station has been removed right now */ - if (sta_id == IWL_MVM_STATION_COUNT) - return 0; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - mvmsta = (void *)sta->drv_priv; - - /* nothing to do */ - if (mvmsta->bt_reduced_txpower == enable) - return 0; - - bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); - if (!bt_cmd) - return -ENOMEM; - cmd.data[0] = bt_cmd; - - bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER), - bt_cmd->bt_reduced_tx_power = sta_id; - - if (enable) - bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; - - IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", - enable ? "en" : "dis", sta_id); - - mvmsta->bt_reduced_txpower = enable; - - ret = iwl_mvm_send_cmd(mvm, &cmd); - - kfree(bt_cmd); - return ret; -} - -struct iwl_bt_iterator_data { - struct iwl_bt_coex_profile_notif *notif; - struct iwl_mvm *mvm; - u32 num_bss_ifaces; - bool reduced_tx_power; -}; - -static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_bt_iterator_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - struct ieee80211_chanctx_conf *chanctx_conf; - enum ieee80211_smps_mode smps_mode; - enum ieee80211_band band; - int ave_rssi; - - lockdep_assert_held(&mvm->mutex); - if (vif->type != NL80211_IFTYPE_STATION) - return; - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (chanctx_conf && chanctx_conf->def.chan) - band = chanctx_conf->def.chan->band; - else - band = -1; - rcu_read_unlock(); - - smps_mode = IEEE80211_SMPS_AUTOMATIC; - - /* non associated BSSes aren't to be considered */ - if (!vif->bss_conf.assoc) - return; - - if (band != IEEE80211_BAND_2GHZ) { - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); - return; - } - - if (data->notif->bt_status) - smps_mode = IEEE80211_SMPS_DYNAMIC; - - if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD) - smps_mode = IEEE80211_SMPS_STATIC; - - IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_status %d traffic_load %d smps_req %d\n", - mvmvif->id, data->notif->bt_status, - data->notif->bt_traffic_load, smps_mode); - - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); - - /* don't reduce the Tx power if in loose scheme */ - if (is_loose_coex()) - return; - - data->num_bss_ifaces++; - - /* reduced Txpower only if there are open BT connections, so ...*/ - if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) { - /* ... cancel reduced Tx power ... */ - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - data->reduced_tx_power = false; - - /* ... and there is no need to get reports on RSSI any more. */ - ieee80211_disable_rssi_reports(vif); - return; - } - - ave_rssi = ieee80211_ave_rssi(vif); - - /* if the RSSI isn't valid, fake it is very low */ - if (!ave_rssi) - ave_rssi = -100; - if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - - /* - * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the - * BSS / P2P clients have rssi above threshold. - * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before - * the iteration, if one interface's rssi isn't good enough, - * bt_kill_msk will be set to default values. - */ - } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) { - if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) - IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); - - /* - * One interface hasn't rssi above threshold, bt_kill_msk must - * be set to default values. - */ - data->reduced_tx_power = false; - } - - /* Begin to monitor the RSSI: it may influence the reduced Tx power */ - ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD, - BT_ENABLE_REDUCED_TXPOWER_THRESHOLD); -} - -static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) -{ - struct iwl_bt_iterator_data data = { - .mvm = mvm, - .notif = &mvm->last_bt_notif, - .reduced_tx_power = true, - }; - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_notif_iterator, &data); - - /* - * If there are no BSS / P2P client interfaces, reduced Tx Power is - * irrelevant since it is based on the RSSI coming from the beacon. - * Use BT_KILL_MSK_DEFAULT in that case. - */ - data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; - - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); -} - -/* upon association, the fw will send in BT Coex notification */ -int iwl_mvm_rx_bt_coex_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); - struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; - - - IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not "); - IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); - IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load); - IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", - notif->bt_agg_traffic_load); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); - - /* remember this notification for future use: rssi fluctuations */ - memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); - - iwl_mvm_bt_coex_notif_handle(mvm); - - /* - * This is an async handler for a notification, returning anything other - * than 0 doesn't make sense even if HCMD failed. - */ - return 0; -} - -static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; - struct iwl_bt_iterator_data *data = _data; - struct iwl_mvm *mvm = data->mvm; - - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvmsta; - - if (vif->type != NL80211_IFTYPE_STATION || - mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) - return; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], - lockdep_is_held(&mvm->mutex)); - mvmsta = (void *)sta->drv_priv; - - data->num_bss_ifaces++; - - /* - * This interface doesn't support reduced Tx power (because of low - * RSSI probably), then set bt_kill_msk to default values. - */ - if (!mvmsta->bt_reduced_txpower) - data->reduced_tx_power = false; - /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */ -} - -void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event) -{ - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; - struct iwl_bt_iterator_data data = { - .mvm = mvm, - .reduced_tx_power = true, - }; - int ret; - - mutex_lock(&mvm->mutex); - - /* Rssi update while not associated ?! */ - if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) - goto out_unlock; - - /* No open connection - reports should be disabled */ - if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2)) - goto out_unlock; - - IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, - rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); - - /* - * Check if rssi is good enough for reduced Tx power, but not in loose - * scheme. - */ - if (rssi_event == RSSI_EVENT_LOW || is_loose_coex()) - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, - false); - else - ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); - - if (ret) - IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); - - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_bt_rssi_iterator, &data); - - /* - * If there are no BSS / P2P client interfaces, reduced Tx Power is - * irrelevant since it is based on the RSSI coming from the beacon. - * Use BT_KILL_MSK_DEFAULT in that case. - */ - data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; - - if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) - IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); - - out_unlock: - mutex_unlock(&mvm->mutex); -} - -void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - iwl_mvm_bt_coex_notif_handle(mvm); -} diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c new file mode 100644 index 00000000000..c8c3b38228f --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -0,0 +1,1322 @@ +/****************************************************************************** + * + * 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) 2013 - 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) 2013 - 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 <linux/ieee80211.h> +#include <linux/etherdevice.h> +#include <net/mac80211.h> + +#include "fw-api-coex.h" +#include "iwl-modparams.h" +#include "mvm.h" +#include "iwl-debug.h" + +#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \ + [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \ + ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS)) + +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2, + BT_COEX_PRIO_TBL_PRIO_BYPASS, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1, + BT_COEX_PRIO_TBL_PRIO_LOW, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2, + BT_COEX_PRIO_TBL_PRIO_LOW, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1, + BT_COEX_PRIO_TBL_PRIO_HIGH, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2, + BT_COEX_PRIO_TBL_PRIO_HIGH, 1), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM, + BT_COEX_PRIO_TBL_DISABLED, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52, + BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24, + BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0), + EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE, + BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0), + 0, 0, 0, 0, 0, 0, +}; + +#undef EVENT_PRIO_ANT + +#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62) +#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65) +#define BT_ANTENNA_COUPLING_THRESHOLD (30) + +static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) +{ + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0, + sizeof(struct iwl_bt_coex_prio_tbl_cmd), + &iwl_bt_prio_tbl); +} + +const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = { + [BT_KILL_MSK_DEFAULT] = 0xffff0000, + [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, + [BT_KILL_MSK_REDUCED_TXPOW] = 0, +}; + +const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = { + [BT_KILL_MSK_DEFAULT] = 0xffff0000, + [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, + [BT_KILL_MSK_REDUCED_TXPOW] = 0, +}; + +static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { + cpu_to_le32(0xf0f0f0f0), /* 50% */ + cpu_to_le32(0xc0c0c0c0), /* 25% */ + cpu_to_le32(0xfcfcfcfc), /* 75% */ + cpu_to_le32(0xfefefefe), /* 87.5% */ +}; + +static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, +}; + +static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + /* Tight */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Loose */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Tx Tx disabled */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xeeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, +}; + +/* 20MHz / 40MHz below / 40Mhz above*/ +static const __le64 iwl_ci_mask[][3] = { + /* dummy entry for channel 0 */ + {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, + { + cpu_to_le64(0x0000001FFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x00007FFFFFULL), + }, + { + cpu_to_le64(0x000000FFFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0003FFFFFFULL), + }, + { + cpu_to_le64(0x000003FFFCULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x000FFFFFFCULL), + }, + { + cpu_to_le64(0x00001FFFE0ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x007FFFFFE0ULL), + }, + { + cpu_to_le64(0x00007FFF80ULL), + cpu_to_le64(0x00007FFFFFULL), + cpu_to_le64(0x01FFFFFF80ULL), + }, + { + cpu_to_le64(0x0003FFFC00ULL), + cpu_to_le64(0x0003FFFFFFULL), + cpu_to_le64(0x0FFFFFFC00ULL), + }, + { + cpu_to_le64(0x000FFFF000ULL), + cpu_to_le64(0x000FFFFFFCULL), + cpu_to_le64(0x3FFFFFF000ULL), + }, + { + cpu_to_le64(0x007FFF8000ULL), + cpu_to_le64(0x007FFFFFE0ULL), + cpu_to_le64(0xFFFFFF8000ULL), + }, + { + cpu_to_le64(0x01FFFE0000ULL), + cpu_to_le64(0x01FFFFFF80ULL), + cpu_to_le64(0xFFFFFE0000ULL), + }, + { + cpu_to_le64(0x0FFFF00000ULL), + cpu_to_le64(0x0FFFFFFC00ULL), + cpu_to_le64(0x0ULL), + }, + { + cpu_to_le64(0x3FFFC00000ULL), + cpu_to_le64(0x3FFFFFF000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFFE000000ULL), + cpu_to_le64(0xFFFFFF8000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFF8000000ULL), + cpu_to_le64(0xFFFFFE0000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFC0000000ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0ULL) + }, +}; + +static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = { + cpu_to_le32(0x28412201), + cpu_to_le32(0x11118451), +}; + +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) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + enum iwl_bt_coex_lut_type ret; + u16 phy_ctx_id; + + /* + * Checking that we hold mvm->mutex is a good idea, but the rate + * control can't acquire the mutex since it runs in Tx path. + * So this is racy in that case, but in the worst case, the AMPDU + * size limit will be wrong for a short time which is not a big + * issue. + */ + + rcu_read_lock(); + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return BT_COEX_LOOSE_LUT; + } + + ret = BT_COEX_TX_DIS_LUT; + + if (mvm->cfg->bt_shared_single_ant) { + rcu_read_unlock(); + return ret; + } + + phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); + + if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); + else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); + /* else - default = TX TX disallowed */ + + rcu_read_unlock(); + + return ret; +} + +int iwl_send_bt_init_conf(struct iwl_mvm *mvm) +{ + struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret; + u32 flags; + + ret = iwl_send_bt_prio_tbl(mvm); + if (ret) + return ret; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + + bt_cmd->max_kill = 5; + bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD; + bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling; + bt_cmd->bt4_tx_tx_delta_freq_thr = 15; + bt_cmd->bt4_tx_rx_max_freq0 = 15; + bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT; + bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT; + + flags = iwlwifi_mod_params.bt_coex_active ? + BT_COEX_NW : BT_COEX_DISABLE; + bt_cmd->flags = cpu_to_le32(flags); + + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_BT_PRIO_BOOST | + BT_VALID_MAX_KILL | + BT_VALID_3W_TMRS | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS | + BT_VALID_REDUCED_TX_POWER | + BT_VALID_LUT | + BT_VALID_WIFI_RX_SW_PRIO_BOOST | + BT_VALID_WIFI_TX_SW_PRIO_BOOST | + BT_VALID_ANT_ISOLATION | + BT_VALID_ANT_ISOLATION_THRS | + BT_VALID_TXTX_DELTA_FREQ_THRS | + BT_VALID_TXRX_MAX_FREQ_0 | + BT_VALID_SYNC_TO_SCO); + + 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)); + else + 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, + sizeof(iwl_bt_mprio_lut)); + bt_cmd->kill_ack_msk = + cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); + bt_cmd->kill_cts_msk = + cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); + + memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); + memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, + bool reduced_tx_power) +{ + enum iwl_bt_kill_msk bt_kill_msk; + struct iwl_bt_coex_cmd *bt_cmd; + struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif; + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .data[0] = &bt_cmd, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + int ret = 0; + + lockdep_assert_held(&mvm->mutex); + + if (reduced_tx_power) { + /* Reduced Tx power has precedence on the type of the profile */ + bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW; + } else { + /* Low latency BT profile is active: give higher prio to BT */ + if (BT_MBOX_MSG(notif, 3, SCO_STATE) || + BT_MBOX_MSG(notif, 3, A2DP_STATE) || + BT_MBOX_MSG(notif, 3, SNIFF_STATE)) + bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP; + else + bt_kill_msk = BT_KILL_MSK_DEFAULT; + } + + IWL_DEBUG_COEX(mvm, + "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n", + bt_kill_msk, + BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in", + BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in", + BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in"); + + /* Don't send HCMD if there is no update */ + if (bt_kill_msk == mvm->bt_kill_msk) + return 0; + + mvm->bt_kill_msk = bt_kill_msk; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); + if (!bt_cmd) + return -ENOMEM; + cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW); + + bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); + bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS); + + IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n", + iwl_bt_ack_kill_msk[bt_kill_msk], + iwl_bt_cts_kill_msk[bt_kill_msk]); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, + bool enable) +{ + struct iwl_bt_coex_cmd *bt_cmd; + /* Send ASYNC since this can be sent from an atomic context */ + struct iwl_host_cmd cmd = { + .id = BT_CONFIG, + .len = { sizeof(*bt_cmd), }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + .flags = CMD_ASYNC, + }; + struct iwl_mvm_sta *mvmsta; + int ret; + + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + if (!mvmsta) + return 0; + + /* nothing to do */ + if (mvmsta->bt_reduced_txpower == enable) + return 0; + + bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC); + if (!bt_cmd) + return -ENOMEM; + 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_REDUCED_TX_POWER); + bt_cmd->bt_reduced_tx_power = sta_id; + + if (enable) + bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT; + + IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n", + enable ? "en" : "dis", sta_id); + + mvmsta->bt_reduced_txpower = enable; + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} + +struct iwl_bt_iterator_data { + struct iwl_bt_coex_profile_notif *notif; + struct iwl_mvm *mvm; + u32 num_bss_ifaces; + bool reduced_tx_power; + struct ieee80211_chanctx_conf *primary; + struct ieee80211_chanctx_conf *secondary; + bool primary_ll; +}; + +static inline +void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, int rssi) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->bf_data.last_bt_coex_event = rssi; + mvmvif->bf_data.bt_coex_max_thold = + enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0; + mvmvif->bf_data.bt_coex_min_thold = + enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0; +} + +/* must be called under rcu_read_lock */ +static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_smps_mode smps_mode; + u32 bt_activity_grading; + int ave_rssi; + + lockdep_assert_held(&mvm->mutex); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + /* Count BSSes vifs */ + data->num_bss_ifaces++; + /* default smps_mode for BSS / P2P client is AUTOMATIC */ + smps_mode = IEEE80211_SMPS_AUTOMATIC; + break; + case NL80211_IFTYPE_AP: + /* default smps_mode for AP / GO is OFF */ + smps_mode = IEEE80211_SMPS_OFF; + if (!mvmvif->ap_ibss_active) { + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + return; + } + + /* the Ack / Cts kill mask must be default if AP / GO */ + data->reduced_tx_power = false; + break; + default: + return; + } + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + /* If channel context is invalid or not on 2.4GHz .. */ + if ((!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { + /* ... relax constraints and disable rssi events */ + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + data->reduced_tx_power = false; + if (vif->type == NL80211_IFTYPE_STATION) { + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + } + return; + } + + bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading); + if (bt_activity_grading >= BT_HIGH_TRAFFIC) + smps_mode = IEEE80211_SMPS_STATIC; + else if (bt_activity_grading >= BT_LOW_TRAFFIC) + smps_mode = vif->type == NL80211_IFTYPE_AP ? + IEEE80211_SMPS_OFF : + IEEE80211_SMPS_DYNAMIC; + + /* relax SMPS contraints for next association */ + if (!vif->bss_conf.assoc) + smps_mode = IEEE80211_SMPS_AUTOMATIC; + + IWL_DEBUG_COEX(data->mvm, + "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", + mvmvif->id, data->notif->bt_status, bt_activity_grading, + smps_mode); + + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); + + /* low latency is always primary */ + if (iwl_mvm_vif_low_latency(mvmvif)) { + data->primary_ll = true; + + data->secondary = data->primary; + data->primary = chanctx_conf; + } + + if (vif->type == NL80211_IFTYPE_AP) { + if (!mvmvif->ap_ibss_active) + return; + + if (chanctx_conf == data->primary) + return; + + if (!data->primary_ll) { + /* + * downgrade the current primary no matter what its + * type is. + */ + data->secondary = data->primary; + data->primary = chanctx_conf; + } else { + /* there is low latency vif - we will be secondary */ + data->secondary = chanctx_conf; + } + return; + } + + /* + * STA / P2P Client, try to be primary if first vif. If we are in low + * latency mode, we are already in primary and just don't do much + */ + if (!data->primary || data->primary == chanctx_conf) + data->primary = chanctx_conf; + else if (!data->secondary) + /* if secondary is not NULL, it might be a GO */ + data->secondary = chanctx_conf; + + /* + * don't reduce the Tx power if one of these is true: + * we are in LOOSE + * single share antenna product + * BT is active + * we are associated + */ + if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || + mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc || + !data->notif->bt_status) { + data->reduced_tx_power = false; + iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); + return; + } + + /* try to get the avg rssi from fw */ + ave_rssi = mvmvif->bf_data.ave_beacon_signal; + + /* if the RSSI isn't valid, fake it is very low */ + if (!ave_rssi) + ave_rssi = -100; + if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + + /* + * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the + * BSS / P2P clients have rssi above threshold. + * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before + * the iteration, if one interface's rssi isn't good enough, + * bt_kill_msk will be set to default values. + */ + } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) { + if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) + IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); + + /* + * One interface hasn't rssi above threshold, bt_kill_msk must + * be set to default values. + */ + data->reduced_tx_power = false; + } + + /* Begin to monitor the RSSI: it may influence the reduced Tx power */ + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); +} + +static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) +{ + struct iwl_bt_iterator_data data = { + .mvm = mvm, + .notif = &mvm->last_bt_notif, + .reduced_tx_power = true, + }; + struct iwl_bt_coex_ci_cmd cmd = {}; + u8 ci_bw_idx; + + rcu_read_lock(); + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_notif_iterator, &data); + + if (data.primary) { + struct ieee80211_chanctx_conf *chan = data.primary; + if (WARN_ON(!chan->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_primary = 0; + } else { + cmd.co_run_bw_primary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_primary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); + } + + if (data.secondary) { + struct ieee80211_chanctx_conf *chan = data.secondary; + if (WARN_ON(!data.secondary->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_secondary = 0; + } else { + cmd.co_run_bw_secondary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_secondary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); + } + + rcu_read_unlock(); + + /* Don't spam the fw with the same command over and over */ + if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0, + sizeof(cmd), &cmd)) + IWL_ERR(mvm, "Failed to send BT_CI cmd\n"); + memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); + } + + /* + * If there are no BSS / P2P client interfaces, reduced Tx Power is + * irrelevant since it is based on the RSSI coming from the beacon. + * Use BT_KILL_MSK_DEFAULT in that case. + */ + data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; + + if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) + IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} + +int iwl_mvm_rx_bt_coex_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); + struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data; + + + IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); + IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", + notif->bt_status ? "ON" : "OFF"); + IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", + le32_to_cpu(notif->primary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", + le32_to_cpu(notif->bt_activity_grading)); + IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", + notif->bt_agg_traffic_load); + + /* remember this notification for future use: rssi fluctuations */ + memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); + + iwl_mvm_bt_coex_notif_handle(mvm); + + /* + * This is an async handler for a notification, returning anything other + * than 0 doesn't make sense even if HCMD failed. + */ + return 0; +} + +static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_bt_iterator_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + + struct ieee80211_sta *sta; + struct iwl_mvm_sta *mvmsta; + + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + /* If channel context is invalid or not on 2.4GHz - don't count it */ + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + + if (vif->type != NL80211_IFTYPE_STATION || + mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], + lockdep_is_held(&mvm->mutex)); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + data->num_bss_ifaces++; + + /* + * This interface doesn't support reduced Tx power (because of low + * RSSI probably), then set bt_kill_msk to default values. + */ + if (!mvmsta->bt_reduced_txpower) + data->reduced_tx_power = false; + /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */ +} + +void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + enum ieee80211_rssi_event rssi_event) +{ + struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_bt_iterator_data data = { + .mvm = mvm, + .reduced_tx_power = true, + }; + int ret; + + lockdep_assert_held(&mvm->mutex); + + /* + * Rssi update while not associated - can happen since the statistics + * are handled asynchronously + */ + if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) + return; + + /* No BT - reports should be disabled */ + if (!mvm->last_bt_notif.bt_status) + return; + + IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, + rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); + + /* + * Check if rssi is good enough for reduced Tx power, but not in loose + * scheme. + */ + if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || + iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, + false); + else + ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true); + + if (ret) + IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n"); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bt_rssi_iterator, &data); + + /* + * If there are no BSS / P2P client interfaces, reduced Tx Power is + * irrelevant since it is based on the RSSI coming from the beacon. + * Use BT_KILL_MSK_DEFAULT in that case. + */ + data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces; + + if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) + IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) +#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) + +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; + + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + + if (lut_type == BT_COEX_LOOSE_LUT) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + /* tight coex, high bt traffic, reduce AGG time limit */ + return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; +} + +bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return true; + + /* + * In Tight, BT can't Rx while we Tx, so use both antennas since BT is + * already killed. + * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we + * Tx. + */ + return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT; +} + +bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, + enum ieee80211_band band) +{ + u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); + + if (band != IEEE80211_BAND_2GHZ) + return false; + + return bt_activity >= BT_LOW_TRAFFIC; +} + +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) +{ + 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; + int ret; + 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, }, + }; + + 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)); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + + kfree(bt_cmd); + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 2bf29f7992e..51685693af2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -70,11 +70,17 @@ #define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) #define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) #define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20 -#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 20 +#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8 +#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 +#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 #define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50 #define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50 #define IWL_MVM_PS_SNOOZE_INTERVAL 25 #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_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 417639f77b0..645b3cfc29a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,6 +67,7 @@ #include <net/cfg80211.h> #include <net/ipv6.h> #include <net/tcp.h> +#include <net/addrconf.h> #include "iwl-modparams.h" #include "fw-api.h" #include "mvm.h" @@ -192,8 +193,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, wkc.wep_key.key_offset = data->wep_key_idx; } - ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, CMD_SYNC, - sizeof(wkc), &wkc); + ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc); data->error = ret != 0; mvm->ptk_ivlen = key->iv_len; @@ -340,7 +340,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, struct iwl_host_cmd cmd = { .id = WOWLAN_PATTERNS, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .flags = CMD_SYNC, }; int i, err; @@ -375,73 +374,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; - } cmd = {}; - struct iwl_proto_offload_cmd_common *common; - u32 enabled = 0, size; -#if IS_ENABLED(CONFIG_IPV6) - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - int i; - - if (mvm->fw->ucode_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 (mvm->fw->ucode_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); - - return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC, - size, &cmd); -} - enum iwl_mvm_tcp_packet_type { MVM_TCP_TX_SYN, MVM_TCP_RX_SYNACK, @@ -584,7 +516,6 @@ static int iwl_mvm_send_remote_wake_cfg(struct iwl_mvm *mvm, .id = REMOTE_WAKE_CONFIG_CMD, .len = { sizeof(*cfg), }, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, - .flags = CMD_SYNC, }; int ret; @@ -732,10 +663,8 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (WARN_ON(!vif->bss_conf.assoc)) return -EINVAL; - /* hack */ - vif->bss_conf.assoc = false; + ret = iwl_mvm_mac_ctxt_add(mvm, vif); - vif->bss_conf.assoc = true; if (ret) return ret; @@ -771,7 +700,7 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return ret; rcu_assign_pointer(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id], ap_sta); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); if (ret) return ret; @@ -779,13 +708,13 @@ 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); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(quota_cmd), "a_cmd); if (ret) IWL_ERR(mvm, "Failed to send quota: %d\n", ret); @@ -793,6 +722,79 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return 0; } +static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_nonqos_seq_query_cmd query_cmd = { + .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET), + .mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + }; + struct iwl_host_cmd cmd = { + .id = NON_QOS_TX_COUNTER_CMD, + .flags = CMD_WANT_SKB, + }; + int err; + u32 size; + + cmd.data[0] = &query_cmd; + cmd.len[0] = sizeof(query_cmd); + + err = iwl_mvm_send_cmd(mvm, &cmd); + if (err) + return err; + + size = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (size < sizeof(__le16)) { + err = -EINVAL; + } else { + err = le16_to_cpup((__le16 *)cmd.resp_pkt->data); + /* firmware returns next, not last-used seqno */ + err = (u16) (err - 0x10); + } + + iwl_free_resp(&cmd); + return err; +} + +void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_nonqos_seq_query_cmd query_cmd = { + .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET), + .mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + .value = cpu_to_le16(mvmvif->seqno), + }; + + /* return if called during restart, not resume from D3 */ + if (!mvmvif->seqno_valid) + return; + + mvmvif->seqno_valid = false; + + if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, 0, + sizeof(query_cmd), &query_cmd)) + 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, 0, + cmd_len, cmd); +} + static int __iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan, bool test) @@ -805,7 +807,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 = { @@ -818,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, }; struct iwl_host_cmd d3_cfg_cmd = { .id = D3_CONFIG_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, .data[0] = &d3_cfg_cmd_data, .len[0] = sizeof(d3_cfg_cmd_data), }; @@ -827,10 +829,8 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, .tkip = &tkip_cmd, .use_tkip = false, }; - int ret, i; + int ret; int len __maybe_unused; - u16 seq; - u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; if (!wowlan) { /* @@ -847,8 +847,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); - old_aux_sta_id = mvm->aux_sta.sta_id; - /* see if there's only a single BSS vif and it's associated */ ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, @@ -872,60 +870,41 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; - /* - * The D3 firmware still hardcodes the AP station ID for the - * BSS we're associated with as 0. Store the real STA ID here - * and assign 0. When we leave this function, we'll restore - * the original value for the resume code. - */ - old_ap_sta_id = mvm_ap_sta->sta_id; - mvm_ap_sta->sta_id = 0; - mvmvif->ap_sta_id = 0; - - /* 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; - /* - * We know the last used seqno, and the uCode expects to know that - * one, it will increment before TX. - */ - seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ; - wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq); + /* 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.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++) { - 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) { @@ -933,7 +912,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 | @@ -964,16 +943,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvm->ptk_ivlen = 0; mvm->ptk_icvlen = 0; - /* - * The D3 firmware still hardcodes the AP station ID for the - * BSS we're associated with as 0. As a result, we have to move - * the auxiliary station to ID 1 so the ID 0 remains free for - * the AP station for later. - * We set the sta_id to 1 here, and reset it to its previous - * value (that we stored above) later. - */ - mvm->aux_sta.sta_id = 1; - ret = iwl_mvm_load_d3_fw(mvm); if (ret) goto out; @@ -1001,7 +970,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (key_data.use_rsc_tsc) { struct iwl_host_cmd rsc_tsc_cmd = { .id = WOWLAN_TSC_RSC_PARAM, - .flags = CMD_SYNC, .data[0] = key_data.rsc_tsc, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .len[0] = sizeof(*key_data.rsc_tsc), @@ -1015,7 +983,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (key_data.use_tkip) { ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_TKIP_PARAM, - CMD_SYNC, sizeof(tkip_cmd), + 0, sizeof(tkip_cmd), &tkip_cmd); if (ret) goto out; @@ -1032,8 +1000,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr; ret = iwl_mvm_send_cmd_pdu(mvm, - WOWLAN_KEK_KCK_MATERIAL, - CMD_SYNC, + WOWLAN_KEK_KCK_MATERIAL, 0, sizeof(kek_kck_cmd), &kek_kck_cmd); if (ret) @@ -1041,9 +1008,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; @@ -1051,7 +1016,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, 0); if (ret) goto out; @@ -1059,7 +1024,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; - ret = iwl_mvm_power_update_mode(mvm, vif); + ret = iwl_mvm_power_update_device(mvm); + if (ret) + goto out; + + ret = iwl_mvm_power_update_mac(mvm); if (ret) goto out; @@ -1074,15 +1043,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; #ifdef CONFIG_IWLWIFI_DEBUGFS - len = le32_to_cpu(d3_cfg_cmd.resp_pkt->len_n_flags) & - FH_RSCSR_FRAME_SIZE_MSK; - if (len >= sizeof(u32) * 2) { + len = iwl_rx_packet_payload_len(d3_cfg_cmd.resp_pkt); + if (len >= sizeof(u32)) { mvm->d3_test_pme_ptr = le32_to_cpup((__le32 *)d3_cfg_cmd.resp_pkt->data); - } else if (test) { - /* in test mode we require the pointer */ - ret = -EIO; - goto out; } #endif iwl_free_resp(&d3_cfg_cmd); @@ -1091,13 +1055,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, iwl_trans_d3_suspend(mvm->trans, test); out: - mvm->aux_sta.sta_id = old_aux_sta_id; - mvm_ap_sta->sta_id = old_ap_sta_id; - mvmvif->ap_sta_id = old_ap_sta_id; - out_noreset: - kfree(key_data.rsc_tsc); if (ret < 0) ieee80211_restart_hw(mvm->hw); + out_noreset: + kfree(key_data.rsc_tsc); mutex_unlock(&mvm->mutex); @@ -1106,19 +1067,38 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + if (iwl_mvm_is_d0i3_supported(mvm)) { + mutex_lock(&mvm->d0i3_suspend_mutex); + __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); + mutex_unlock(&mvm->d0i3_suspend_mutex); + return 0; + } + return __iwl_mvm_suspend(hw, wowlan, false); } +/* converted data from the different status responses */ +struct iwl_wowlan_status_data { + u16 pattern_number; + u16 qos_seq_ctr[8]; + u32 wakeup_reasons; + u32 wake_packet_length; + u32 wake_packet_bufsize; + const u8 *wake_packet; +}; + static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_wowlan_status *status) + struct iwl_wowlan_status_data *status) { struct sk_buff *pkt = NULL; struct cfg80211_wowlan_wakeup wakeup = { .pattern_idx = -1, }; struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - u32 reasons = le32_to_cpu(status->wakeup_reasons); + u32 reasons = status->wakeup_reasons; if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { wakeup_report = NULL; @@ -1130,7 +1110,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) wakeup.pattern_idx = - le16_to_cpu(status->pattern_number); + status->pattern_number; if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) @@ -1158,8 +1138,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, wakeup.tcp_match = true; if (status->wake_packet_bufsize) { - int pktsize = le32_to_cpu(status->wake_packet_bufsize); - int pktlen = le32_to_cpu(status->wake_packet_length); + int pktsize = status->wake_packet_bufsize; + int pktlen = status->wake_packet_length; const u8 *pktdata = status->wake_packet; struct ieee80211_hdr *hdr = (void *)pktdata; int truncated = pktlen - pktsize; @@ -1239,8 +1219,236 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, kfree_skb(pkt); } +static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc, + struct ieee80211_key_seq *seq) +{ + u64 pn; + + pn = le64_to_cpu(sc->pn); + seq->ccmp.pn[0] = pn >> 40; + seq->ccmp.pn[1] = pn >> 32; + seq->ccmp.pn[2] = pn >> 24; + seq->ccmp.pn[3] = pn >> 16; + seq->ccmp.pn[4] = pn >> 8; + seq->ccmp.pn[5] = pn; +} + +static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, + struct ieee80211_key_seq *seq) +{ + seq->tkip.iv32 = le32_to_cpu(sc->iv32); + seq->tkip.iv16 = le16_to_cpu(sc->iv16); +} + +static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, + struct ieee80211_key_conf *key) +{ + int tid; + + BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); + + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } +} + +static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, + struct ieee80211_key_conf *key) +{ + int tid; + + BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); + + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } +} + +static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, + struct iwl_wowlan_status *status) +{ + union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); + break; + default: + WARN_ON(1); + } +} + +struct iwl_mvm_d3_gtk_iter_data { + struct iwl_wowlan_status *status; + void *last_gtk; + u32 cipher; + bool find_phase, unhandled_cipher; + int num_keys; +}; + +static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mvm_d3_gtk_iter_data *data = _data; + + if (data->unhandled_cipher) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* ignore WEP completely, nothing to do */ + return; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_TKIP: + /* we support these */ + break; + default: + /* everything else (even CMAC for MFP) - disconnect from AP */ + data->unhandled_cipher = true; + return; + } + + data->num_keys++; + + /* + * pairwise key - update sequence counters only; + * note that this assumes no TDLS sessions are active + */ + if (sta) { + struct ieee80211_key_seq seq = {}; + union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; + + if (data->find_phase) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq); + iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); + iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); + break; + } + ieee80211_set_key_tx_seq(key, &seq); + + /* that's it for this key */ + return; + } + + if (data->find_phase) { + data->last_gtk = key; + data->cipher = key->cipher; + return; + } + + if (data->status->num_of_gtk_rekeys) + ieee80211_remove_key(key); + else if (data->last_gtk == key) + iwl_mvm_set_key_rx_seq(key, data->status); +} + +static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status *status) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .status = status, + }; + u32 disconnection_reasons = + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; + + if (!status || !vif->bss_conf.bssid) + return false; + + if (le32_to_cpu(status->wakeup_reasons) & disconnection_reasons) + return false; + + /* find last GTK that we used initially, if any */ + gtkdata.find_phase = true; + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_d3_update_gtks, >kdata); + /* not trying to keep connections with MFP/unhandled ciphers */ + if (gtkdata.unhandled_cipher) + return false; + if (!gtkdata.num_keys) + goto out; + if (!gtkdata.last_gtk) + return false; + + /* + * invalidate all other GTKs that might still exist and update + * the one that we used + */ + gtkdata.find_phase = false; + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_d3_update_gtks, >kdata); + + if (status->num_of_gtk_rekeys) { + struct ieee80211_key_conf *key; + struct { + struct ieee80211_key_conf conf; + u8 key[32]; + } conf = { + .conf.cipher = gtkdata.cipher, + .conf.keyidx = status->gtk.key_index, + }; + + switch (gtkdata.cipher) { + case WLAN_CIPHER_SUITE_CCMP: + conf.conf.keylen = WLAN_KEY_LEN_CCMP; + memcpy(conf.conf.key, status->gtk.decrypt_key, + WLAN_KEY_LEN_CCMP); + break; + case WLAN_CIPHER_SUITE_TKIP: + conf.conf.keylen = WLAN_KEY_LEN_TKIP; + memcpy(conf.conf.key, status->gtk.decrypt_key, 16); + /* leave TX MIC key zeroed, we don't use it anyway */ + memcpy(conf.conf.key + + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + status->gtk.tkip_mic_key, 8); + break; + } + + key = ieee80211_gtk_rekey_add(vif, &conf.conf); + if (IS_ERR(key)) + return false; + iwl_mvm_set_key_rx_seq(key, status); + } + + if (status->num_of_gtk_rekeys) { + __be64 replay_ctr = + cpu_to_be64(le64_to_cpu(status->replay_ctr)); + ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, + (void *)&replay_ctr, GFP_KERNEL); + } + +out: + mvmvif->seqno_valid = true; + /* +0x10 because the set API expects next-to-use, not last-used */ + mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; + + return true; +} + /* releases the MVM mutex */ -static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, +static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { u32 base = mvm->error_event_table; @@ -1251,10 +1459,14 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, } err_info; struct iwl_host_cmd cmd = { .id = WOWLAN_GET_STATUSES, - .flags = CMD_SYNC | CMD_WANT_SKB, + .flags = CMD_WANT_SKB, }; - struct iwl_wowlan_status *status; - int ret, len; + struct iwl_wowlan_status_data status; + struct iwl_wowlan_status *fw_status; + int ret, len, status_size, i; + bool keep; + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvm_ap_sta; iwl_trans_read_mem_bytes(mvm->trans, base, &err_info, sizeof(err_info)); @@ -1273,7 +1485,7 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, } /* only for tracing for now */ - ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL); + ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, 0, 0, NULL); if (ret) IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret); @@ -1287,32 +1499,62 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, if (!cmd.resp_pkt) goto out_unlock; - len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { + status_size = sizeof(*fw_status); + + len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (len < status_size) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } - status = (void *)cmd.resp_pkt->data; + fw_status = (void *)cmd.resp_pkt->data; - if (len - sizeof(struct iwl_cmd_header) != - sizeof(*status) + - ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { + status.pattern_number = le16_to_cpu(fw_status->pattern_number); + for (i = 0; i < 8; i++) + status.qos_seq_ctr[i] = + le16_to_cpu(fw_status->qos_seq_ctr[i]); + status.wakeup_reasons = le32_to_cpu(fw_status->wakeup_reasons); + status.wake_packet_length = + le32_to_cpu(fw_status->wake_packet_length); + status.wake_packet_bufsize = + le32_to_cpu(fw_status->wake_packet_bufsize); + status.wake_packet = fw_status->wake_packet; + + if (len != status_size + ALIGN(status.wake_packet_bufsize, 4)) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } + /* still at hard-coded place 0 for D3 image */ + ap_sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[0], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(ap_sta)) + goto out_free_resp; + + mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = status.qos_seq_ctr[i]; + /* firmware stores last-used value, we store next value */ + seq += 0x10; + mvm_ap_sta->tid_data[i].seq_number = seq; + } + /* now we have all the data we need, unlock to avoid mac80211 issues */ mutex_unlock(&mvm->mutex); - iwl_mvm_report_wakeup_reasons(mvm, vif, status); + iwl_mvm_report_wakeup_reasons(mvm, vif, &status); + + keep = iwl_mvm_setup_connection_keep(mvm, vif, fw_status); + iwl_free_resp(&cmd); - return; + return keep; out_free_resp: iwl_free_resp(&cmd); out_unlock: mutex_unlock(&mvm->mutex); + return false; } static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) @@ -1335,6 +1577,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) #endif } +static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + /* skip the one we keep connection on */ + if (data == vif) + return; + + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_resume_disconnect(vif); +} + static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) { struct iwl_d3_iter_data resume_iter_data = { @@ -1343,6 +1596,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) struct ieee80211_vif *vif = NULL; int ret; enum iwl_d3_status d3_status; + bool keep = false; mutex_lock(&mvm->mutex); @@ -1368,7 +1622,11 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) /* query SRAM first in case we want event logging */ iwl_mvm_read_d3_sram(mvm); - iwl_mvm_query_wakeup_reasons(mvm, vif); + keep = iwl_mvm_query_wakeup_reasons(mvm, vif); +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (keep) + mvm->keep_vif = vif; +#endif /* has unlocked the mutex, so skip that */ goto out; @@ -1376,8 +1634,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) mutex_unlock(&mvm->mutex); out: - if (!test && vif) - ieee80211_resume_disconnect(vif); + if (!test) + ieee80211_iterate_active_interfaces_rtnl(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); /* return 1 to reconfigure the device */ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); @@ -1388,6 +1648,19 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + if (iwl_mvm_is_d0i3_supported(mvm)) { + bool exit_now; + + mutex_lock(&mvm->d0i3_suspend_mutex); + __clear_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); + exit_now = __test_and_clear_bit(D0I3_PENDING_WAKEUP, + &mvm->d0i3_suspend_flags); + mutex_unlock(&mvm->d0i3_suspend_mutex); + if (exit_now) + _iwl_mvm_exit_d0i3(mvm); + return 0; + } + return __iwl_mvm_resume(mvm, false); } @@ -1423,6 +1696,7 @@ static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file) return err; } mvm->d3_test_active = true; + mvm->keep_vif = NULL; return 0; } @@ -1433,10 +1707,14 @@ static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, u32 pme_asserted; while (true) { - pme_asserted = iwl_trans_read_mem32(mvm->trans, - mvm->d3_test_pme_ptr); - if (pme_asserted) - break; + /* read pme_ptr if available */ + if (mvm->d3_test_pme_ptr) { + pme_asserted = iwl_trans_read_mem32(mvm->trans, + mvm->d3_test_pme_ptr); + if (pme_asserted) + break; + } + if (msleep_interruptible(100)) break; } @@ -1447,6 +1725,10 @@ static ssize_t iwl_mvm_d3_test_read(struct file *file, char __user *user_buf, static void iwl_mvm_d3_test_disconn_work_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) { + /* skip the one we keep connection on */ + if (_data == vif) + return; + if (vif->type == NL80211_IFTYPE_STATION) ieee80211_connection_loss(vif); } @@ -1473,7 +1755,7 @@ static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file) ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_d3_test_disconn_work_iter, NULL); + iwl_mvm_d3_test_disconn_work_iter, mvm->keep_vif); ieee80211_wake_queues(mvm->hw); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c new file mode 100644 index 00000000000..2e90ff795c1 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -0,0 +1,596 @@ +/****************************************************************************** + * + * 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 "mvm.h" +#include "debugfs.h" + +static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_dbgfs_pm_mask param, int val) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; + + dbgfs_pm->mask |= param; + + switch (param) { + case MVM_DEBUGFS_PM_KEEP_ALIVE: { + struct ieee80211_hw *hw = mvm->hw; + int dtimper = hw->conf.ps_dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); + if (val * MSEC_PER_SEC < 3 * dtimper_msec) + IWL_WARN(mvm, + "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", + val * MSEC_PER_SEC, 3 * dtimper_msec); + dbgfs_pm->keep_alive_seconds = val; + break; + } + case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: + IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", + val ? "enabled" : "disabled"); + dbgfs_pm->skip_over_dtim = val; + break; + case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: + IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); + dbgfs_pm->skip_dtim_periods = val; + break; + case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); + dbgfs_pm->rx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); + dbgfs_pm->tx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_LPRX_ENA: + IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); + dbgfs_pm->lprx_ena = val; + break; + case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: + IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); + dbgfs_pm->lprx_rssi_threshold = val; + break; + case MVM_DEBUGFS_PM_SNOOZE_ENABLE: + IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); + dbgfs_pm->snooze_ena = val; + break; + case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: + IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); + dbgfs_pm->uapsd_misbehaving = val; + break; + } +} + +static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + enum iwl_dbgfs_pm_mask param; + int val, ret; + + if (!strncmp("keep_alive=", buf, 11)) { + if (sscanf(buf + 11, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_KEEP_ALIVE; + } else if (!strncmp("skip_over_dtim=", buf, 15)) { + if (sscanf(buf + 15, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; + } else if (!strncmp("skip_dtim_periods=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; + } else if (!strncmp("rx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; + } else if (!strncmp("tx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; + } else if (!strncmp("lprx=", buf, 5)) { + if (sscanf(buf + 5, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_ENA; + } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { + if (sscanf(buf + 20, "%d", &val) != 1) + return -EINVAL; + if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < + POWER_LPRX_RSSI_THRESHOLD_MIN) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; + } else if (!strncmp("snooze_enable=", buf, 14)) { + if (sscanf(buf + 14, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; + } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_pm(mvm, vif, param, val); + ret = iwl_mvm_power_update_mac(mvm); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_pm_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[512]; + int bufsz = sizeof(buf); + int pos; + + pos = iwl_mvm_power_mac_dbgfs_read(mvm, vif, buf, bufsz); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_mac_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u8 ap_sta_id; + struct ieee80211_chanctx_conf *chanctx_conf; + char buf[512]; + int bufsz = sizeof(buf); + int pos = 0; + int i; + + mutex_lock(&mvm->mutex); + + ap_sta_id = mvmvif->ap_sta_id; + + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_ADHOC: + pos += scnprintf(buf+pos, bufsz-pos, "type: ibss\n"); + break; + case NL80211_IFTYPE_STATION: + pos += scnprintf(buf+pos, bufsz-pos, "type: bss\n"); + break; + case NL80211_IFTYPE_AP: + pos += scnprintf(buf+pos, bufsz-pos, "type: ap\n"); + break; + case NL80211_IFTYPE_P2P_CLIENT: + pos += scnprintf(buf+pos, bufsz-pos, "type: p2p client\n"); + break; + case NL80211_IFTYPE_P2P_GO: + pos += scnprintf(buf+pos, bufsz-pos, "type: p2p go\n"); + break; + case NL80211_IFTYPE_P2P_DEVICE: + pos += scnprintf(buf+pos, bufsz-pos, "type: p2p dev\n"); + break; + default: + break; + } + + pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", + mvmvif->id, mvmvif->color); + pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", + vif->bss_conf.bssid); + pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); + for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) + pos += scnprintf(buf+pos, bufsz-pos, + "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", + i, mvmvif->queue_params[i].txop, + mvmvif->queue_params[i].cw_min, + mvmvif->queue_params[i].cw_max, + mvmvif->queue_params[i].aifs, + mvmvif->queue_params[i].uapsd); + + if (vif->type == NL80211_IFTYPE_STATION && + ap_sta_id != IWL_MVM_STATION_COUNT) { + struct ieee80211_sta *sta; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], + lockdep_is_held(&mvm->mutex)); + if (!IS_ERR_OR_NULL(sta)) { + struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + + pos += scnprintf(buf+pos, bufsz-pos, + "ap_sta_id %d - reduced Tx power %d\n", + ap_sta_id, + mvm_sta->bt_reduced_txpower); + } + } + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + if (chanctx_conf) + pos += scnprintf(buf+pos, bufsz-pos, + "idle rx chains %d, active rx chains: %d\n", + chanctx_conf->rx_chains_static, + chanctx_conf->rx_chains_dynamic); + rcu_read_unlock(); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, + enum iwl_dbgfs_bf_mask param, int value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + dbgfs_bf->mask |= param; + + switch (param) { + case MVM_DEBUGFS_BF_ENERGY_DELTA: + dbgfs_bf->bf_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: + dbgfs_bf->bf_roaming_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_STATE: + dbgfs_bf->bf_roaming_state = value; + break; + case MVM_DEBUGFS_BF_TEMP_THRESHOLD: + dbgfs_bf->bf_temp_threshold = value; + break; + case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: + dbgfs_bf->bf_temp_fast_filter = value; + break; + case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: + dbgfs_bf->bf_temp_slow_filter = value; + break; + case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: + dbgfs_bf->bf_enable_beacon_filter = value; + break; + case MVM_DEBUGFS_BF_DEBUG_FLAG: + dbgfs_bf->bf_debug_flag = value; + break; + case MVM_DEBUGFS_BF_ESCAPE_TIMER: + dbgfs_bf->bf_escape_timer = value; + break; + case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: + dbgfs_bf->ba_enable_beacon_abort = value; + break; + case MVM_DEBUGFS_BA_ESCAPE_TIMER: + dbgfs_bf->ba_escape_timer = value; + break; + } +} + +static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + enum iwl_dbgfs_bf_mask param; + int value, ret = 0; + + if (!strncmp("bf_energy_delta=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ENERGY_DELTA_MIN || + value > IWL_BF_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || + value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_state=", buf, 17)) { + if (sscanf(buf+17, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_STATE_MIN || + value > IWL_BF_ROAMING_STATE_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_STATE; + } else if (!strncmp("bf_temp_threshold=", buf, 18)) { + if (sscanf(buf+18, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_THRESHOLD_MIN || + value > IWL_BF_TEMP_THRESHOLD_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; + } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_FAST_FILTER_MIN || + value > IWL_BF_TEMP_FAST_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; + } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || + value > IWL_BF_TEMP_SLOW_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; + } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; + } else if (!strncmp("bf_debug_flag=", buf, 14)) { + if (sscanf(buf+14, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_DEBUG_FLAG; + } else if (!strncmp("bf_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ESCAPE_TIMER_MIN || + value > IWL_BF_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ESCAPE_TIMER; + } else if (!strncmp("ba_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BA_ESCAPE_TIMER_MIN || + value > IWL_BA_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BA_ESCAPE_TIMER; + } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { + if (sscanf(buf+23, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_bf(vif, param, value); + if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); + else + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_bf_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = + cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), + .ba_enable_beacon_abort = + cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), + }; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + if (mvmvif->bf_data.bf_enabled) + cmd.bf_enable_beacon_filter = cpu_to_le32(1); + else + cmd.bf_enable_beacon_filter = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", + le32_to_cpu(cmd.bf_energy_delta)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", + le32_to_cpu(cmd.bf_roaming_energy_delta)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", + le32_to_cpu(cmd.bf_roaming_state)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", + le32_to_cpu(cmd.bf_temp_threshold)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", + le32_to_cpu(cmd.bf_temp_fast_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", + le32_to_cpu(cmd.bf_temp_slow_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", + le32_to_cpu(cmd.bf_enable_beacon_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", + le32_to_cpu(cmd.bf_debug_flag)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", + le32_to_cpu(cmd.bf_escape_timer)); + pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", + le32_to_cpu(cmd.ba_escape_timer)); + pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", + le32_to_cpu(cmd.ba_enable_beacon_abort)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_low_latency_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + u8 value; + int ret; + + ret = kstrtou8(buf, 0, &value); + if (ret) + return ret; + if (value > 1) + return -EINVAL; + + mutex_lock(&mvm->mutex); + iwl_mvm_update_low_latency(mvm, vif, value); + mutex_unlock(&mvm->mutex); + + return count; +} + +static ssize_t iwl_dbgfs_low_latency_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[3]; + + buf[0] = mvmvif->low_latency ? '1' : '0'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); +} + +#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) +#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, vif, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ + } while (0) + +MVM_DEBUGFS_READ_FILE_OPS(mac_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); + +void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct dentry *dbgfs_dir = vif->debugfs_dir; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[100]; + + /* + * Check if debugfs directory already exist before creating it. + * This may happen when, for example, resetting hw or suspend-resume + */ + if (!dbgfs_dir || mvmvif->dbgfs_dir) + return; + + mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); + mvmvif->mvm = mvm; + + if (!mvmvif->dbgfs_dir) { + IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", + dbgfs_dir->d_name.name); + return; + } + + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) || + (vif->type == NL80211_IFTYPE_STATION && vif->p2p && + mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM))) + MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | + S_IRUSR); + + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvmvif == mvm->bf_allowed_vif) + MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + + /* + * Create symlink for convenience pointing to interface specific + * debugfs entries for the driver. For example, under + * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ + * find + * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ + */ + snprintf(buf, 100, "../../../%s/%s/%s/%s", + dbgfs_dir->d_parent->d_parent->d_name.name, + dbgfs_dir->d_parent->d_name.name, + dbgfs_dir->d_name.name, + mvmvif->dbgfs_dir->d_name.name); + + mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, + mvm->debugfs_dir, buf); + if (!mvmvif->dbgfs_slink) + IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", + dbgfs_dir->d_name.name); + return; +err: + IWL_ERR(mvm, "Can't create debugfs entity\n"); +} + +void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + debugfs_remove(mvmvif->dbgfs_slink); + mvmvif->dbgfs_slink = NULL; + + debugfs_remove_recursive(mvmvif->dbgfs_dir); + mvmvif->dbgfs_dir = NULL; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index aac81b8984b..29ca72695ea 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -60,33 +60,23 @@ * 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 "debugfs.h" +#include "iwl-fw-error-dump.h" -struct iwl_dbgfs_mvm_ctx { - struct iwl_mvm *mvm; - struct ieee80211_vif *vif; -}; - -static ssize_t iwl_dbgfs_tx_flush_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - - char buf[16]; - int buf_size, ret; + int ret; u32 scd_q_msk; if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) return -EIO; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%x", &scd_q_msk) != 1) return -EINVAL; @@ -99,114 +89,134 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct file *file, return ret; } -static ssize_t iwl_dbgfs_sta_drain_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_sta_drain_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - struct ieee80211_sta *sta; - - char buf[8]; - int buf_size, sta_id, drain, ret; + struct iwl_mvm_sta *mvmsta; + int sta_id, drain, ret; if (!mvm->ucode_loaded || mvm->cur_ucode != IWL_UCODE_REGULAR) return -EIO; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - if (sscanf(buf, "%d %d", &sta_id, &drain) != 2) return -EINVAL; + if (sta_id < 0 || sta_id >= IWL_MVM_STATION_COUNT) + return -EINVAL; + if (drain < 0 || drain > 1) + return -EINVAL; mutex_lock(&mvm->mutex); - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); + + if (!mvmsta) ret = -ENOENT; else - ret = iwl_mvm_drain_sta(mvm, (void *)sta->drv_priv, drain) ? : - count; + ret = iwl_mvm_drain_sta(mvm, mvmsta, drain) ? : count; mutex_unlock(&mvm->mutex); 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; + 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) { struct iwl_mvm *mvm = file->private_data; const struct fw_img *img; - int ofs, len, pos = 0; - size_t bufsz, ret; - char *buf; + unsigned int ofs, len; + size_t ret; u8 *ptr; if (!mvm->ucode_loaded) return -EINVAL; /* default is to dump the entire data segment */ - if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) { - img = &mvm->fw->img[mvm->cur_ucode]; - ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - len = img->sec[IWL_UCODE_SECTION_DATA].len; - } else { + img = &mvm->fw->img[mvm->cur_ucode]; + ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + len = img->sec[IWL_UCODE_SECTION_DATA].len; + + if (mvm->dbgfs_sram_len) { ofs = mvm->dbgfs_sram_offset; len = mvm->dbgfs_sram_len; } - bufsz = len * 4 + 256; - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - ptr = kzalloc(len, GFP_KERNEL); - if (!ptr) { - kfree(buf); + if (!ptr) return -ENOMEM; - } - - pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len); - pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs); iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len); - for (ofs = 0; ofs < len; ofs += 16) { - pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs); - hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos, - bufsz - pos, false); - pos += strlen(buf + pos); - if (bufsz - pos > 0) - buf[pos++] = '\n'; - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len); - kfree(buf); kfree(ptr); return ret; } -static ssize_t iwl_dbgfs_sram_write(struct file *file, - const char __user *user_buf, size_t count, - loff_t *ppos) +static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - char buf[64]; - int buf_size; + const struct fw_img *img; u32 offset, len; + u32 img_offset, img_len; + + if (!mvm->ucode_loaded) + return -EINVAL; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; + img = &mvm->fw->img[mvm->cur_ucode]; + img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset; + img_len = img->sec[IWL_UCODE_SECTION_DATA].len; if (sscanf(buf, "%x,%x", &offset, &len) == 2) { if ((offset & 0x3) || (len & 0x3)) return -EINVAL; + + if (offset + len > img_offset + img_len) + return -EINVAL; + mvm->dbgfs_sram_offset = offset; mvm->dbgfs_sram_len = len; } else { @@ -246,262 +256,50 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - char buf[8] = {}; - int allow; - - if (!mvm->ucode_loaded) - return -EIO; - - if (copy_from_user(buf, user_buf, sizeof(buf))) - return -EFAULT; - - if (sscanf(buf, "%d", &allow) != 1) - return -EINVAL; - - IWL_DEBUG_POWER(mvm, "%s device power down\n", - allow ? "allow" : "prevent"); - - /* - * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it - */ - - return count; -} - -static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_mvm *mvm = file->private_data; - char buf[8] = {}; - int allow; - - if (copy_from_user(buf, user_buf, sizeof(buf))) - return -EFAULT; - - if (sscanf(buf, "%d", &allow) != 1) - return -EINVAL; - - IWL_DEBUG_POWER(mvm, "%s device power down in d3\n", - allow ? "allow" : "prevent"); - - /* - * TODO: When WoWLAN FW alive notification happens, driver will send - * REPLY_DEBUG_CMD setting power_down_allow flag according to - * mvm->prevent_power_down_d3 - */ - mvm->prevent_power_down_d3 = !allow; + char buf[64]; + int bufsz = sizeof(buf); + int pos = 0; - return count; -} + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n", + mvm->disable_power_off); + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n", + mvm->disable_power_off_d3); -static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - enum iwl_dbgfs_pm_mask param, int val) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; - - dbgfs_pm->mask |= param; - - switch (param) { - case MVM_DEBUGFS_PM_KEEP_ALIVE: { - struct ieee80211_hw *hw = mvm->hw; - int dtimper = hw->conf.ps_dtim_period ?: 1; - int dtimper_msec = dtimper * vif->bss_conf.beacon_int; - - IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); - if (val * MSEC_PER_SEC < 3 * dtimper_msec) { - IWL_WARN(mvm, - "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", - val * MSEC_PER_SEC, 3 * dtimper_msec); - } - dbgfs_pm->keep_alive_seconds = val; - break; - } - case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: - IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", - val ? "enabled" : "disabled"); - dbgfs_pm->skip_over_dtim = val; - break; - case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: - IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); - dbgfs_pm->skip_dtim_periods = val; - break; - case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); - dbgfs_pm->rx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: - IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); - dbgfs_pm->tx_data_timeout = val; - break; - case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: - IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); - dbgfs_pm->disable_power_off = val; - case MVM_DEBUGFS_PM_LPRX_ENA: - IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); - dbgfs_pm->lprx_ena = val; - break; - case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: - IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); - dbgfs_pm->lprx_rssi_threshold = val; - break; - case MVM_DEBUGFS_PM_SNOOZE_ENABLE: - IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); - dbgfs_pm->snooze_ena = val; - break; - } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -static ssize_t iwl_dbgfs_pm_params_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) { - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - enum iwl_dbgfs_pm_mask param; - char buf[32] = {}; - int val; - int ret; + int ret, val; - if (copy_from_user(buf, user_buf, sizeof(buf))) - return -EFAULT; + if (!mvm->ucode_loaded) + return -EIO; - if (!strncmp("keep_alive=", buf, 11)) { - if (sscanf(buf + 11, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_KEEP_ALIVE; - } else if (!strncmp("skip_over_dtim=", buf, 15)) { - if (sscanf(buf + 15, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; - } else if (!strncmp("skip_dtim_periods=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; - } else if (!strncmp("rx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; - } else if (!strncmp("tx_data_timeout=", buf, 16)) { - if (sscanf(buf + 16, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; - } else if (!strncmp("disable_power_off=", buf, 18)) { - if (sscanf(buf + 18, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; - } else if (!strncmp("lprx=", buf, 5)) { - if (sscanf(buf + 5, "%d", &val) != 1) - return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_ENA; - } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { - if (sscanf(buf + 20, "%d", &val) != 1) - return -EINVAL; - if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < - POWER_LPRX_RSSI_THRESHOLD_MIN) + if (!strncmp("disable_power_off_d0=", buf, 21)) { + if (sscanf(buf + 21, "%d", &val) != 1) return -EINVAL; - param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; - } else if (!strncmp("snooze_enable=", buf, 14)) { - if (sscanf(buf + 14, "%d", &val) != 1) + mvm->disable_power_off = val; + } else if (!strncmp("disable_power_off_d3=", buf, 21)) { + if (sscanf(buf + 21, "%d", &val) != 1) return -EINVAL; - param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; + mvm->disable_power_off_d3 = val; } else { return -EINVAL; } mutex_lock(&mvm->mutex); - iwl_dbgfs_update_pm(mvm, vif, param, val); - ret = iwl_mvm_power_update_mode(mvm, vif); + ret = iwl_mvm_power_update_device(mvm); mutex_unlock(&mvm->mutex); return ret ?: count; } -static ssize_t iwl_dbgfs_pm_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - char buf[512]; - int bufsz = sizeof(buf); - int pos; - - pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_mac_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - u8 ap_sta_id; - struct ieee80211_chanctx_conf *chanctx_conf; - char buf[512]; - int bufsz = sizeof(buf); - int pos = 0; - int i; - - mutex_lock(&mvm->mutex); - - ap_sta_id = mvmvif->ap_sta_id; - - pos += scnprintf(buf+pos, bufsz-pos, "mac id/color: %d / %d\n", - mvmvif->id, mvmvif->color); - pos += scnprintf(buf+pos, bufsz-pos, "bssid: %pM\n", - vif->bss_conf.bssid); - pos += scnprintf(buf+pos, bufsz-pos, "QoS:\n"); - for (i = 0; i < ARRAY_SIZE(mvmvif->queue_params); i++) { - pos += scnprintf(buf+pos, bufsz-pos, - "\t%d: txop:%d - cw_min:%d - cw_max = %d - aifs = %d upasd = %d\n", - i, mvmvif->queue_params[i].txop, - mvmvif->queue_params[i].cw_min, - mvmvif->queue_params[i].cw_max, - mvmvif->queue_params[i].aifs, - mvmvif->queue_params[i].uapsd); - } - - if (vif->type == NL80211_IFTYPE_STATION && - ap_sta_id != IWL_MVM_STATION_COUNT) { - struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvm_sta; - - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], - lockdep_is_held(&mvm->mutex)); - mvm_sta = (void *)sta->drv_priv; - pos += scnprintf(buf+pos, bufsz-pos, - "ap_sta_id %d - reduced Tx power %d\n", - ap_sta_id, mvm_sta->bt_reduced_txpower); - } - - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (chanctx_conf) { - pos += scnprintf(buf+pos, bufsz-pos, - "idle rx chains %d, active rx chains: %d\n", - chanctx_conf->rx_chains_static, - chanctx_conf->rx_chains_dynamic); - } - rcu_read_unlock(); - - mutex_unlock(&mvm->mutex); - - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - #define BT_MBOX_MSG(_notif, _num, _field) \ ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ >> BT_MBOX##_num##_##_field##_POS) @@ -581,15 +379,24 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, BT_MBOX_PRINT(3, UPDATE_REQUEST, true); pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n", - notif->bt_status); + notif->bt_status); pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n", - notif->bt_open_conn); + notif->bt_open_conn); pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n", - notif->bt_traffic_load); + notif->bt_traffic_load); pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n", - notif->bt_agg_traffic_load); + notif->bt_agg_traffic_load); pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); + notif->bt_ci_compliance); + pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", + le32_to_cpu(notif->primary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", + 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); @@ -600,6 +407,54 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, } #undef BT_MBOX_PRINT +static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; + char buf[256]; + int bufsz = sizeof(buf); + int pos = 0; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, + "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n", + le64_to_cpu(cmd->bt_primary_ci), + !!cmd->co_run_bw_primary); + pos += scnprintf(buf+pos, bufsz-pos, + "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n", + le64_to_cpu(cmd->bt_secondary_ci), + !!cmd->co_run_bw_secondary); + + pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", + iwl_bt_ack_kill_msk[mvm->bt_kill_msk]); + pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n", + iwl_bt_cts_kill_msk[mvm->bt_kill_msk]); + + mutex_unlock(&mvm->mutex); + + 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, \ @@ -615,9 +470,11 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, int pos = 0; char *buf; int ret; - int bufsz = sizeof(struct mvm_statistics_rx_phy) * 20 + - sizeof(struct mvm_statistics_rx_non_phy) * 10 + - sizeof(struct mvm_statistics_rx_ht_phy) * 10 + 200; + /* 43 is the size of each data line, 33 is the size of each header */ + size_t bufsz = + ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) + + (4 * 33) + 1; + struct mvm_statistics_rx_phy *ofdm; struct mvm_statistics_rx_phy *cck; struct mvm_statistics_rx_non_phy *general; @@ -712,6 +569,7 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b); PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c); PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills); + PRINT_STATS_LE32("mac_id", general->mac_id); PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, @@ -736,11 +594,83 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, } #undef PRINT_STAT_LE32 -static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t iwl_dbgfs_frame_stats_read(struct iwl_mvm *mvm, + char __user *user_buf, size_t count, + loff_t *ppos, + struct iwl_mvm_frame_stats *stats) +{ + char *buff, *pos, *endpos; + int idx, i; + int ret; + static const size_t bufsz = 1024; + + buff = kmalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + spin_lock_bh(&mvm->drv_stats_lock); + + 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(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(pos, endpos - pos, "NGI/SGI\t\t:\t%d/%d\n", + stats->ngi_frames, + stats->sgi_frames); + pos += scnprintf(pos, endpos - pos, "SISO/MIMO2\t:\t%d/%d\n", + stats->siso_frames, + stats->mimo2_frames); + pos += scnprintf(pos, endpos - pos, "FAIL/SCSS\t:\t%d/%d\n", + stats->fail_frames, + stats->success_frames); + pos += scnprintf(pos, endpos - pos, "MPDUs agg\t:\t%d\n", + stats->agg_frames); + pos += scnprintf(pos, endpos - pos, "A-MPDUs\t\t:\t%d\n", + stats->ampdu_count); + 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(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(pos, endpos - pos, "Rate[%d]: ", + (int)(ARRAY_SIZE(stats->last_rates) - i)); + 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 - buff); + kfree(buff); + + return ret; +} + +static ssize_t iwl_dbgfs_drv_rx_stats_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; + + return iwl_dbgfs_frame_stats_read(mvm, user_buf, count, ppos, + &mvm->drv_rx_stats); +} + +static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ int ret; mutex_lock(&mvm->mutex); @@ -750,227 +680,249 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, mvm->restart_fw++; /* take the return value to make compiler happy - it will fail anyway */ - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, CMD_SYNC, 0, NULL); + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_ERROR, 0, 0, NULL); mutex_unlock(&mvm->mutex); return count; } -static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, - enum iwl_dbgfs_bf_mask param, int value) +static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + iwl_force_nmi(mvm->trans); + + return count; +} + +static ssize_t +iwl_dbgfs_scan_ant_rxchain_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int pos = 0; + char buf[32]; + const size_t bufsz = sizeof(buf); + + /* print which antennas were set for the scan command by the user */ + pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: "); + if (mvm->scan_rx_ant & ANT_A) + pos += scnprintf(buf + pos, bufsz - pos, "A"); + if (mvm->scan_rx_ant & ANT_B) + pos += scnprintf(buf + pos, bufsz - pos, "B"); + if (mvm->scan_rx_ant & ANT_C) + pos += scnprintf(buf + pos, bufsz - pos, "C"); + pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + u8 scan_rx_ant; + + if (sscanf(buf, "%hhx", &scan_rx_ant) != 1) + return -EINVAL; + if (scan_rx_ant > ANT_ABC) + return -EINVAL; + if (scan_rx_ant & ~mvm->fw->valid_rx_ant) + return -EINVAL; + + mvm->scan_rx_ant = scan_rx_ant; + + return count; +} + +#define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; - - dbgfs_bf->mask |= param; - - switch (param) { - case MVM_DEBUGFS_BF_ENERGY_DELTA: - dbgfs_bf->bf_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: - dbgfs_bf->bf_roaming_energy_delta = value; - break; - case MVM_DEBUGFS_BF_ROAMING_STATE: - dbgfs_bf->bf_roaming_state = value; - break; - case MVM_DEBUGFS_BF_TEMP_THRESHOLD: - dbgfs_bf->bf_temp_threshold = value; - break; - case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: - dbgfs_bf->bf_temp_fast_filter = value; - break; - case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: - dbgfs_bf->bf_temp_slow_filter = value; - break; - case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: - dbgfs_bf->bf_enable_beacon_filter = value; - break; - case MVM_DEBUGFS_BF_DEBUG_FLAG: - dbgfs_bf->bf_debug_flag = value; - break; - case MVM_DEBUGFS_BF_ESCAPE_TIMER: - dbgfs_bf->bf_escape_timer = value; - break; - case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: - dbgfs_bf->ba_enable_beacon_abort = value; - break; - case MVM_DEBUGFS_BA_ESCAPE_TIMER: - dbgfs_bf->ba_escape_timer = value; - break; + struct iwl_mvm *mvm = file->private_data; + struct iwl_bcast_filter_cmd cmd; + const struct iwl_fw_bcast_filter *filter; + char *buf; + int bufsz = 1024; + int i, j, pos = 0; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { + ADD_TEXT("None\n"); + mutex_unlock(&mvm->mutex); + goto out; } + mutex_unlock(&mvm->mutex); + + for (i = 0; cmd.filters[i].attrs[0].mask; i++) { + filter = &cmd.filters[i]; + + ADD_TEXT("Filter [%d]:\n", i); + ADD_TEXT("\tDiscard=%d\n", filter->discard); + ADD_TEXT("\tFrame Type: %s\n", + filter->frame_type ? "IPv4" : "Generic"); + + for (j = 0; j < ARRAY_SIZE(filter->attrs); j++) { + const struct iwl_fw_bcast_filter_attr *attr; + + attr = &filter->attrs[j]; + if (!attr->mask) + break; + + ADD_TEXT("\tAttr [%d]: offset=%d (from %s), mask=0x%x, value=0x%x reserved=0x%x\n", + j, attr->offset, + attr->offset_type ? "IP End" : + "Payload Start", + be32_to_cpu(attr->mask), + be32_to_cpu(attr->val), + le16_to_cpu(attr->reserved1)); + } + } +out: + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; } -static ssize_t iwl_dbgfs_bf_params_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t iwl_dbgfs_bcast_filters_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) { - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm *mvm = mvmvif->dbgfs_data; - enum iwl_dbgfs_bf_mask param; - char buf[256]; - int buf_size; - int value; - int ret = 0; + int pos, next_pos; + struct iwl_fw_bcast_filter filter = {}; + struct iwl_bcast_filter_cmd cmd; + u32 filter_id, attr_id, mask, value; + int err = 0; + + if (sscanf(buf, "%d %hhi %hhi %n", &filter_id, &filter.discard, + &filter.frame_type, &pos) != 3) + return -EINVAL; - memset(buf, 0, sizeof(buf)); - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; + if (filter_id >= ARRAY_SIZE(mvm->dbgfs_bcast_filtering.cmd.filters) || + filter.frame_type > BCAST_FILTER_FRAME_TYPE_IPV4) + return -EINVAL; - if (!strncmp("bf_energy_delta=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ENERGY_DELTA_MIN || - value > IWL_BF_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || - value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; - } else if (!strncmp("bf_roaming_state=", buf, 17)) { - if (sscanf(buf+17, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ROAMING_STATE_MIN || - value > IWL_BF_ROAMING_STATE_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ROAMING_STATE; - } else if (!strncmp("bf_temp_threshold=", buf, 18)) { - if (sscanf(buf+18, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_THRESHOLD_MIN || - value > IWL_BF_TEMP_THRESHOLD_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; - } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_FAST_FILTER_MIN || - value > IWL_BF_TEMP_FAST_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; - } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { - if (sscanf(buf+20, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || - value > IWL_BF_TEMP_SLOW_FILTER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; - } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { - if (sscanf(buf+24, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; - } else if (!strncmp("bf_debug_flag=", buf, 14)) { - if (sscanf(buf+14, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) - return -EINVAL; - param = MVM_DEBUGFS_BF_DEBUG_FLAG; - } else if (!strncmp("bf_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BF_ESCAPE_TIMER_MIN || - value > IWL_BF_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BF_ESCAPE_TIMER; - } else if (!strncmp("ba_escape_timer=", buf, 16)) { - if (sscanf(buf+16, "%d", &value) != 1) - return -EINVAL; - if (value < IWL_BA_ESCAPE_TIMER_MIN || - value > IWL_BA_ESCAPE_TIMER_MAX) - return -EINVAL; - param = MVM_DEBUGFS_BA_ESCAPE_TIMER; - } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { - if (sscanf(buf+23, "%d", &value) != 1) - return -EINVAL; - if (value < 0 || value > 1) + for (attr_id = 0; attr_id < ARRAY_SIZE(filter.attrs); + attr_id++) { + struct iwl_fw_bcast_filter_attr *attr = + &filter.attrs[attr_id]; + + if (pos >= count) + break; + + if (sscanf(&buf[pos], "%hhi %hhi %i %i %n", + &attr->offset, &attr->offset_type, + &mask, &value, &next_pos) != 4) return -EINVAL; - param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; - } else { - return -EINVAL; + + attr->mask = cpu_to_be32(mask); + attr->val = cpu_to_be32(value); + if (mask) + filter.num_attrs++; + + pos += next_pos; } mutex_lock(&mvm->mutex); - iwl_dbgfs_update_bf(vif, param, value); - if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) { - ret = iwl_mvm_disable_beacon_filter(mvm, vif); - } else { - ret = iwl_mvm_enable_beacon_filter(mvm, vif); + memcpy(&mvm->dbgfs_bcast_filtering.cmd.filters[filter_id], + &filter, sizeof(filter)); + + /* send updated bcast filtering configuration */ + if (mvm->dbgfs_bcast_filtering.override && + iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) + err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, + sizeof(cmd), &cmd); + mutex_unlock(&mvm->mutex); + + return err ?: count; +} + +static ssize_t iwl_dbgfs_bcast_filters_macs_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct iwl_bcast_filter_cmd cmd; + char *buf; + int bufsz = 1024; + int i, pos = 0; + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&mvm->mutex); + if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) { + ADD_TEXT("None\n"); + mutex_unlock(&mvm->mutex); + goto out; } mutex_unlock(&mvm->mutex); - return ret ?: count; + for (i = 0; i < ARRAY_SIZE(cmd.macs); i++) { + const struct iwl_fw_bcast_mac *mac = &cmd.macs[i]; + + ADD_TEXT("Mac [%d]: discard=%d attached_filters=0x%x\n", + i, mac->default_discard, mac->attached_filters); + } +out: + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; } -static ssize_t iwl_dbgfs_bf_params_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t iwl_dbgfs_bcast_filters_macs_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) { - struct ieee80211_vif *vif = file->private_data; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = - cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), - .ba_enable_beacon_abort = - cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), - }; - - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - if (mvmvif->bf_data.bf_enabled) - cmd.bf_enable_beacon_filter = cpu_to_le32(1); - else - cmd.bf_enable_beacon_filter = 0; - - pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", - le32_to_cpu(cmd.bf_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", - le32_to_cpu(cmd.bf_roaming_energy_delta)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", - le32_to_cpu(cmd.bf_roaming_state)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", - le32_to_cpu(cmd.bf_temp_threshold)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", - le32_to_cpu(cmd.bf_temp_fast_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", - le32_to_cpu(cmd.bf_temp_slow_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", - le32_to_cpu(cmd.bf_enable_beacon_filter)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", - le32_to_cpu(cmd.bf_debug_flag)); - pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", - le32_to_cpu(cmd.bf_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", - le32_to_cpu(cmd.ba_escape_timer)); - pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", - le32_to_cpu(cmd.ba_enable_beacon_abort)); + struct iwl_bcast_filter_cmd cmd; + struct iwl_fw_bcast_mac mac = {}; + u32 mac_id, attached_filters; + int err = 0; - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); + if (!mvm->bcast_filters) + return -ENOENT; + + if (sscanf(buf, "%d %hhi %i", &mac_id, &mac.default_discard, + &attached_filters) != 3) + return -EINVAL; + + if (mac_id >= ARRAY_SIZE(cmd.macs) || + mac.default_discard > 1 || + attached_filters >= BIT(ARRAY_SIZE(cmd.filters))) + return -EINVAL; + + mac.attached_filters = cpu_to_le16(attached_filters); + + mutex_lock(&mvm->mutex); + memcpy(&mvm->dbgfs_bcast_filtering.cmd.macs[mac_id], + &mac, sizeof(mac)); + + /* send updated bcast filtering configuration */ + if (mvm->dbgfs_bcast_filtering.override && + iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) + err = iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, + sizeof(cmd), &cmd); + mutex_unlock(&mvm->mutex); + + return err ?: count; } +#endif #ifdef CONFIG_PM_SLEEP -static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_d3_sram_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - struct iwl_mvm *mvm = file->private_data; - char buf[8] = {}; int store; - if (copy_from_user(buf, user_buf, sizeof(buf))) - return -EFAULT; - if (sscanf(buf, "%d", &store) != 1) return -EINVAL; @@ -1023,74 +975,197 @@ static ssize_t iwl_dbgfs_d3_sram_read(struct file *file, char __user *user_buf, } #endif -#define MVM_DEBUGFS_READ_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ +#define PRINT_MVM_REF(ref) do { \ + if (test_bit(ref, mvm->ref_bitmap)) \ + pos += scnprintf(buf + pos, bufsz - pos, \ + "\t(0x%lx) %s\n", \ + BIT(ref), #ref); \ +} while (0) + +static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int pos = 0; + char buf[256]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "taken mvm refs: 0x%lx\n", + mvm->ref_bitmap[0]); + + PRINT_MVM_REF(IWL_MVM_REF_UCODE_DOWN); + PRINT_MVM_REF(IWL_MVM_REF_SCAN); + PRINT_MVM_REF(IWL_MVM_REF_ROC); + PRINT_MVM_REF(IWL_MVM_REF_P2P_CLIENT); + PRINT_MVM_REF(IWL_MVM_REF_AP_IBSS); + PRINT_MVM_REF(IWL_MVM_REF_USER); + PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; +static ssize_t iwl_dbgfs_d0i3_refs_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + unsigned long value; + int ret; + bool taken; -#define MVM_DEBUGFS_WRITE_FILE_OPS(name) \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -}; + ret = kstrtoul(buf, 10, &value); + if (ret < 0) + return ret; -#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, mvm, \ - &iwl_dbgfs_##name##_ops)) \ - goto err; \ - } while (0) + mutex_lock(&mvm->mutex); -#define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, vif, \ + taken = test_bit(IWL_MVM_REF_USER, mvm->ref_bitmap); + if (value == 1 && !taken) + iwl_mvm_ref(mvm, IWL_MVM_REF_USER); + else if (value == 0 && taken) + iwl_mvm_unref(mvm, IWL_MVM_REF_USER); + else + ret = -EINVAL; + + mutex_unlock(&mvm->mutex); + + if (ret < 0) + return ret; + return count; +} + +#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_mvm) +#define MVM_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ + if (!debugfs_create_file(alias, mode, parent, mvm, \ &iwl_dbgfs_##name##_ops)) \ goto err; \ } while (0) +#define MVM_DEBUGFS_ADD_FILE(name, parent, mode) \ + MVM_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +static ssize_t +iwl_dbgfs_prph_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int pos = 0; + char buf[32]; + const size_t bufsz = sizeof(buf); + + if (!mvm->dbgfs_prph_reg_addr) + return -EINVAL; + + pos += scnprintf(buf + pos, bufsz - pos, "Reg 0x%x: (0x%x)\n", + mvm->dbgfs_prph_reg_addr, + iwl_read_prph(mvm->trans, mvm->dbgfs_prph_reg_addr)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +iwl_dbgfs_prph_reg_write(struct iwl_mvm *mvm, char *buf, + size_t count, loff_t *ppos) +{ + u8 args; + u32 value; + + args = sscanf(buf, "%i %i", &mvm->dbgfs_prph_reg_addr, &value); + /* if we only want to set the reg address - nothing more to do */ + if (args == 1) + goto out; + + /* otherwise, make sure we have both address and value */ + if (args != 2) + return -EINVAL; + + iwl_write_prph(mvm->trans, mvm->dbgfs_prph_reg_addr, value); +out: + return count; +} + +MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ -MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush); -MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram); +MVM_DEBUGFS_WRITE_FILE_OPS(tx_flush, 16); +MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram, 64); MVM_DEBUGFS_READ_FILE_OPS(stations); MVM_DEBUGFS_READ_FILE_OPS(bt_notif); -MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); -MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); +MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off, 64); MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); -MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); -#ifdef CONFIG_PM_SLEEP -MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); +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); #endif -/* Interface specific debugfs entries */ -MVM_DEBUGFS_READ_FILE_OPS(mac_params); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params); -MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params); +#ifdef CONFIG_PM_SLEEP +MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram, 8); +#endif 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(power_down_allow, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); + MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, + S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); + 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); + MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { + bcast_dir = debugfs_create_dir("bcast_filtering", + mvm->debugfs_dir); + if (!bcast_dir) + goto err; + + if (!debugfs_create_bool("override", S_IRUSR | S_IWUSR, + bcast_dir, + &mvm->dbgfs_bcast_filtering.override)) + goto err; + + MVM_DEBUGFS_ADD_FILE_ALIAS("filters", bcast_filters, + bcast_dir, S_IWUSR | S_IRUSR); + MVM_DEBUGFS_ADD_FILE_ALIAS("macs", bcast_filters_macs, + bcast_dir, S_IWUSR | S_IRUSR); + } +#endif + #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); @@ -1099,6 +1174,19 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) goto err; #endif + if (!debugfs_create_blob("nvm_hw", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_hw_blob)) + goto err; + if (!debugfs_create_blob("nvm_sw", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_sw_blob)) + goto err; + if (!debugfs_create_blob("nvm_calib", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_calib_blob)) + goto err; + if (!debugfs_create_blob("nvm_prod", S_IRUSR, + mvm->debugfs_dir, &mvm->nvm_prod_blob)) + goto err; + /* * Create a symlink with mac80211. It will be removed when mac80211 * exists (before the opmode exists which removes the target.) @@ -1114,72 +1202,3 @@ err: IWL_ERR(mvm, "Can't create the mvm debugfs directory\n"); return -ENOMEM; } - -void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct dentry *dbgfs_dir = vif->debugfs_dir; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - char buf[100]; - - /* - * Check if debugfs directory already exist before creating it. - * This may happen when, for example, resetting hw or suspend-resume - */ - if (!dbgfs_dir || mvmvif->dbgfs_dir) - return; - - mvmvif->dbgfs_dir = debugfs_create_dir("iwlmvm", dbgfs_dir); - mvmvif->dbgfs_data = mvm; - - if (!mvmvif->dbgfs_dir) { - IWL_ERR(mvm, "Failed to create debugfs directory under %s\n", - dbgfs_dir->d_name.name); - return; - } - - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && - vif->type == NL80211_IFTYPE_STATION && !vif->p2p) - MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | - S_IRUSR); - - MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, - S_IRUSR); - - if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && - mvmvif == mvm->bf_allowed_vif) - MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, - S_IRUSR | S_IWUSR); - - /* - * Create symlink for convenience pointing to interface specific - * debugfs entries for the driver. For example, under - * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmvm/ - * find - * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmvm/ - */ - snprintf(buf, 100, "../../../%s/%s/%s/%s", - dbgfs_dir->d_parent->d_parent->d_name.name, - dbgfs_dir->d_parent->d_name.name, - dbgfs_dir->d_name.name, - mvmvif->dbgfs_dir->d_name.name); - - mvmvif->dbgfs_slink = debugfs_create_symlink(dbgfs_dir->d_name.name, - mvm->debugfs_dir, buf); - if (!mvmvif->dbgfs_slink) - IWL_ERR(mvm, "Can't create debugfs symbolic link under %s\n", - dbgfs_dir->d_name.name); - return; -err: - IWL_ERR(mvm, "Can't create debugfs entity\n"); -} - -void iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - - debugfs_remove(mvmvif->dbgfs_slink); - mvmvif->dbgfs_slink = NULL; - - debugfs_remove_recursive(mvmvif->dbgfs_dir); - mvmvif->dbgfs_dir = NULL; -} diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.h b/drivers/net/wireless/iwlwifi/mvm/debugfs.h new file mode 100644 index 00000000000..e3a9774af49 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * 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. + * + *****************************************************************************/ + +#define MVM_DEBUGFS_READ_FILE_OPS(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +} + +#define MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + argtype *arg = file->private_data; \ + char buf[buflen] = {}; \ + size_t buf_size = min(count, sizeof(buf) - 1); \ + \ + if (copy_from_user(buf, user_buf, buf_size)) \ + return -EFAULT; \ + \ + return iwl_dbgfs_##name##_write(arg, buf, buf_size, ppos); \ +} \ + +#define _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ +MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; + +#define _MVM_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ +MVM_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ +}; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h index 05c61d6f384..5fe82c29c8a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -70,39 +70,34 @@ /** * enum iwl_bt_coex_flags - flags for BT_COEX command - * @BT_CH_PRIMARY_EN: - * @BT_CH_SECONDARY_EN: - * @BT_NOTIF_COEX_OFF: * @BT_COEX_MODE_POS: * @BT_COEX_MODE_MSK: * @BT_COEX_DISABLE: * @BT_COEX_2W: * @BT_COEX_3W: * @BT_COEX_NW: - * @BT_USE_DEFAULTS: - * @BT_SYNC_2_BT_DISABLE: - * @BT_COEX_CORUNNING_TBL_EN: + * @BT_COEX_SYNC2SCO: + * @BT_COEX_CORUNNING: + * @BT_COEX_MPLUT: + * + * The COEX_MODE must be set for each command. Even if it is not changed. */ enum iwl_bt_coex_flags { - BT_CH_PRIMARY_EN = BIT(0), - BT_CH_SECONDARY_EN = BIT(1), - BT_NOTIF_COEX_OFF = BIT(2), BT_COEX_MODE_POS = 3, BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS, BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS, BT_COEX_2W = 0x1 << BT_COEX_MODE_POS, BT_COEX_3W = 0x2 << BT_COEX_MODE_POS, BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, - BT_USE_DEFAULTS = BIT(6), - BT_SYNC_2_BT_DISABLE = BIT(7), - /* - * For future use - when the flags will be enlarged - * BT_COEX_CORUNNING_TBL_EN = BIT(8), - */ + BT_COEX_SYNC2SCO = BIT(7), + BT_COEX_CORUNNING = BIT(8), + BT_COEX_MPLUT = BIT(9), }; /* * indicates what has changed in the BT_COEX command. + * BT_VALID_ENABLE must be set for each command. Commands without this bit will + * discarded by the firmware */ enum iwl_bt_coex_valid_bit_msk { BT_VALID_ENABLE = BIT(0), @@ -121,11 +116,9 @@ enum iwl_bt_coex_valid_bit_msk { BT_VALID_CORUN_LUT_40 = BIT(13), BT_VALID_ANT_ISOLATION = BIT(14), BT_VALID_ANT_ISOLATION_THRS = BIT(15), - /* - * For future use - when the valid flags will be enlarged - * BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), - * BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), - */ + BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), + BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), + BT_VALID_SYNC_TO_SCO = BIT(18), }; /** @@ -142,47 +135,93 @@ enum iwl_bt_reduced_tx_power { BT_REDUCED_TX_POWER_DATA = BIT(1), }; +enum iwl_bt_coex_lut_type { + BT_COEX_TIGHT_LUT = 0, + BT_COEX_LOOSE_LUT, + BT_COEX_TX_DIS_LUT, + + BT_COEX_MAX_LUT, + BT_COEX_INVALID_LUT = 0xff, +}; /* BT_COEX_DECISION_LUT_INDEX_API_E_VER_1 */ + #define BT_COEX_LUT_SIZE (12) +#define BT_COEX_CORUN_LUT_SIZE (32) +#define BT_COEX_MULTI_PRIO_LUT_SIZE (2) +#define BT_COEX_BOOST_SIZE (4) +#define BT_REDUCED_TX_POWER_BIT BIT(7) /** * struct iwl_bt_coex_cmd - bt coex configuration command * @flags:&enum iwl_bt_coex_flags - * @lead_time: * @max_kill: - * @bt3_time_t7_value: - * @kill_ack_msk: - * @kill_cts_msk: - * @bt3_prio_sample_time: - * @bt3_timer_t2_value: - * @bt4_reaction_time: - * @decision_lut[12]: * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power - * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk - * @bt_prio_boost: values for PTA boost register + * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT + * should be set by default + * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT + * should be set by default + * @bt4_antenna_isolation: antenna isolation + * @bt4_antenna_isolation_thr: antenna threshold value + * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency + * @bt4_tx_rx_max_freq0: TxRx max frequency + * @bt_prio_boost: BT priority boost registers * @wifi_tx_prio_boost: SW boost of wifi tx priority * @wifi_rx_prio_boost: SW boost of wifi rx priority + * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK. + * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS. + * @decision_lut: PTA decision LUT, per Prio-Ch + * @bt4_multiprio_lut: multi priority LUT configuration + * @bt4_corun_lut20: co-running 20 MHz LUT configuration + * @bt4_corun_lut40: co-running 40 MHz LUT configuration + * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk * * The structure is used for the BT_COEX command. */ struct iwl_bt_coex_cmd { - u8 flags; - u8 lead_time; + __le32 flags; u8 max_kill; - u8 bt3_time_t7_value; + u8 bt_reduced_tx_power; + u8 override_primary_lut; + u8 override_secondary_lut; + + u8 bt4_antenna_isolation; + u8 bt4_antenna_isolation_thr; + u8 bt4_tx_tx_delta_freq_thr; + u8 bt4_tx_rx_max_freq0; + + __le32 bt_prio_boost[BT_COEX_BOOST_SIZE]; + __le32 wifi_tx_prio_boost; + __le32 wifi_rx_prio_boost; __le32 kill_ack_msk; __le32 kill_cts_msk; - u8 bt3_prio_sample_time; - u8 bt3_timer_t2_value; - __le16 bt4_reaction_time; - __le32 decision_lut[BT_COEX_LUT_SIZE]; - u8 bt_reduced_tx_power; - u8 reserved; - __le16 valid_bit_msk; - __le32 bt_prio_boost; - u8 reserved2; - u8 wifi_tx_prio_boost; - __le16 wifi_rx_prio_boost; -} __packed; /* BT_COEX_CMD_API_S_VER_3 */ + + __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; + __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; + __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE]; + __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE]; + + __le32 valid_bit_msk; +} __packed; /* BT_COEX_CMD_API_S_VER_5 */ + +/** + * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command + * @bt_primary_ci: + * @bt_secondary_ci: + * @co_run_bw_primary: + * @co_run_bw_secondary: + * @primary_ch_phy_id: + * @secondary_ch_phy_id: + * + * Used for BT_COEX_CI command + */ +struct iwl_bt_coex_ci_cmd { + __le64 bt_primary_ci; + __le64 bt_secondary_ci; + + u8 co_run_bw_primary; + u8 co_run_bw_secondary; + u8 primary_ch_phy_id; + u8 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_1 */ #define BT_MBOX(n_dw, _msg, _pos, _nbits) \ BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ @@ -244,24 +283,40 @@ enum iwl_bt_mxbox_dw3 { ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ >> BT_MBOX##_num##_##_field##_POS) +enum iwl_bt_activity_grading { + BT_OFF = 0, + BT_ON_NO_CONNECTION = 1, + BT_LOW_TRAFFIC = 2, + BT_HIGH_TRAFFIC = 3, +}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */ + /** * struct iwl_bt_coex_profile_notif - notification about BT coex * @mbox_msg: message from BT to WiFi - * @:bt_status: 0 - off, 1 - on - * @:bt_open_conn: number of BT connections open - * @:bt_traffic_load: load of BT traffic - * @:bt_agg_traffic_load: aggregated load of BT traffic - * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @msg_idx: the index of the message + * @bt_status: 0 - off, 1 - on + * @bt_open_conn: number of BT connections open + * @bt_traffic_load: load of BT traffic + * @bt_agg_traffic_load: aggregated load of BT traffic + * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @primary_ch_lut: LUT used for primary channel + * @secondary_ch_lut: LUT used for secondary channel + * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading */ struct iwl_bt_coex_profile_notif { __le32 mbox_msg[4]; + __le32 msg_idx; u8 bt_status; u8 bt_open_conn; u8 bt_traffic_load; u8 bt_agg_traffic_load; u8 bt_ci_compliance; u8 reserved[3]; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */ + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */ enum iwl_bt_coex_prio_table_event { BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0, @@ -300,20 +355,4 @@ struct iwl_bt_coex_prio_tbl_cmd { u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; } __packed; -enum iwl_bt_coex_env_action { - BT_COEX_ENV_CLOSE = 0, - BT_COEX_ENV_OPEN = 1, -}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */ - -/** - * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope - * @action: enum %iwl_bt_coex_env_action - * @type: enum %iwl_bt_coex_prio_table_event - */ -struct iwl_bt_coex_prot_env_cmd { - u8 action; /* 0 = closed, 1 = open */ - u8 type; /* 0 .. 15 */ - u8 reserved[2]; -} __packed; - #endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index df72fcdf817..13696fe419b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -100,7 +100,12 @@ enum iwl_proto_offloads { #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 6 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 + +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 /** * struct iwl_proto_offload_cmd_common - ARP/NS offload common part @@ -155,6 +160,43 @@ struct iwl_proto_offload_cmd_v2 { u8 reserved2[3]; } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ +struct iwl_ns_config { + struct in6_addr source_ipv6_addr; + struct in6_addr dest_ipv6_addr; + u8 target_mac_addr[ETH_ALEN]; + __le16 reserved; +} __packed; /* NS_OFFLOAD_CONFIG */ + +struct iwl_targ_addr { + struct in6_addr addr; + __le32 config_num; +} __packed; /* TARGET_IPV6_ADDRESS */ + +/** + * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @target_ipv6_addr: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_small { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ + +/** + * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @target_ipv6_addr: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_large { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ /* * WOWLAN_PATTERNS @@ -189,11 +231,15 @@ enum iwl_wowlan_wakeup_filters { IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT = BIT(8), IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS = BIT(9), IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE = BIT(10), - /* BIT(11) reserved */ + IWL_WOWLAN_WAKEUP_REMOTE_TCP_EXTERNAL = BIT(11), IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET = BIT(12), + IWL_WOWLAN_WAKEUP_IOAC_MAGIC_PACKET = BIT(13), + IWL_WOWLAN_WAKEUP_HOST_TIMER = BIT(14), + IWL_WOWLAN_WAKEUP_RX_FRAME = BIT(15), + 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]; @@ -201,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 */ @@ -293,20 +345,28 @@ enum iwl_wowlan_wakeup_reason { IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ +struct iwl_wowlan_gtk_status { + u8 key_index; + u8 reserved[3]; + u8 decrypt_key[16]; + u8 tkip_mic_key[8]; + struct iwl_wowlan_rsc_tsc_params_cmd rsc; +} __packed; + struct iwl_wowlan_status { + struct iwl_wowlan_gtk_status gtk; __le64 replay_ctr; __le16 pattern_number; __le16 non_qos_seq_ctr; __le16 qos_seq_ctr[8]; __le32 wakeup_reasons; - __le32 rekey_status; __le32 num_of_gtk_rekeys; __le32 transmitted_ndps; __le32 received_beacons; __le32 wake_packet_length; __le32 wake_packet_bufsize; u8 wake_packet[]; /* can be truncated from _length to _bufsize */ -} __packed; /* WOWLAN_STATUSES_API_S_VER_4 */ +} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ #define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 #define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h index 98b1feb43d3..c405cda1025 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -170,12 +170,14 @@ struct iwl_mac_data_ap { * @beacon_tsf: beacon transmit time in TSF * @bi: beacon interval in TU * @bi_reciprocal: 2^32 / bi + * @beacon_template: beacon template ID */ struct iwl_mac_data_ibss { __le32 beacon_time; __le64 beacon_tsf; __le32 bi; __le32 bi_reciprocal; + __le32 beacon_template; } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ /** @@ -372,4 +374,13 @@ static inline u32 iwl_mvm_reciprocal(u32 v) return 0xFFFFFFFF / v; } +#define IWL_NONQOS_SEQ_GET 0x1 +#define IWL_NONQOS_SEQ_SET 0x2 +struct iwl_nonqos_seq_query_cmd { + __le32 get_set_flag; + __le32 mac_id_n_color; + __le16 value; + __le16 reserved; +} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ + #endif /* __fw_api_mac_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 8e7ab41079c..cbbcd8e284e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -85,6 +85,8 @@ * PBW Snoozing enabled * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. + * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving + * detection enablement */ enum iwl_power_flags { POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), @@ -94,6 +96,7 @@ enum iwl_power_flags { POWER_FLAGS_BT_SCO_ENA = BIT(8), POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), POWER_FLAGS_LPRX_ENA_MSK = BIT(11), + POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), }; #define IWL_POWER_VEC_SIZE 5 @@ -132,6 +135,33 @@ struct iwl_powertable_cmd { } __packed; /** + * enum iwl_device_power_flags - masks for device power command flags + * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off + * receiver and transmitter. '0' - does not allow. This flag should be + * always set to '1' unless one need to disable actual power down for debug + * purposes. + * @DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning + * that power management is disabled. '0' Power management is enabled, one + * of power schemes is applied. +*/ +enum iwl_device_power_flags { + DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), + DEVICE_POWER_FLAGS_CAM_MSK = BIT(13), +}; + +/** + * struct iwl_device_power_cmd - device wide power command. + * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) + * + * @flags: Power table command flags from DEVICE_POWER_FLAGS_* + */ +struct iwl_device_power_cmd { + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ + __le16 flags; + __le16 reserved; +} __packed; + +/** * struct iwl_mac_power_cmd - New power command containing uAPSD support * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) * @id_and_color: MAC contex identifier @@ -201,6 +231,19 @@ struct iwl_mac_power_cmd { u8 reserved; } __packed; +/* + * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when + * associated AP is identified as improperly implementing uAPSD protocol. + * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 + * @sta_id: index of station in uCode's station table - associated AP ID in + * this context. + */ +struct iwl_uapsd_misbehaving_ap_notif { + __le32 sta_id; + u8 mac_id; + u8 reserved[3]; +} __packed; + /** * struct iwl_beacon_filter_cmd * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) @@ -258,54 +301,65 @@ struct iwl_beacon_filter_cmd { /* Beacon filtering and beacon abort */ #define IWL_BF_ENERGY_DELTA_DEFAULT 5 +#define IWL_BF_ENERGY_DELTA_D0I3 20 #define IWL_BF_ENERGY_DELTA_MAX 255 #define IWL_BF_ENERGY_DELTA_MIN 0 #define IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 +#define IWL_BF_ROAMING_ENERGY_DELTA_D0I3 20 #define IWL_BF_ROAMING_ENERGY_DELTA_MAX 255 #define IWL_BF_ROAMING_ENERGY_DELTA_MIN 0 #define IWL_BF_ROAMING_STATE_DEFAULT 72 +#define IWL_BF_ROAMING_STATE_D0I3 72 #define IWL_BF_ROAMING_STATE_MAX 255 #define IWL_BF_ROAMING_STATE_MIN 0 #define IWL_BF_TEMP_THRESHOLD_DEFAULT 112 +#define IWL_BF_TEMP_THRESHOLD_D0I3 112 #define IWL_BF_TEMP_THRESHOLD_MAX 255 #define IWL_BF_TEMP_THRESHOLD_MIN 0 #define IWL_BF_TEMP_FAST_FILTER_DEFAULT 1 +#define IWL_BF_TEMP_FAST_FILTER_D0I3 1 #define IWL_BF_TEMP_FAST_FILTER_MAX 255 #define IWL_BF_TEMP_FAST_FILTER_MIN 0 #define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 5 +#define IWL_BF_TEMP_SLOW_FILTER_D0I3 5 #define IWL_BF_TEMP_SLOW_FILTER_MAX 255 #define IWL_BF_TEMP_SLOW_FILTER_MIN 0 #define IWL_BF_ENABLE_BEACON_FILTER_DEFAULT 1 #define IWL_BF_DEBUG_FLAG_DEFAULT 0 +#define IWL_BF_DEBUG_FLAG_D0I3 0 #define IWL_BF_ESCAPE_TIMER_DEFAULT 50 +#define IWL_BF_ESCAPE_TIMER_D0I3 1024 #define IWL_BF_ESCAPE_TIMER_MAX 1024 #define IWL_BF_ESCAPE_TIMER_MIN 0 #define IWL_BA_ESCAPE_TIMER_DEFAULT 6 -#define IWL_BA_ESCAPE_TIMER_D3 6 +#define IWL_BA_ESCAPE_TIMER_D0I3 6 +#define IWL_BA_ESCAPE_TIMER_D3 9 #define IWL_BA_ESCAPE_TIMER_MAX 1024 #define IWL_BA_ESCAPE_TIMER_MIN 0 #define IWL_BA_ENABLE_BEACON_ABORT_DEFAULT 1 -#define IWL_BF_CMD_CONFIG_DEFAULTS \ - .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA_DEFAULT), \ - .bf_roaming_energy_delta = \ - cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT), \ - .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE_DEFAULT), \ - .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD_DEFAULT), \ - .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER_DEFAULT), \ - .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER_DEFAULT), \ - .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG_DEFAULT), \ - .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \ - .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_DEFAULT) +#define IWL_BF_CMD_CONFIG(mode) \ + .bf_energy_delta = cpu_to_le32(IWL_BF_ENERGY_DELTA ## mode), \ + .bf_roaming_energy_delta = \ + cpu_to_le32(IWL_BF_ROAMING_ENERGY_DELTA ## mode), \ + .bf_roaming_state = cpu_to_le32(IWL_BF_ROAMING_STATE ## mode), \ + .bf_temp_threshold = cpu_to_le32(IWL_BF_TEMP_THRESHOLD ## mode), \ + .bf_temp_fast_filter = cpu_to_le32(IWL_BF_TEMP_FAST_FILTER ## mode), \ + .bf_temp_slow_filter = cpu_to_le32(IWL_BF_TEMP_SLOW_FILTER ## mode), \ + .bf_debug_flag = cpu_to_le32(IWL_BF_DEBUG_FLAG ## mode), \ + .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER ## mode), \ + .ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER ## mode) +#define IWL_BF_CMD_CONFIG_DEFAULTS IWL_BF_CMD_CONFIG(_DEFAULT) +#define IWL_BF_CMD_CONFIG_D0I3 IWL_BF_CMD_CONFIG(_D0I3) #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h index fdd33bc0a59..8bb5b94bf96 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +68,7 @@ /* * These serve as indexes into * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; + * TODO: avoid overlap between legacy and HT rates */ enum { IWL_RATE_1M_INDEX = 0, @@ -78,18 +79,31 @@ enum { IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, IWL_RATE_6M_INDEX, IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, + IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, + IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, IWL_RATE_9M_INDEX, IWL_RATE_12M_INDEX, + IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, + IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, IWL_RATE_24M_INDEX, + IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, + IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, IWL_RATE_48M_INDEX, + IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX, + IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, IWL_RATE_60M_INDEX, - IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, + IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, + IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, + IWL_RATE_MCS_8_INDEX, + IWL_RATE_MCS_9_INDEX, + IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, - IWL_RATE_COUNT, + IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, }; #define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) @@ -108,6 +122,7 @@ enum { IWL_RATE_2M_PLCP = 20, IWL_RATE_5M_PLCP = 55, IWL_RATE_11M_PLCP = 110, + IWL_RATE_INVM_PLCP = -1, }; /* @@ -164,6 +179,8 @@ enum { * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) */ #define RATE_HT_MCS_RATE_CODE_MSK 0x7 +#define RATE_HT_MCS_NSS_POS 3 +#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) /* Bit 10: (1) Use Green Field preamble */ #define RATE_HT_MCS_GF_POS 10 @@ -240,7 +257,8 @@ enum { /* Bit 17-18: (0) SS, (1) SS*2 */ #define RATE_MCS_STBC_POS 17 -#define RATE_MCS_STBC_MSK (1 << RATE_MCS_STBC_POS) +#define RATE_MCS_HT_STBC_MSK (3 << RATE_MCS_STBC_POS) +#define RATE_MCS_VHT_STBC_MSK (1 << RATE_MCS_STBC_POS) /* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ #define RATE_MCS_BF_POS 19 @@ -264,8 +282,31 @@ enum { /* # entries in rate scale table to support Tx retries */ #define LQ_MAX_RETRY_NUM 16 -/* Link quality command flags, only this one is available */ -#define LQ_FLAG_SET_STA_TLC_RTS_MSK BIT(0) +/* Link quality command flags bit fields */ + +/* Bit 0: (0) Don't use RTS (1) Use RTS */ +#define LQ_FLAG_USE_RTS_POS 0 +#define LQ_FLAG_USE_RTS_MSK (1 << LQ_FLAG_USE_RTS_POS) + +/* Bit 1-3: LQ command color. Used to match responses to LQ commands */ +#define LQ_FLAG_COLOR_POS 1 +#define LQ_FLAG_COLOR_MSK (7 << LQ_FLAG_COLOR_POS) + +/* Bit 4-5: Tx RTS BW Signalling + * (0) No RTS BW signalling + * (1) Static BW signalling + * (2) Dynamic BW signalling + */ +#define LQ_FLAG_RTS_BW_SIG_POS 4 +#define LQ_FLAG_RTS_BW_SIG_NONE (0 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_STATIC (1 << LQ_FLAG_RTS_BW_SIG_POS) +#define LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << LQ_FLAG_RTS_BW_SIG_POS) + +/* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection + * Dyanmic BW selection allows Tx with narrower BW then requested in rates + */ +#define LQ_FLAG_DYNAMIC_BW_POS 6 +#define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) /** * struct iwl_lq_cmd - link quality command @@ -293,7 +334,7 @@ enum { */ struct iwl_lq_cmd { u8 sta_id; - u8 reserved1; + u8 reduced_tpc; u16 control; /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ u8 flags; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 83cb9b992ea..6959fda3fe0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -169,8 +169,12 @@ enum iwl_scan_type { SCAN_TYPE_DISCOVERY_FORCED = 6, }; /* SCAN_ACTIVITY_TYPE_E_VER_1 */ -/* Maximal number of channels to scan */ -#define MAX_NUM_SCAN_CHANNELS 0x24 +/** + * Maximal number of channels to scan + * it should be equal to: + * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000) + */ +#define MAX_NUM_SCAN_CHANNELS 50 /** * struct iwl_scan_cmd - scan request command @@ -183,9 +187,9 @@ enum iwl_scan_type { * this number of packets were received (typically 1) * @passive2active: is auto switching from passive to active during scan allowed * @rxchain_sel_flags: RXON_RX_CHAIN_* - * @max_out_time: in usecs, max out of serving channel time + * @max_out_time: in TUs, max out of serving channel time * @suspend_time: how long to pause scan when returning to service channel: - * bits 0-19: beacon interal in usecs (suspend before executing) + * bits 0-19: beacon interal in TUs (suspend before executing) * bits 20-23: reserved * bits 24-31: number of beacons (suspend between channels) * @rxon_flags: RXON_FLG_* @@ -356,6 +360,7 @@ struct iwl_scan_complete_notif { /* scan offload */ #define IWL_MAX_SCAN_CHANNELS 40 #define IWL_SCAN_MAX_BLACKLIST_LEN 64 +#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 #define IWL_SCAN_MAX_PROFILES 11 #define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 @@ -368,6 +373,12 @@ struct iwl_scan_complete_notif { #define IWL_FULL_SCAN_MULTIPLIER 5 #define IWL_FAST_SCHED_SCAN_ITERATIONS 3 +enum scan_framework_client { + SCAN_CLIENT_SCHED_SCAN = BIT(0), + SCAN_CLIENT_NETDETECT = BIT(1), + SCAN_CLIENT_ASSET_TRACKING = BIT(2), +}; + /** * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6 * @scan_flags: see enum iwl_scan_flags @@ -376,8 +387,8 @@ struct iwl_scan_complete_notif { * @quiet_plcp_th: quiet channel num of packets threshold * @good_CRC_th: passive to active promotion threshold * @rx_chain: RXON rx chain. - * @max_out_time: max uSec to be out of assoceated channel - * @suspend_time: pause scan this long when returning to service channel + * @max_out_time: max TUs to be out of assoceated channel + * @suspend_time: pause scan this TUs when returning to service channel * @flags: RXON flags * @filter_flags: RXONfilter * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz. @@ -449,11 +460,12 @@ struct iwl_scan_offload_cfg { * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S * @ssid: MAC address to filter out * @reported_rssi: AP rssi reported to the host + * @client_bitmap: clients ignore this entry - enum scan_framework_client */ struct iwl_scan_offload_blacklist { u8 ssid[ETH_ALEN]; u8 reported_rssi; - u8 reserved; + u8 client_bitmap; } __packed; enum iwl_scan_offload_network_type { @@ -475,6 +487,7 @@ enum iwl_scan_offload_band_selection { * @aut_alg: authentication olgorithm to match - bitmap * @network_type: enum iwl_scan_offload_network_type * @band_selection: enum iwl_scan_offload_band_selection + * @client_bitmap: clients waiting for match - enum scan_framework_client */ struct iwl_scan_offload_profile { u8 ssid_index; @@ -482,7 +495,8 @@ struct iwl_scan_offload_profile { u8 auth_alg; u8 network_type; u8 band_selection; - u8 reserved[3]; + u8 client_bitmap; + u8 reserved[2]; } __packed; /** @@ -491,12 +505,19 @@ struct iwl_scan_offload_profile { * @profiles: profiles to search for match * @blacklist_len: length of blacklist * @num_profiles: num of profiles in the list + * @match_notify: clients waiting for match found notification + * @pass_match: clients waiting for the results + * @active_clients: active clients bitmap - enum scan_framework_client + * @any_beacon_notify: clients waiting for match notification without match */ struct iwl_scan_offload_profile_cfg { - struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN]; struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; u8 blacklist_len; u8 num_profiles; + u8 match_notify; + u8 pass_match; + u8 active_clients; + u8 any_beacon_notify; u8 reserved[2]; } __packed; @@ -515,16 +536,18 @@ struct iwl_scan_offload_schedule { /* * iwl_scan_offload_flags * - * IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID: filter mode - upload every beacon or match - * ssid list. + * IWL_SCAN_OFFLOAD_FLAG_PASS_ALL: pass all results - no filtering. * IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan. - * IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan - * on A band. + * IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE: EBS duration is 100mSec - typical + * beacon period. Finding channel activity in this mode is not guaranteed. + * IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE: EBS duration is 200mSec. + * Assuming beacon period is 100ms finding channel activity is guaranteed. */ enum iwl_scan_offload_flags { - IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID = BIT(0), + IWL_SCAN_OFFLOAD_FLAG_PASS_ALL = BIT(0), IWL_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL = BIT(2), - IWL_SCAN_OFFLOAD_FLAG_ENERGY_SCAN = BIT(3), + IWL_SCAN_OFFLOAD_FLAG_EBS_QUICK_MODE = BIT(5), + IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE = BIT(6), }; /** @@ -547,17 +570,35 @@ enum iwl_scan_offload_compleate_status { IWL_SCAN_OFFLOAD_ABORTED = 2, }; +enum iwl_scan_ebs_status { + IWL_SCAN_EBS_SUCCESS, + IWL_SCAN_EBS_FAILED, + IWL_SCAN_EBS_CHAN_NOT_FOUND, +}; + /** * iwl_scan_offload_complete - SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1 * @last_schedule_line: last schedule line executed (fast or regular) * @last_schedule_iteration: last scan iteration executed before scan abort * @status: enum iwl_scan_offload_compleate_status + * @ebs_status: last EBS status, see IWL_SCAN_EBS_* */ struct iwl_scan_offload_complete { u8 last_schedule_line; u8 last_schedule_iteration; u8 status; - u8 reserved; + u8 ebs_status; } __packed; +/** + * iwl_sched_scan_results - SCAN_OFFLOAD_MATCH_FOUND_NTF_API_S_VER_1 + * @ssid_bitmap: SSIDs indexes found in this iteration + * @client_bitmap: clients that are active and wait for this notification + */ +struct iwl_sched_scan_results { + __le16 ssid_bitmap; + u8 client_bitmap; + u8 reserved; +}; + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index a30691a8a85..39cebee8016 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -97,9 +97,6 @@ enum iwl_sta_flags { STA_FLG_FLG_ANT_B), STA_FLG_PS = BIT(8), - STA_FLG_INVALID = BIT(9), - STA_FLG_DLP_EN = BIT(10), - STA_FLG_SET_ALL_KEYS = BIT(11), STA_FLG_DRAIN_FLOW = BIT(12), STA_FLG_PAN = BIT(13), STA_FLG_CLASS_AUTH = BIT(14), @@ -138,7 +135,14 @@ enum iwl_sta_flags { /** * enum iwl_sta_key_flag - key flags for the ADD_STA host command - * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm + * @STA_KEY_FLG_NO_ENC: no encryption + * @STA_KEY_FLG_WEP: WEP encryption algorithm + * @STA_KEY_FLG_CCM: CCMP encryption algorithm + * @STA_KEY_FLG_TKIP: TKIP encryption algorithm + * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) + * @STA_KEY_FLG_CMAC: CMAC encryption algorithm + * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm + * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from * station info array (1 - n 1X mode) * @STA_KEY_FLG_KEYID_MSK: the index of the key @@ -152,6 +156,7 @@ enum iwl_sta_key_flag { STA_KEY_FLG_WEP = (1 << 0), STA_KEY_FLG_CCM = (2 << 0), STA_KEY_FLG_TKIP = (3 << 0), + STA_KEY_FLG_EXT = (4 << 0), STA_KEY_FLG_CMAC = (6 << 0), STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), STA_KEY_FLG_EN_MSK = (7 << 0), @@ -194,11 +199,14 @@ enum iwl_sta_modify_flag { * @STA_SLEEP_STATE_AWAKE: * @STA_SLEEP_STATE_PS_POLL: * @STA_SLEEP_STATE_UAPSD: + * @STA_SLEEP_STATE_MOREDATA: set more-data bit on + * (last) released frame */ enum iwl_sta_sleep_flag { - STA_SLEEP_STATE_AWAKE = 0, - STA_SLEEP_STATE_PS_POLL = BIT(0), - STA_SLEEP_STATE_UAPSD = BIT(1), + STA_SLEEP_STATE_AWAKE = 0, + STA_SLEEP_STATE_PS_POLL = BIT(0), + STA_SLEEP_STATE_UAPSD = BIT(1), + STA_SLEEP_STATE_MOREDATA = BIT(2), }; /* STA ID and color bits definitions */ @@ -247,22 +255,19 @@ struct iwl_mvm_keyinfo { } __packed; /** - * struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table + * struct iwl_mvm_add_sta_cmd - Add/modify a station in the fw's sta table. * ( REPLY_ADD_STA = 0x18 ) * @add_modify: 1: modify existing, 0: add new station - * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent - * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key - * sent + * @awake_acs: + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. * @mac_id_n_color: the Mac context this station belongs to * @addr[ETH_ALEN]: station's MAC address * @sta_id: index of station in uCode's station table * @modify_mask: STA_MODIFY_*, selects which parameters to modify vs. leave * alone. 1 - modify, 0 - don't change. - * @key: look at %iwl_mvm_keyinfo * @station_flags: look at %iwl_sta_flags * @station_flags_msk: what of %station_flags have changed - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %STA_MODIFY_TID_DISABLE_TX to change this field. * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) * Set %STA_MODIFY_ADD_BA_TID to use this field, and also set * add_immediate_ba_ssn. @@ -288,20 +293,16 @@ struct iwl_mvm_keyinfo { */ struct iwl_mvm_add_sta_cmd { u8 add_modify; - u8 unicast_tx_key_id; - u8 multicast_tx_key_id; - u8 reserved1; + u8 awake_acs; + __le16 tid_disable_tx; __le32 mac_id_n_color; - u8 addr[ETH_ALEN]; + u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ __le16 reserved2; u8 sta_id; u8 modify_mask; __le16 reserved3; - struct iwl_mvm_keyinfo key; __le32 station_flags; __le32 station_flags_msk; - __le16 tid_disable_tx; - __le16 reserved4; u8 add_immediate_ba_tid; u8 remove_immediate_ba_tid; __le16 add_immediate_ba_ssn; @@ -310,7 +311,31 @@ struct iwl_mvm_add_sta_cmd { __le16 assoc_id; __le16 beamform_flags; __le32 tfd_queue_msk; -} __packed; /* ADD_STA_CMD_API_S_VER_5 */ +} __packed; /* ADD_STA_CMD_API_S_VER_7 */ + +/** + * struct iwl_mvm_add_sta_key_cmd - add/modify sta key + * ( REPLY_ADD_STA_KEY = 0x17 ) + * @sta_id: index of station in uCode's station table + * @key_offset: key offset in key storage + * @key_flags: type %iwl_sta_key_flag + * @key: key material data + * @key2: key material data + * @rx_secur_seq_cnt: RX security sequence counter for the key + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + */ +struct iwl_mvm_add_sta_key_cmd { + u8 sta_id; + u8 key_offset; + __le16 key_flags; + u8 key[16]; + u8 key2[16]; + u8 rx_secur_seq_cnt[16]; + u8 tkip_rx_tsc_byte2; + u8 reserved; + __le16 tkip_rx_ttak[5]; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ /** * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command @@ -376,5 +401,15 @@ struct iwl_mvm_wep_key_cmd { struct iwl_mvm_wep_key wep_key[0]; } __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ +/** + * struct iwl_mvm_eosp_notification - EOSP notification from firmware + * @remain_frame_count: # of frames remaining, non-zero if SP was cut + * short by GO absence + * @sta_id: station ID + */ +struct iwl_mvm_eosp_notification { + __le32 remain_frame_count; + __le32 sta_id; +} __packed; /* UAPSD_EOSP_NTFY_API_S_VER_1 */ #endif /* __fw_api_sta_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index d606197bde8..6cc5f52b807 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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), @@ -132,6 +135,7 @@ enum iwl_tx_flags { #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_EXT 0x04 #define TX_CMD_SEC_MSK 0x07 #define TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 @@ -478,7 +482,8 @@ struct iwl_mvm_tx_resp { u8 pa_integ_res_b[3]; u8 pa_integ_res_c[3]; __le16 measurement_req_id; - __le16 reserved; + u8 reduced_tpc; + u8 reserved; __le32 tfd_info; __le16 seq_ctl; diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 66264cc5a01..309a9b9a94f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -70,19 +70,20 @@ #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" +#include "fw-api-scan.h" -/* queue and FIFO numbers by usage */ +/* maximal number of Tx queues in any platform */ +#define IWL_MVM_MAX_QUEUES 20 + +/* Tx queue numbers */ enum { IWL_MVM_OFFCHANNEL_QUEUE = 8, IWL_MVM_CMD_QUEUE = 9, - IWL_MVM_AUX_QUEUE = 15, - IWL_MVM_FIRST_AGG_QUEUE = 16, - IWL_MVM_NUM_QUEUES = 20, - IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1, - IWL_MVM_CMD_FIFO = 7 }; +#define IWL_MVM_CMD_FIFO 7 + #define IWL_MVM_STATION_COUNT 16 /* commands */ @@ -95,8 +96,10 @@ enum { /* PHY context commands */ PHY_CONTEXT_CMD = 0x8, DBG_CFG = 0x9, + ANTENNA_COUPLING_NOTIFICATION = 0xa, /* station table */ + ADD_STA_KEY = 0x17, ADD_STA = 0x18, REMOVE_STA = 0x19, @@ -114,6 +117,7 @@ enum { TIME_EVENT_NOTIFICATION = 0x2a, BINDING_CONTEXT_CMD = 0x2b, TIME_QUOTA_CMD = 0x2c, + NON_QOS_TX_COUNTER_CMD = 0x2d, LQ_CMD = 0x4e, @@ -130,6 +134,7 @@ enum { SCAN_OFFLOAD_COMPLETE = 0x6D, SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, SCAN_OFFLOAD_CONFIG_CMD = 0x6f, + MATCH_FOUND_NOTIFICATION = 0xd9, /* Phy */ PHY_CONFIGURATION_CMD = 0x6a, @@ -138,6 +143,7 @@ enum { /* Power - legacy power table command */ POWER_TABLE_CMD = 0x77, + PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, /* Thermal Throttling*/ REPLY_THERMAL_MNG_BACKOFF = 0x7e, @@ -159,6 +165,7 @@ enum { TX_ANT_CONFIGURATION_CMD = 0x98, BT_CONFIG = 0x9b, STATISTICS_NOTIFICATION = 0x9d, + EOSP_NOTIFICATION = 0x9e, REDUCE_TX_POWER_CMD = 0x9f, /* RF-KILL commands and notifications */ @@ -178,12 +185,15 @@ enum { BT_COEX_PRIO_TABLE = 0xcc, BT_COEX_PROT_ENV = 0xcd, BT_PROFILE_NOTIFICATION = 0xce, + BT_COEX_CI = 0x5d, + REPLY_SF_CFG_CMD = 0xd1, REPLY_BEACON_FILTERING_CMD = 0xd2, REPLY_DEBUG_CMD = 0xf0, DEBUG_LOG_MSG = 0xf7, + BCAST_FILTER_CMD = 0xcf, MCAST_FILTER_CMD = 0xd0, /* D3 commands/notifications */ @@ -191,6 +201,7 @@ enum { PROT_OFFLOAD_CONFIG_CMD = 0xd4, OFFLOADS_QUERY_CMD = 0xd5, REMOTE_WAKE_CONFIG_CMD = 0xd6, + D0I3_END_CMD = 0xed, /* for WoWLAN in particular */ WOWLAN_PATTERNS = 0xe0, @@ -307,14 +318,12 @@ enum { /* Section types for NVM_ACCESS_CMD */ enum { - NVM_SECTION_TYPE_HW = 0, - NVM_SECTION_TYPE_SW, - NVM_SECTION_TYPE_PAPD, - NVM_SECTION_TYPE_BT, - NVM_SECTION_TYPE_CALIBRATION, - NVM_SECTION_TYPE_PRODUCTION, - NVM_SECTION_TYPE_POST_FCS_CALIB, - NVM_NUM_OF_SECTIONS, + NVM_SECTION_TYPE_SW = 1, + NVM_SECTION_TYPE_REGULATORY = 3, + NVM_SECTION_TYPE_CALIBRATION = 4, + NVM_SECTION_TYPE_PRODUCTION = 5, + NVM_SECTION_TYPE_MAC_OVERRIDE = 11, + NVM_MAX_NUM_SECTIONS = 12, }; /** @@ -406,6 +415,35 @@ struct mvm_alive_resp { __le32 scd_base_ptr; /* SRAM address for SCD */ } __packed; /* ALIVE_RES_API_S_VER_1 */ +struct mvm_alive_resp_ver2 { + __le16 status; + __le16 flags; + u8 ucode_minor; + u8 ucode_major; + __le16 id; + u8 api_minor; + u8 api_major; + u8 ver_subtype; + u8 ver_type; + u8 mac; + u8 opt; + __le16 reserved2; + __le32 timestamp; + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 log_event_table_ptr; /* SRAM address for LMAC event log */ + __le32 cpu_register_ptr; + __le32 dbgm_config_ptr; + __le32 alive_counter_ptr; + __le32 scd_base_ptr; /* SRAM address for SCD */ + __le32 st_fwrd_addr; /* pointer to Store and forward */ + __le32 st_fwrd_size; + u8 umac_minor; /* UMAC version: minor */ + u8 umac_major; /* UMAC version: major */ + __le16 umac_id; /* UMAC version: id */ + __le32 error_info_addr; /* SRAM address for UMAC error log */ + __le32 dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_2 */ + /* Error response/notification */ enum { FW_ERR_UNKNOWN_CMD = 0x0, @@ -567,52 +605,7 @@ enum { TE_V1_NOTIF_INTERNAL_FRAG_END = BIT(7), }; /* MAC_EVENT_ACTION_API_E_VER_2 */ - -/** - * struct iwl_time_event_cmd_api_v1 - configuring Time Events - * with struct MAC_TIME_EVENT_DATA_API_S_VER_1 (see also - * with version 2. determined by IWL_UCODE_TLV_FLAGS) - * ( TIME_EVENT_CMD = 0x29 ) - * @id_and_color: ID and color of the relevant MAC - * @action: action to perform, one of FW_CTXT_ACTION_* - * @id: this field has two meanings, depending on the action: - * If the action is ADD, then it means the type of event to add. - * For all other actions it is the unique event ID assigned when the - * event was added by the FW. - * @apply_time: When to start the Time Event (in GP2) - * @max_delay: maximum delay to event's start (apply time), in TU - * @depends_on: the unique ID of the event we depend on (if any) - * @interval: interval between repetitions, in TU - * @interval_reciprocal: 2^32 / interval - * @duration: duration of event in TU - * @repeat: how many repetitions to do, can be TE_REPEAT_ENDLESS - * @dep_policy: one of TE_V1_INDEPENDENT, TE_V1_DEP_OTHER, TE_V1_DEP_TSF - * and TE_V1_EVENT_SOCIOPATHIC - * @is_present: 0 or 1, are we present or absent during the Time Event - * @max_frags: maximal number of fragments the Time Event can be divided to - * @notify: notifications using TE_V1_NOTIF_* (whom to notify when) - */ -struct iwl_time_event_cmd_v1 { - /* COMMON_INDEX_HDR_API_S_VER_1 */ - __le32 id_and_color; - __le32 action; - __le32 id; - /* MAC_TIME_EVENT_DATA_API_S_VER_1 */ - __le32 apply_time; - __le32 max_delay; - __le32 dep_policy; - __le32 depends_on; - __le32 is_present; - __le32 max_frags; - __le32 interval; - __le32 interval_reciprocal; - __le32 duration; - __le32 repeat; - __le32 notify; -} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_1 */ - - -/* Time event - defines for command API v2 */ +/* Time event - defines for command API */ /* * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. @@ -643,7 +636,7 @@ enum { #define TE_V2_PLACEMENT_POS 12 #define TE_V2_ABSENCE_POS 15 -/* Time event policy values (for time event cmd api v2) +/* Time event policy values * A notification (both event and fragment) includes a status indicating weather * the FW was able to schedule the event or not. For fragment start/end * notification the status is always success. There is no start/end fragment @@ -676,6 +669,7 @@ enum { TE_V2_NOTIF_HOST_FRAG_END = BIT(5), TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6), TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7), + T2_V2_START_IMMEDIATELY = BIT(11), TE_V2_NOTIF_MSK = 0xff, @@ -689,7 +683,7 @@ enum { }; /** - * struct iwl_time_event_cmd_api_v2 - configuring Time Events + * struct iwl_time_event_cmd_api - configuring Time Events * with struct MAC_TIME_EVENT_DATA_API_S_VER_2 (see also * with version 1. determined by IWL_UCODE_TLV_FLAGS) * ( TIME_EVENT_CMD = 0x29 ) @@ -712,7 +706,7 @@ enum { * TE_EVENT_SOCIOPATHIC * using TE_ABSENCE and using TE_NOTIF_* */ -struct iwl_time_event_cmd_v2 { +struct iwl_time_event_cmd { /* COMMON_INDEX_HDR_API_S_VER_1 */ __le32 id_and_color; __le32 action; @@ -1048,6 +1042,7 @@ enum iwl_mvm_rx_status { RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), + RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), @@ -1127,6 +1122,7 @@ struct iwl_set_calib_default_cmd { } __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */ #define MAX_PORT_ID_NUM 2 +#define MAX_MCAST_FILTERING_ADDRESSES 256 /** * struct iwl_mcast_filter_cmd - configure multicast filter. @@ -1151,6 +1147,90 @@ struct iwl_mcast_filter_cmd { u8 addr_list[0]; } __packed; /* MCAST_FILTERING_CMD_API_S_VER_1 */ +#define MAX_BCAST_FILTERS 8 +#define MAX_BCAST_FILTER_ATTRS 2 + +/** + * enum iwl_mvm_bcast_filter_attr_offset - written by fw for each Rx packet + * @BCAST_FILTER_OFFSET_PAYLOAD_START: offset is from payload start. + * @BCAST_FILTER_OFFSET_IP_END: offset is from ip header end (i.e. + * start of ip payload). + */ +enum iwl_mvm_bcast_filter_attr_offset { + BCAST_FILTER_OFFSET_PAYLOAD_START = 0, + BCAST_FILTER_OFFSET_IP_END = 1, +}; + +/** + * struct iwl_fw_bcast_filter_attr - broadcast filter attribute + * @offset_type: &enum iwl_mvm_bcast_filter_attr_offset. + * @offset: starting offset of this pattern. + * @val: value to match - big endian (MSB is the first + * byte to match from offset pos). + * @mask: mask to match (big endian). + */ +struct iwl_fw_bcast_filter_attr { + u8 offset_type; + u8 offset; + __le16 reserved1; + __be32 val; + __be32 mask; +} __packed; /* BCAST_FILTER_ATT_S_VER_1 */ + +/** + * enum iwl_mvm_bcast_filter_frame_type - filter frame type + * @BCAST_FILTER_FRAME_TYPE_ALL: consider all frames. + * @BCAST_FILTER_FRAME_TYPE_IPV4: consider only ipv4 frames + */ +enum iwl_mvm_bcast_filter_frame_type { + BCAST_FILTER_FRAME_TYPE_ALL = 0, + BCAST_FILTER_FRAME_TYPE_IPV4 = 1, +}; + +/** + * struct iwl_fw_bcast_filter - broadcast filter + * @discard: discard frame (1) or let it pass (0). + * @frame_type: &enum iwl_mvm_bcast_filter_frame_type. + * @num_attrs: number of valid attributes in this filter. + * @attrs: attributes of this filter. a filter is considered matched + * only when all its attributes are matched (i.e. AND relationship) + */ +struct iwl_fw_bcast_filter { + u8 discard; + u8 frame_type; + u8 num_attrs; + u8 reserved1; + struct iwl_fw_bcast_filter_attr attrs[MAX_BCAST_FILTER_ATTRS]; +} __packed; /* BCAST_FILTER_S_VER_1 */ + +/** + * struct iwl_fw_bcast_mac - per-mac broadcast filtering configuration. + * @default_discard: default action for this mac (discard (1) / pass (0)). + * @attached_filters: bitmap of relevant filters for this mac. + */ +struct iwl_fw_bcast_mac { + u8 default_discard; + u8 reserved1; + __le16 attached_filters; +} __packed; /* BCAST_MAC_CONTEXT_S_VER_1 */ + +/** + * struct iwl_bcast_filter_cmd - broadcast filtering configuration + * @disable: enable (0) / disable (1) + * @max_bcast_filters: max number of filters (MAX_BCAST_FILTERS) + * @max_macs: max number of macs (NUM_MAC_INDEX_DRIVER) + * @filters: broadcast filters + * @macs: broadcast filtering configuration per-mac + */ +struct iwl_bcast_filter_cmd { + u8 disable; + u8 max_bcast_filters; + u8 max_macs; + u8 reserved1; + struct iwl_fw_bcast_filter filters[MAX_BCAST_FILTERS]; + struct iwl_fw_bcast_mac macs[NUM_MAC_INDEX_DRIVER]; +} __packed; /* BCAST_FILTERING_HCMD_API_S_VER_1 */ + struct mvm_statistics_dbg { __le32 burst_check; __le32 burst_count; @@ -1359,4 +1439,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */ struct mvm_statistics_general general; } __packed; +/*********************************** + * Smart Fifo API + ***********************************/ +/* Smart Fifo state */ +enum iwl_sf_state { + SF_LONG_DELAY_ON = 0, /* should never be called by driver */ + SF_FULL_ON, + SF_UNINIT, + SF_INIT_OFF, + SF_HW_NUM_STATES +}; + +/* Smart Fifo possible scenario */ +enum iwl_sf_scenario { + SF_SCENARIO_SINGLE_UNICAST, + SF_SCENARIO_AGG_UNICAST, + SF_SCENARIO_MULTICAST, + SF_SCENARIO_BA_RESP, + SF_SCENARIO_TX_RESP, + SF_NUM_SCENARIO +}; + +#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ +#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ + +/* smart FIFO default values */ +#define SF_W_MARK_SISO 4096 +#define SF_W_MARK_MIMO2 8192 +#define SF_W_MARK_MIMO3 6144 +#define SF_W_MARK_LEGACY 4096 +#define SF_W_MARK_SCAN 4096 + +/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ +#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ +#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ +#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ +#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ + +#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ + +/** + * Smart Fifo configuration command. + * @state: smart fifo state, types listed in iwl_sf_sate. + * @watermark: Minimum allowed availabe free space in RXF for transient state. + * @long_delay_timeouts: aging and idle timer values for each scenario + * in long delay state. + * @full_on_timeouts: timer values for each scenario in full on state. + */ +struct iwl_sf_cfg_cmd { + enum iwl_sf_state state; + __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; + __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; + __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; +} __packed; /* SF_CFG_API_S_VER_2 */ + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index c76299a3a1e..883e702152d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -99,7 +99,7 @@ static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) }; IWL_DEBUG_FW(mvm, "select valid tx ant: %u\n", valid_tx_ant); - return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, TX_ANT_CONFIGURATION_CMD, 0, sizeof(tx_ant_cmd), &tx_ant_cmd); } @@ -110,18 +110,50 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, container_of(notif_wait, struct iwl_mvm, notif_wait); struct iwl_mvm_alive_data *alive_data = data; struct mvm_alive_resp *palive; - - palive = (void *)pkt->data; - - mvm->error_event_table = le32_to_cpu(palive->error_event_table_ptr); - mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr); - alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); - - alive_data->valid = le16_to_cpu(palive->status) == IWL_ALIVE_STATUS_OK; - IWL_DEBUG_FW(mvm, - "Alive ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16_to_cpu(palive->status), palive->ver_type, - palive->ver_subtype, palive->flags); + struct mvm_alive_resp_ver2 *palive2; + + if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { + palive = (void *)pkt->data; + + mvm->support_umac_log = false; + mvm->error_event_table = + le32_to_cpu(palive->error_event_table_ptr); + mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr); + alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); + + alive_data->valid = le16_to_cpu(palive->status) == + IWL_ALIVE_STATUS_OK; + IWL_DEBUG_FW(mvm, + "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16_to_cpu(palive->status), palive->ver_type, + palive->ver_subtype, palive->flags); + } else { + palive2 = (void *)pkt->data; + + mvm->error_event_table = + le32_to_cpu(palive2->error_event_table_ptr); + mvm->log_event_table = + le32_to_cpu(palive2->log_event_table_ptr); + alive_data->scd_base_addr = le32_to_cpu(palive2->scd_base_ptr); + mvm->umac_error_event_table = + le32_to_cpu(palive2->error_info_addr); + mvm->sf_space.addr = le32_to_cpu(palive2->st_fwrd_addr); + mvm->sf_space.size = le32_to_cpu(palive2->st_fwrd_size); + + alive_data->valid = le16_to_cpu(palive2->status) == + IWL_ALIVE_STATUS_OK; + if (mvm->umac_error_event_table) + mvm->support_umac_log = true; + + IWL_DEBUG_FW(mvm, + "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", + le16_to_cpu(palive2->status), palive2->ver_type, + palive2->ver_subtype, palive2->flags); + + IWL_DEBUG_FW(mvm, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + palive2->umac_major, palive2->umac_minor); + } return true; } @@ -150,14 +182,13 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, int ret, i; enum iwl_ucode_type old_type = mvm->cur_ucode; static const u8 alive_cmd[] = { MVM_ALIVE }; + struct iwl_sf_region st_fwrd_space; - mvm->cur_ucode = ucode_type; fw = iwl_get_ucode_image(mvm, ucode_type); - - mvm->ucode_loaded = false; - - if (!fw) + if (WARN_ON(!fw)) return -EINVAL; + mvm->cur_ucode = ucode_type; + mvm->ucode_loaded = false; iwl_init_notification_wait(&mvm->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), @@ -187,6 +218,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, return -EIO; } + /* + * update the sdio allocation according to the pointer we get in the + * alive notification. + */ + st_fwrd_space.addr = mvm->sf_space.addr; + st_fwrd_space.size = mvm->sf_space.size; + ret = iwl_trans_update_sf(mvm->trans, &st_fwrd_space); + iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); /* @@ -199,7 +238,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, */ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { - if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE) + if (i < mvm->first_agg_queue && i != IWL_MVM_CMD_QUEUE) mvm->queue_to_mac80211[i] = i; else mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; @@ -228,7 +267,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); - return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, sizeof(phy_cfg_cmd), &phy_cfg_cmd); } @@ -243,7 +282,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) lockdep_assert_held(&mvm->mutex); - if (mvm->init_ucode_run) + if (WARN_ON_ONCE(mvm->init_ucode_complete)) return 0; iwl_init_notification_wait(&mvm->notif_wait, @@ -260,19 +299,24 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) goto error; } - ret = iwl_send_bt_prio_tbl(mvm); + ret = iwl_send_bt_init_conf(mvm); if (ret) goto error; + /* Read the NVM only at driver load time, no need to do this twice */ if (read_nvm) { /* Read nvm */ - ret = iwl_nvm_init(mvm); + ret = iwl_nvm_init(mvm, true); if (ret) { IWL_ERR(mvm, "Failed to read NVM: %d\n", ret); goto error; } } + /* In case we read the NVM from external file, load it to the NIC */ + if (mvm->nvm_file_name) + iwl_mvm_load_nvm_to_nic(mvm); + ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); WARN_ON(ret); @@ -284,11 +328,12 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) IWL_DEBUG_RF_KILL(mvm, "jump over all phy activities due to RF kill\n"); iwl_remove_notification(&mvm->notif_wait, &calib_wait); - return 1; + ret = 1; + goto out; } /* Send TX valid antennas before triggering calibrations */ - ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); + ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); if (ret) goto error; @@ -310,15 +355,13 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, MVM_UCODE_CALIB_TIMEOUT); if (!ret) - mvm->init_ucode_run = true; + mvm->init_ucode_complete = true; goto out; error: iwl_remove_notification(&mvm->notif_wait, &calib_wait); out: - if (!iwlmvm_mod_params.init_dbg) { - iwl_trans_stop_device(mvm->trans); - } else if (!mvm->nvm_data) { + if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { /* we want to debug INIT and we have no NVM - fake */ mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + sizeof(struct ieee80211_channel) + @@ -326,8 +369,6 @@ out: GFP_KERNEL); if (!mvm->nvm_data) return -ENOMEM; - mvm->nvm_data->valid_rx_ant = 1; - mvm->nvm_data->valid_tx_ant = 1; mvm->nvm_data->bands[0].channels = mvm->nvm_data->channels; mvm->nvm_data->bands[0].n_channels = 1; mvm->nvm_data->bands[0].n_bitrates = 1; @@ -339,8 +380,6 @@ out: return ret; } -#define UCODE_CALIB_TIMEOUT (2*HZ) - int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; @@ -353,8 +392,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) return ret; - /* If we were in RFKILL during module loading, load init ucode now */ - if (!mvm->init_ucode_run) { + /* + * If we haven't completed the run of the init ucode during + * module loading, load init ucode now + * (for example, if we were in RFKILL) + */ + if (!mvm->init_ucode_complete) { ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); @@ -363,11 +406,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ret = -ERFKILL; goto error; } - /* should stop & start HW since that INIT image just loaded */ - iwl_trans_stop_hw(mvm->trans, false); - ret = iwl_trans_start_hw(mvm->trans); - if (ret) - return ret; + if (!iwlmvm_mod_params.init_dbg) { + /* + * should stop and start HW since that INIT + * image just loaded + */ + iwl_trans_stop_device(mvm->trans); + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; + } } if (iwlmvm_mod_params.init_dbg) @@ -379,11 +427,11 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } - ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); + ret = iwl_mvm_sf_update(mvm, NULL, false); if (ret) - goto error; + IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); - ret = iwl_send_bt_prio_tbl(mvm); + ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); if (ret) goto error; @@ -424,6 +472,17 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } + /* Initialize tx backoffs to the minimal possible */ + iwl_mvm_tt_tx_backoff(mvm, 0); + + ret = iwl_mvm_power_update_device(mvm); + if (ret) + goto error; + + /* allow FW/transport low power modes if not during restart */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: @@ -447,7 +506,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) goto error; } - ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); + ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); if (ret) goto error; diff --git a/drivers/net/wireless/iwlwifi/mvm/led.c b/drivers/net/wireless/iwlwifi/mvm/led.c index 2269a9e5cc6..e3b3cf4dbd7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/iwlwifi/mvm/led.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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; @@ -103,7 +105,7 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm) return 0; default: return -EINVAL; - }; + } mvm->led.name = kasprintf(GFP_KERNEL, "%s-led", wiphy_name(mvm->hw->wiphy)); diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 5fe23a5ea9b..8b79081d488 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -69,10 +69,10 @@ #include "mvm.h" const u8 iwl_mvm_ac_to_tx_fifo[] = { - IWL_MVM_TX_FIFO_BK, - IWL_MVM_TX_FIFO_BE, - IWL_MVM_TX_FIFO_VI, IWL_MVM_TX_FIFO_VO, + IWL_MVM_TX_FIFO_VI, + IWL_MVM_TX_FIFO_BE, + IWL_MVM_TX_FIFO_BK, }; struct iwl_mvm_mac_iface_iterator_data { @@ -80,40 +80,21 @@ struct iwl_mvm_mac_iface_iterator_data { struct ieee80211_vif *vif; unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)]; unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)]; - unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_FIRST_AGG_QUEUE)]; + unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_MAX_QUEUES)]; enum iwl_tsf_id preferred_tsf; bool found_vif; }; -static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, - struct ieee80211_vif *vif) +static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) { struct iwl_mvm_mac_iface_iterator_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - u32 ac; + u16 min_bi; - /* Iterator may already find the interface being added -- skip it */ - if (vif == data->vif) { - data->found_vif = true; + /* Skip the interface for which we are trying to assign a tsf_id */ + if (vif == data->vif) return; - } - - /* Mark the queues used by the vif */ - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) - __set_bit(vif->hw_queue[ac], data->used_hw_queues); - - if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) - __set_bit(vif->cab_queue, data->used_hw_queues); - - /* - * Mark MAC IDs as used by clearing the available bit, and - * (below) mark TSFs as used if their existing use is not - * compatible with the new interface type. - * No locking or atomic bit operations are needed since the - * data is on the stack of the caller function. - */ - __clear_bit(mvmvif->id, data->available_mac_ids); /* * The TSF is a hardware/firmware resource, there are 4 and @@ -134,33 +115,56 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, switch (data->vif->type) { case NL80211_IFTYPE_STATION: /* - * The new interface is client, so if the existing one - * we're iterating is an AP, the TSF should be used to - * avoid drift between the new client and existing AP, - * the existing AP will get drift updates from the new - * client context in this case + * The new interface is a client, so if the one we're iterating + * is an AP, and the beacon interval of the AP is a multiple or + * divisor of the beacon interval of the client, the same TSF + * should be used to avoid drift between the new client and + * existing AP. The existing AP will get drift updates from the + * new client context in this case. */ - if (vif->type == NL80211_IFTYPE_AP) { - if (data->preferred_tsf == NUM_TSF_IDS && - test_bit(mvmvif->tsf_id, data->available_tsf_ids)) - data->preferred_tsf = mvmvif->tsf_id; + if (vif->type != NL80211_IFTYPE_AP || + data->preferred_tsf != NUM_TSF_IDS || + !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) + break; + + min_bi = min(data->vif->bss_conf.beacon_int, + vif->bss_conf.beacon_int); + + if (!min_bi) + break; + + if ((data->vif->bss_conf.beacon_int - + vif->bss_conf.beacon_int) % min_bi == 0) { + data->preferred_tsf = mvmvif->tsf_id; return; } break; + case NL80211_IFTYPE_AP: /* - * The new interface is AP/GO, so should get drift - * updates from an existing client or use the same - * TSF as an existing GO. There's no drift between - * TSFs internally but if they used different TSFs - * then a new client MAC could update one of them - * and cause drift that way. + * The new interface is AP/GO, so if its beacon interval is a + * multiple or a divisor of the beacon interval of an existing + * interface, it should get drift updates from an existing + * client or use the same TSF as an existing GO. There's no + * drift between TSFs internally but if they used different + * TSFs then a new client MAC could update one of them and + * cause drift that way. */ - if (vif->type == NL80211_IFTYPE_STATION || - vif->type == NL80211_IFTYPE_AP) { - if (data->preferred_tsf == NUM_TSF_IDS && - test_bit(mvmvif->tsf_id, data->available_tsf_ids)) - data->preferred_tsf = mvmvif->tsf_id; + if ((vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_STATION) || + data->preferred_tsf != NUM_TSF_IDS || + !test_bit(mvmvif->tsf_id, data->available_tsf_ids)) + break; + + min_bi = min(data->vif->bss_conf.beacon_int, + vif->bss_conf.beacon_int); + + if (!min_bi) + break; + + if ((data->vif->bss_conf.beacon_int - + vif->bss_conf.beacon_int) % min_bi == 0) { + data->preferred_tsf = mvmvif->tsf_id; return; } break; @@ -187,6 +191,39 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, data->preferred_tsf = NUM_TSF_IDS; } +static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mac_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 ac; + + /* Iterator may already find the interface being added -- skip it */ + if (vif == data->vif) { + data->found_vif = true; + return; + } + + /* Mark the queues used by the vif */ + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) + __set_bit(vif->hw_queue[ac], data->used_hw_queues); + + if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE) + __set_bit(vif->cab_queue, data->used_hw_queues); + + /* Mark MAC IDs as used by clearing the available bit, and + * (below) mark TSFs as used if their existing use is not + * compatible with the new interface type. + * No locking or atomic bit operations are needed since the + * data is on the stack of the caller function. + */ + __clear_bit(mvmvif->id, data->available_mac_ids); + + /* find a suitable tsf_id */ + iwl_mvm_mac_tsf_id_iter(_data, mac, vif); +} + /* * Get the mask of the queus used by the vif */ @@ -205,6 +242,29 @@ u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, return qmask; } +void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_mac_iface_iterator_data data = { + .mvm = mvm, + .vif = vif, + .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 }, + /* no preference yet */ + .preferred_tsf = NUM_TSF_IDS, + }; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mvm_mac_tsf_id_iter, &data); + + if (data.preferred_tsf != NUM_TSF_IDS) + mvmvif->tsf_id = data.preferred_tsf; + else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids)) + mvmvif->tsf_id = find_first_bit(data.available_tsf_ids, + NUM_TSF_IDS); +} + static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -218,7 +278,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, .preferred_tsf = NUM_TSF_IDS, .used_hw_queues = { BIT(IWL_MVM_OFFCHANNEL_QUEUE) | - BIT(IWL_MVM_AUX_QUEUE) | + BIT(mvm->aux_queue) | BIT(IWL_MVM_CMD_QUEUE) }, .found_vif = false, @@ -242,9 +302,17 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, * that we should share it with another interface. */ - /* Currently, MAC ID 0 should be used only for the managed vif */ - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */ + switch (vif->type) { + case NL80211_IFTYPE_ADHOC: + break; + case NL80211_IFTYPE_STATION: + if (!vif->p2p) + break; + /* fall through */ + default: __clear_bit(0, data.available_mac_ids); + } ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, @@ -302,9 +370,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, /* Find available queues, and allocate them to the ACs */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { u8 queue = find_first_zero_bit(data.used_hw_queues, - IWL_MVM_FIRST_AGG_QUEUE); + mvm->first_agg_queue); - if (queue >= IWL_MVM_FIRST_AGG_QUEUE) { + if (queue >= mvm->first_agg_queue) { IWL_ERR(mvm, "Failed to allocate queue\n"); ret = -EIO; goto exit_fail; @@ -317,9 +385,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, /* Allocate the CAB queue for softAP and GO interfaces */ if (vif->type == NL80211_IFTYPE_AP) { u8 queue = find_first_zero_bit(data.used_hw_queues, - IWL_MVM_FIRST_AGG_QUEUE); + mvm->first_agg_queue); - if (queue >= IWL_MVM_FIRST_AGG_QUEUE) { + if (queue >= mvm->first_agg_queue) { IWL_ERR(mvm, "Failed to allocate cab queue\n"); ret = -EIO; goto exit_fail; @@ -480,6 +548,40 @@ static void iwl_mvm_ack_rates(struct iwl_mvm *mvm, *ofdm_rates = ofdm; } +static void iwl_mvm_mac_ctxt_set_ht_flags(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_ctx_cmd *cmd) +{ + /* for both sta and ap, ht_operation_mode hold the protection_mode */ + u8 protection_mode = vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + /* The fw does not distinguish between ht and fat */ + u32 ht_flag = MAC_PROT_FLG_HT_PROT | MAC_PROT_FLG_FAT_PROT; + + IWL_DEBUG_RATE(mvm, "protection mode set to %d\n", protection_mode); + /* + * See section 9.23.3.1 of IEEE 80211-2012. + * Nongreenfield HT STAs Present is not supported. + */ + switch (protection_mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + cmd->protection_flags |= cpu_to_le32(ht_flag); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* Protect when channel wider than 20MHz */ + if (vif->bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) + cmd->protection_flags |= cpu_to_le32(ht_flag); + break; + default: + IWL_ERR(mvm, "Illegal protection mode %d\n", + protection_mode); + break; + } +} + static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_ctx_cmd *cmd, @@ -487,6 +589,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_chanctx_conf *chanctx; + bool ht_enabled = !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION); u8 cck_ack_rates, ofdm_ack_rates; int i; @@ -542,35 +646,37 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cpu_to_le32(vif->bss_conf.use_short_slot ? MAC_FLG_SHORT_SLOT : 0); - for (i = 0; i < AC_NUM; i++) { - cmd->ac[i].cw_min = cpu_to_le16(mvmvif->queue_params[i].cw_min); - cmd->ac[i].cw_max = cpu_to_le16(mvmvif->queue_params[i].cw_max); - cmd->ac[i].aifsn = mvmvif->queue_params[i].aifs; - cmd->ac[i].edca_txop = + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 txf = iwl_mvm_ac_to_tx_fifo[i]; + + cmd->ac[txf].cw_min = + cpu_to_le16(mvmvif->queue_params[i].cw_min); + cmd->ac[txf].cw_max = + cpu_to_le16(mvmvif->queue_params[i].cw_max); + cmd->ac[txf].edca_txop = cpu_to_le16(mvmvif->queue_params[i].txop * 32); - cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]); + cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs; + cmd->ac[txf].fifos_mask = BIT(txf); } /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ if (vif->type == NL80211_IFTYPE_AP) - cmd->ac[AC_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); + cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= + BIT(IWL_MVM_TX_FIFO_MCAST); if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); - /* Don't use cts to self as the fw doesn't support it currently. */ if (vif->bss_conf.use_cts_prot) cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); - /* - * I think that we should enable these 2 flags regardless the HT PROT - * fields in the HT IE, but I am not sure. Someone knows whom to ask?... - */ - if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { + IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n", + vif->bss_conf.use_cts_prot, + vif->bss_conf.ht_operation_mode); + if (vif->bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); - cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_HT_PROT | - MAC_PROT_FLG_FAT_PROT); - } + if (ht_enabled) + iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); } @@ -578,7 +684,7 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, struct iwl_mac_ctx_cmd *cmd) { - int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC, + int ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, sizeof(*cmd), cmd); if (ret) IWL_ERR(mvm, "Failed to send MAC context (action:%d): %d\n", @@ -586,19 +692,39 @@ static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, return ret; } -/* - * Fill the specific data for mac context of type station or p2p client - */ -static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_mac_data_sta *ctxt_sta, - bool force_assoc_off) +static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action, bool force_assoc_off) { + struct iwl_mac_ctx_cmd cmd = {}; + struct iwl_mac_data_sta *ctxt_sta; + + WARN_ON(vif->type != NL80211_IFTYPE_STATION); + + /* Fill the common data for all mac context types */ + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + if (vif->p2p) { + struct ieee80211_p2p_noa_attr *noa = + &vif->bss_conf.p2p_noa_attr; + + cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & + IEEE80211_P2P_OPPPS_CTWINDOW_MASK); + ctxt_sta = &cmd.p2p_sta.sta; + } else { + ctxt_sta = &cmd.sta; + } + /* We need the dtim_period to set the MAC as associated */ if (vif->bss_conf.assoc && vif->bss_conf.dtim_period && !force_assoc_off) { u32 dtim_offs; + /* Allow beacons to pass through as long as we are not + * associated, or we do not have dtim period information. + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + /* * The DTIM count counts down, so when it is N that means N * more beacon intervals happen until the DTIM TBTT. Therefore @@ -645,70 +771,52 @@ static void iwl_mvm_mac_ctxt_cmd_fill_sta(struct iwl_mvm *mvm, ctxt_sta->listen_interval = cpu_to_le32(mvm->hw->conf.listen_interval); ctxt_sta->assoc_id = cpu_to_le32(vif->bss_conf.aid); -} - -static int iwl_mvm_mac_ctxt_cmd_station(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) -{ - struct iwl_mac_ctx_cmd cmd = {}; - - WARN_ON(vif->type != NL80211_IFTYPE_STATION || vif->p2p); - - /* Fill the common data for all mac context types */ - iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); - - /* Allow beacons to pass through as long as we are not associated,or we - * do not have dtim period information */ - if (!vif->bss_conf.assoc || !vif->bss_conf.dtim_period) - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - else - cmd.filter_flags &= ~cpu_to_le32(MAC_FILTER_IN_BEACON); - - /* Fill the data specific for station mode */ - iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.sta, - action == FW_CTXT_ACTION_ADD); return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } -static int iwl_mvm_mac_ctxt_cmd_p2p_client(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) +static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) { struct iwl_mac_ctx_cmd cmd = {}; - struct ieee80211_p2p_noa_attr *noa = &vif->bss_conf.p2p_noa_attr; - WARN_ON(vif->type != NL80211_IFTYPE_STATION || !vif->p2p); + WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); - /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); - /* Fill the data specific for station mode */ - iwl_mvm_mac_ctxt_cmd_fill_sta(mvm, vif, &cmd.p2p_sta.sta, - action == FW_CTXT_ACTION_ADD); - - cmd.p2p_sta.ctwin = cpu_to_le32(noa->oppps_ctwindow & - IEEE80211_P2P_OPPPS_CTWINDOW_MASK); + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | + MAC_FILTER_IN_CONTROL_AND_MGMT | + MAC_FILTER_IN_BEACON | + MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_IN_CRC32); + mvm->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS; return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } -static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u32 action) +static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_ctx_cmd cmd = {}; - WARN_ON(vif->type != NL80211_IFTYPE_MONITOR); + WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); - cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | - MAC_FILTER_IN_CONTROL_AND_MGMT | - MAC_FILTER_IN_BEACON | + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | MAC_FILTER_IN_PROBE_REQUEST); + /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ + cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); + cmd.ibss.bi_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); + + /* TODO: Assumes that the beacon id == mac context id */ + cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id); + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -721,7 +829,8 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) struct iwl_mvm_go_iterator_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (vif->type == NL80211_IFTYPE_AP && vif->p2p && mvmvif->ap_active) + if (vif->type == NL80211_IFTYPE_AP && vif->p2p && + mvmvif->ap_ibss_active) data->go_active = true; } @@ -814,7 +923,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, TX_CMD_FLG_TSF); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw), + iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, mvm->mgmt_last_antenna_idx); beacon_cmd.tx.rate_n_flags = @@ -833,9 +942,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); /* Set up TX beacon command fields */ - iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, - beacon->data, - beacon_skb_len); + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, + beacon->data, + beacon_skb_len); /* Submit command */ cmd.len[0] = sizeof(beacon_cmd); @@ -848,14 +958,15 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, return iwl_mvm_send_cmd(mvm, &cmd); } -/* The beacon template for the AP/GO context has changed and needs update */ +/* The beacon template for the AP/GO/IBSS has changed and needs update */ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct sk_buff *beacon; int ret; - WARN_ON(vif->type != NL80211_IFTYPE_AP); + WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC); beacon = ieee80211_beacon_get(mvm->hw, vif); if (!beacon) @@ -932,7 +1043,7 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm, iwl_mvm_mac_ap_iterator, &data); if (data.beacon_device_ts) { - u32 rand = (prandom_u32() % (80 - 20)) + 20; + u32 rand = (prandom_u32() % (64 - 36)) + 36; mvmvif->ap_beacon_time = data.beacon_device_ts + ieee80211_tu_to_usec(data.beacon_int * rand / 100); @@ -961,8 +1072,12 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); - /* Also enable probe requests to pass */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + /* + * pass probe requests and beacons from other APs (needed + * for ht protection) + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_IN_BEACON); /* Fill the data specific for ap mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, @@ -983,6 +1098,13 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + /* + * pass probe requests and beacons from other APs (needed + * for ht protection) + */ + cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_IN_BEACON); + /* Fill the data specific for GO mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, action == FW_CTXT_ACTION_ADD); @@ -997,16 +1119,12 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, } static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 action) + u32 action, bool force_assoc_off) { switch (vif->type) { case NL80211_IFTYPE_STATION: - if (!vif->p2p) - return iwl_mvm_mac_ctxt_cmd_station(mvm, vif, - action); - else - return iwl_mvm_mac_ctxt_cmd_p2p_client(mvm, vif, - action); + return iwl_mvm_mac_ctxt_cmd_sta(mvm, vif, action, + force_assoc_off); break; case NL80211_IFTYPE_AP: if (!vif->p2p) @@ -1018,6 +1136,8 @@ static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action); case NL80211_IFTYPE_P2P_DEVICE: return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action); + case NL80211_IFTYPE_ADHOC: + return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action); default: break; } @@ -1034,15 +1154,20 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) vif->addr, ieee80211_vif_type_p2p(vif))) return -EIO; - ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD); + ret = iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_ADD, + true); if (ret) return ret; + /* will only do anything at resume from D3 time */ + iwl_mvm_set_last_nonqos_seq(mvm, vif); + mvmvif->uploaded = true; return 0; } -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -1050,7 +1175,8 @@ int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) vif->addr, ieee80211_vif_type_p2p(vif))) return -EIO; - return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY); + return iwl_mvm_mac_ctx_send(mvm, vif, FW_CTXT_ACTION_MODIFY, + force_assoc_off); } int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) @@ -1069,7 +1195,7 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvmvif->color)); cmd.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE); - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, MAC_CONTEXT_CMD, 0, sizeof(cmd), &cmd); if (ret) { IWL_ERR(mvm, "Failed to remove MAC context: %d\n", ret); @@ -1077,6 +1203,10 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) } mvmvif->uploaded = false; + + if (vif->type == NL80211_IFTYPE_MONITOR) + mvm->hw->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; + return 0; } @@ -1091,21 +1221,41 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, u32 rate __maybe_unused = le32_to_cpu(beacon->beacon_notify_hdr.initial_rate); + lockdep_assert_held(&mvm->mutex); + IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n", status & TX_STATUS_MSK, beacon->beacon_notify_hdr.failure_frame, le64_to_cpu(beacon->tsf), rate); + + if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) { + if (!ieee80211_csa_is_complete(mvm->csa_vif)) { + iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif); + } else { + ieee80211_csa_finish(mvm->csa_vif); + mvm->csa_vif = NULL; + } + } + return 0; } static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { - u16 *id = _data; + struct iwl_missed_beacons_notif *missed_beacons = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (mvmvif->id == *id) + if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id)) + return; + + /* + * TODO: the threshold should be adjusted based on latency conditions, + * and/or in case of a CS flow on one of the other AP vifs. + */ + if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) > + IWL_MVM_MISSED_BEACONS_THRESHOLD) ieee80211_beacon_loss(vif); } @@ -1114,12 +1264,19 @@ int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data; - u16 id = (u16)le32_to_cpu(missed_beacons->mac_id); + struct iwl_missed_beacons_notif *mb = (void *)pkt->data; + + IWL_DEBUG_INFO(mvm, + "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n", + le32_to_cpu(mb->mac_id), + le32_to_cpu(mb->consec_missed_beacons), + le32_to_cpu(mb->consec_missed_beacons_since_last_rx), + le32_to_cpu(mb->num_recvd_beacons), + le32_to_cpu(mb->num_expected_beacons)); ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_beacon_loss_iterator, - &id); + mb); return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9833cdf6177..98556d03c1e 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,7 +66,9 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ip.h> +#include <linux/if_arp.h> #include <net/mac80211.h> +#include <net/ieee80211_radiotap.h> #include <net/tcp.h> #include "iwl-op-mode.h" @@ -77,6 +79,7 @@ #include "iwl-eeprom-parse.h" #include "fw-api-scan.h" #include "iwl-phy-db.h" +#include "testmode.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -127,6 +130,117 @@ static const struct wiphy_wowlan_tcp_support iwl_mvm_wowlan_tcp_support = { }; #endif +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +/* + * Use the reserved field to indicate magic values. + * these values will only be used internally by the driver, + * and won't make it to the fw (reserved will be 0). + * BC_FILTER_MAGIC_IP - configure the val of this attribute to + * be the vif's ip address. in case there is not a single + * ip address (0, or more than 1), this attribute will + * be skipped. + * BC_FILTER_MAGIC_MAC - set the val of this attribute to + * the LSB bytes of the vif's mac address + */ +enum { + BC_FILTER_MAGIC_NONE = 0, + BC_FILTER_MAGIC_IP, + BC_FILTER_MAGIC_MAC, +}; + +static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = { + { + /* arp */ + .discard = 0, + .frame_type = BCAST_FILTER_FRAME_TYPE_ALL, + .attrs = { + { + /* frame type - arp, hw type - ethernet */ + .offset_type = + BCAST_FILTER_OFFSET_PAYLOAD_START, + .offset = sizeof(rfc1042_header), + .val = cpu_to_be32(0x08060001), + .mask = cpu_to_be32(0xffffffff), + }, + { + /* arp dest ip */ + .offset_type = + BCAST_FILTER_OFFSET_PAYLOAD_START, + .offset = sizeof(rfc1042_header) + 2 + + sizeof(struct arphdr) + + ETH_ALEN + sizeof(__be32) + + ETH_ALEN, + .mask = cpu_to_be32(0xffffffff), + /* mark it as special field */ + .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_IP), + }, + }, + }, + { + /* dhcp offer bcast */ + .discard = 0, + .frame_type = BCAST_FILTER_FRAME_TYPE_IPV4, + .attrs = { + { + /* udp dest port - 68 (bootp client)*/ + .offset_type = BCAST_FILTER_OFFSET_IP_END, + .offset = offsetof(struct udphdr, dest), + .val = cpu_to_be32(0x00440000), + .mask = cpu_to_be32(0xffff0000), + }, + { + /* dhcp - lsb bytes of client hw address */ + .offset_type = BCAST_FILTER_OFFSET_IP_END, + .offset = 38, + .mask = cpu_to_be32(0xffffffff), + /* mark it as special field */ + .reserved1 = cpu_to_le16(BC_FILTER_MAGIC_MAC), + }, + }, + }, + /* last filter must be empty */ + {}, +}; +#endif + +void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type); + WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap)); + iwl_trans_ref(mvm->trans); +} + +void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type) +{ + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type); + WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap)); + iwl_trans_unref(mvm->trans); +} + +static void +iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref) +{ + int i; + + if (!iwl_mvm_is_d0i3_supported(mvm)) + return; + + for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) { + if (ref == i) + continue; + + IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i); + clear_bit(i, mvm->ref_bitmap); + iwl_trans_unref(mvm->trans); + } +} + static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) { int i; @@ -138,6 +252,14 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) } } +static int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) +{ + /* we create the 802.11 header and SSID element */ + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID) + return mvm->fw->ucode_capa.max_probe_length - 24 - 2; + return mvm->fw->ucode_capa.max_probe_length - 24 - 34; +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -155,11 +277,13 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_TIMING_BEACON_ONLY | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_SUPPORTS_STATIC_SMPS | - IEEE80211_HW_SUPPORTS_UAPSD; + IEEE80211_HW_SUPPORTS_STATIC_SMPS; - hw->queues = IWL_MVM_FIRST_AGG_QUEUE; + hw->queues = mvm->first_agg_queue; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | + IEEE80211_RADIOTAP_MCS_HAVE_STBC; + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC; hw->rate_control_algorithm = "iwl-mvm-rs"; /* @@ -171,6 +295,14 @@ 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 && + IWL_UCODE_API(mvm->fw->ucode_ver) >= 9 && + !iwlwifi_mod_params.uapsd_disable) { + hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; + hw->uapsd_queues = IWL_UAPSD_AC_INFO; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; + } + hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); hw->chanctx_data_size = sizeof(u16); @@ -179,11 +311,18 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE); + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_ADHOC); - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_GO_UAPSD) + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_CSA_FLOW) + hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; hw->wiphy->iface_combinations = iwl_mvm_iface_combinations; hw->wiphy->n_iface_combinations = @@ -191,8 +330,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_remain_on_channel_duration = 10000; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - hw->uapsd_queues = IWL_UAPSD_AC_INFO; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; /* Extract MAC address */ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); @@ -212,9 +349,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) iwl_mvm_reset_phy_ctxts(mvm); - /* we create the 802.11 header and a max-length SSID element */ - hw->wiphy->max_scan_ie_len = - mvm->fw->ucode_capa.max_probe_length - 24 - 34; + hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) @@ -231,13 +367,29 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) else hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + 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; + /* we create the 802.11 header and zero length SSID IE. */ + hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; + hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | NL80211_FEATURE_P2P_GO_OPPPS; mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + /* currently FW API supports only one optional cipher scheme */ + if (mvm->fw->cs[0].cipher) { + mvm->hw->n_cipher_schemes = 1; + mvm->hw->cipher_schemes = &mvm->fw->cs[0]; + } + #ifdef CONFIG_PM_SLEEP - if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && + if (iwl_mvm_is_d0i3_supported(mvm) && + device_can_wakeup(mvm->trans->dev)) { + mvm->wowlan.flags = WIPHY_WOWLAN_ANY; + hw->wiphy->wowlan = &mvm->wowlan; + } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && mvm->trans->ops->d3_suspend && mvm->trans->ops->d3_resume && device_can_wakeup(mvm->trans->dev)) { @@ -258,6 +410,11 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) } #endif +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING + /* assign default bcast filtering configuration */ + mvm->bcast_filters = iwl_mvm_default_bcast_filters; +#endif + ret = iwl_mvm_leds_init(mvm); if (ret) return ret; @@ -269,11 +426,55 @@ 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) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct ieee80211_sta *sta = control->sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; if (iwl_mvm_is_radio_killed(mvm)) { IWL_DEBUG_DROP(mvm, "Dropping - RF/CT KILL\n"); @@ -284,8 +485,18 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, !test_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status)) goto drop; - if (control->sta) { - if (iwl_mvm_tx_skb(mvm, skb, control->sta)) + /* treat non-bufferable MMPDUs as broadcast if sta is sleeping */ + if (unlikely(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER && + ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_is_deauth(hdr->frame_control) && + !ieee80211_is_disassoc(hdr->frame_control) && + !ieee80211_is_action(hdr->frame_control))) + sta = NULL; + + if (sta) { + if (iwl_mvm_defer_tx(mvm, sta, skb)) + return; + if (iwl_mvm_tx_skb(mvm, skb, sta)) goto drop; return; } @@ -297,6 +508,24 @@ static void iwl_mvm_mac_tx(struct ieee80211_hw *hw, ieee80211_free_txskb(hw, skb); } +static inline bool iwl_enable_rx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + return false; + return true; +} + +static inline bool iwl_enable_tx_ampdu(const struct iwl_cfg *cfg) +{ + if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + return false; + if (iwlwifi_mod_params.disable_11n & IWL_ENABLE_HT_TXAGG) + return true; + + /* enabled by default */ + return true; +} + static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, @@ -305,6 +534,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); @@ -312,11 +542,40 @@ 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 */ + switch (action) { + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP_CONT: + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_OPERATIONAL: + iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG); + tx_agg_ref = true; + + /* + * for tx start, wait synchronously until D0i3 exit to + * get the correct sequence number for the tid. + * additionally, some other ampdu actions use direct + * target access, which is not handled automatically + * by the trans layer (unlike commands), so wait for + * d0i3 exit in these cases as well. + */ + 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; + } + break; + default: + break; + } + mutex_lock(&mvm->mutex); switch (action) { case IEEE80211_AMPDU_RX_START: - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) { + if (!iwl_enable_rx_ampdu(mvm->cfg)) { ret = -EINVAL; break; } @@ -326,7 +585,7 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw, ret = iwl_mvm_sta_rx_agg(mvm, sta, tid, 0, false); break; case IEEE80211_AMPDU_TX_START: - if (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) { + if (!iwl_enable_tx_ampdu(mvm->cfg)) { ret = -EINVAL; break; } @@ -349,6 +608,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; } @@ -373,8 +639,16 @@ 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); - iwl_trans_stop_hw(mvm->trans, false); mvm->scan_status = IWL_MVM_SCAN_NONE; @@ -386,6 +660,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) iwl_mvm_cleanup_iterator, mvm); mvm->p2p_device_vif = NULL; + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; iwl_mvm_reset_phy_ctxts(mvm); memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table)); @@ -393,6 +668,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ieee80211_wake_queues(mvm->hw); + /* cleanup all stale references (scan, roc), but keep the + * ucode_down ref until reconfig is complete */ + iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); + mvm->vif_count = 0; mvm->rx_ba_sessions = 0; } @@ -422,11 +701,15 @@ 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", ret); + /* allow transport/FW low power modes */ + iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); + mutex_unlock(&mvm->mutex); } @@ -434,9 +717,14 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + flush_work(&mvm->d0i3_exit_work); flush_work(&mvm->async_handlers_wk); mutex_lock(&mvm->mutex); + + /* disallow low power states when the FW is down */ + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); + /* async_handlers_wk is now blocked */ /* @@ -446,7 +734,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) cancel_work_sync(&mvm->roc_done_wk); iwl_trans_stop_device(mvm->trans); - iwl_trans_stop_hw(mvm->trans, false); iwl_mvm_async_handlers_purge(mvm); /* async_handlers_list is empty and will stay empty: HW is stopped */ @@ -463,25 +750,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) cancel_work_sync(&mvm->async_handlers_wk); } -static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = data; - int ret; - - ret = iwl_mvm_power_disable(mvm, vif); - if (ret) - IWL_ERR(mvm, "failed to disable power management\n"); -} - -static void iwl_mvm_power_update_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = data; - - iwl_mvm_power_update_mode(mvm, vif); -} - static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) { u16 i; @@ -496,6 +764,20 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) return NULL; } +static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + s8 tx_power) +{ + /* FW is in charge of regulatory enforcement */ + struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { + .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, + .pwr_restriction = cpu_to_le16(tx_power), + }; + + return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, + sizeof(reduce_txpwr_cmd), + &reduce_txpwr_cmd); +} + static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -516,26 +798,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_unlock; - /* - * TODO: remove this temporary code. - * Currently MVM FW supports power management only on single MAC. - * If new interface added, disable PM on existing interface. - * P2P device is a special case, since it is handled by FW similary to - * scan. If P2P deviced is added, PM remains enabled on existing - * interface. - * Note: the method below does not count the new interface being added - * at this moment. - */ + /* Counting number of interfaces is needed for legacy PM */ if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count++; - if (mvm->vif_count > 1) { - IWL_DEBUG_MAC80211(mvm, - "Disable power on existing interfaces\n"); - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_pm_disable_iterator, mvm); - } /* * The AP binding flow can be done only after the beacon @@ -548,10 +813,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, * In short: there's not much we can do at this point, other than * allocating resources :) */ - if (vif->type == NL80211_IFTYPE_AP) { + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, - qmask); + qmask, + ieee80211_vif_type_p2p(vif)); if (ret) { IWL_ERR(mvm, "Failed to allocate bcast sta\n"); goto out_release; @@ -565,20 +832,17 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_release; - /* - * Update power state on the new interface. Admittedly, based on - * mac80211 logics this power update will disable power management - */ - iwl_mvm_power_update_mode(mvm, vif); + ret = iwl_mvm_power_update_mac(mvm); + if (ret) + goto out_release; /* beacon filtering */ - ret = iwl_mvm_disable_beacon_filter(mvm, vif); + ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); if (ret) goto out_remove_mac; if (!mvm->bf_allowed_vif && - vif->type == NL80211_IFTYPE_STATION && !vif->p2p && - mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){ + vif->type == NL80211_IFTYPE_STATION && !vif->p2p) { mvm->bf_allowed_vif = mvmvif; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; @@ -630,9 +894,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_release: if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_update_iterator, mvm); + iwl_mvm_mac_ctxt_release(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); @@ -698,7 +960,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, * For AP/GO interface, the tear down of the resources allocated to the * interface is be handled as part of the stop_ap flow. */ - if (vif->type == NL80211_IFTYPE_AP) { + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { +#ifdef CONFIG_NL80211_TESTMODE + if (vif == mvm->noa_vif) { + mvm->noa_vif = NULL; + mvm->noa_duration = 0; + } +#endif iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); goto out_release; } @@ -711,22 +980,10 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvmvif->phy_ctxt = NULL; } - /* - * TODO: remove this temporary code. - * Currently MVM FW supports power management only on single MAC. - * Check if only one additional interface remains after removing - * current one. Update power mode on the remaining interface. - */ if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; - IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count == 1) { - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_update_iterator, mvm); - } + iwl_mvm_power_update_mac(mvm); iwl_mvm_mac_ctxt_remove(mvm, vif); out_release: @@ -734,23 +991,91 @@ out_release: mutex_unlock(&mvm->mutex); } -static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - s8 tx_power) +static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) { - /* FW is in charge of regulatory enforcement */ - struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { - .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, - .pwr_restriction = cpu_to_le16(tx_power), + return 0; +} + +struct iwl_mvm_mc_iter_data { + struct iwl_mvm *mvm; + int port_id; +}; + +static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mc_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd; + int ret, len; + + /* if we don't have free ports, mcast frames will be dropped */ + if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM)) + return; + + if (vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc) + return; + + cmd->port_id = data->port_id++; + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); + len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); + + ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd); + if (ret) + IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); +} + +static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) +{ + struct iwl_mvm_mc_iter_data iter_data = { + .mvm = mvm, }; - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC, - sizeof(reduce_txpwr_cmd), - &reduce_txpwr_cmd); + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) + return; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_mc_iface_iterator, &iter_data); } -static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) +static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) { - return 0; + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcast_filter_cmd *cmd; + struct netdev_hw_addr *addr; + int addr_count = netdev_hw_addr_list_count(mc_list); + bool pass_all = false; + int len; + + if (addr_count > MAX_MCAST_FILTERING_ADDRESSES) { + pass_all = true; + addr_count = 0; + } + + len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); + cmd = kzalloc(len, GFP_ATOMIC); + if (!cmd) + return 0; + + if (pass_all) { + cmd->pass_all = 1; + return (u64)(unsigned long)cmd; + } + + netdev_hw_addr_list_for_each(addr, mc_list) { + IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n", + cmd->count, addr->addr); + memcpy(&cmd->addr_list[cmd->count * ETH_ALEN], + addr->addr, ETH_ALEN); + cmd->count++; + } + + return (u64)(unsigned long)cmd; } static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, @@ -758,22 +1083,177 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, unsigned int *total_flags, u64 multicast) { + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; + + mutex_lock(&mvm->mutex); + + /* replace previous configuration */ + kfree(mvm->mcast_filter_cmd); + mvm->mcast_filter_cmd = cmd; + + if (!cmd) + goto out; + + iwl_mvm_recalc_multicast(mvm); +out: + mutex_unlock(&mvm->mutex); *total_flags = 0; } -static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm, +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING +struct iwl_bcast_iter_data { + struct iwl_mvm *mvm; + struct iwl_bcast_filter_cmd *cmd; + u8 current_filter; +}; + +static void +iwl_mvm_set_bcast_filter(struct ieee80211_vif *vif, + const struct iwl_fw_bcast_filter *in_filter, + struct iwl_fw_bcast_filter *out_filter) +{ + struct iwl_fw_bcast_filter_attr *attr; + int i; + + memcpy(out_filter, in_filter, sizeof(*out_filter)); + + for (i = 0; i < ARRAY_SIZE(out_filter->attrs); i++) { + attr = &out_filter->attrs[i]; + + if (!attr->mask) + break; + + switch (attr->reserved1) { + case cpu_to_le16(BC_FILTER_MAGIC_IP): + if (vif->bss_conf.arp_addr_cnt != 1) { + attr->mask = 0; + continue; + } + + attr->val = vif->bss_conf.arp_addr_list[0]; + break; + case cpu_to_le16(BC_FILTER_MAGIC_MAC): + attr->val = *(__be32 *)&vif->addr[2]; + break; + default: + break; + } + attr->reserved1 = 0; + out_filter->num_attrs++; + } +} + +static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mcast_filter_cmd mcast_filter_cmd = { - .pass_all = 1, + struct iwl_bcast_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_bcast_filter_cmd *cmd = data->cmd; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_fw_bcast_mac *bcast_mac; + int i; + + if (WARN_ON(mvmvif->id >= ARRAY_SIZE(cmd->macs))) + return; + + bcast_mac = &cmd->macs[mvmvif->id]; + + /* + * enable filtering only for associated stations, but not for P2P + * Clients + */ + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p || + !vif->bss_conf.assoc) + return; + + bcast_mac->default_discard = 1; + + /* copy all configured filters */ + for (i = 0; mvm->bcast_filters[i].attrs[0].mask; i++) { + /* + * Make sure we don't exceed our filters limit. + * if there is still a valid filter to be configured, + * be on the safe side and just allow bcast for this mac. + */ + if (WARN_ON_ONCE(data->current_filter >= + ARRAY_SIZE(cmd->filters))) { + bcast_mac->default_discard = 0; + bcast_mac->attached_filters = 0; + break; + } + + iwl_mvm_set_bcast_filter(vif, + &mvm->bcast_filters[i], + &cmd->filters[data->current_filter]); + + /* skip current filter if it contains no attributes */ + if (!cmd->filters[data->current_filter].num_attrs) + continue; + + /* attach the filter to current mac */ + bcast_mac->attached_filters |= + cpu_to_le16(BIT(data->current_filter)); + + data->current_filter++; + } +} + +bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, + struct iwl_bcast_filter_cmd *cmd) +{ + struct iwl_bcast_iter_data iter_data = { + .mvm = mvm, + .cmd = cmd, }; - memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN); + memset(cmd, 0, sizeof(*cmd)); + cmd->max_bcast_filters = ARRAY_SIZE(cmd->filters); + cmd->max_macs = ARRAY_SIZE(cmd->macs); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* use debugfs filters/macs if override is configured */ + if (mvm->dbgfs_bcast_filtering.override) { + memcpy(cmd->filters, &mvm->dbgfs_bcast_filtering.cmd.filters, + sizeof(cmd->filters)); + memcpy(cmd->macs, &mvm->dbgfs_bcast_filtering.cmd.macs, + sizeof(cmd->macs)); + return true; + } +#endif + + /* if no filters are configured, do nothing */ + if (!mvm->bcast_filters) + return false; + + /* configure and attach these filters for each associated sta vif */ + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bcast_filter_iterator, &iter_data); + + return true; +} +static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_bcast_filter_cmd cmd; + + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING)) + return 0; + + if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd)) + return 0; - return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, - sizeof(mcast_filter_cmd), - &mcast_filter_cmd); + return iwl_mvm_send_cmd_pdu(mvm, BCAST_FILTER_CMD, 0, + sizeof(cmd), &cmd); } +#else +static inline int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + return 0; +} +#endif static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -783,7 +1263,15 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + /* + * Re-calculate the tsf id, as the master-slave relations depend on the + * beacon interval, which was not known when the station interface was + * added. + */ + if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc) + iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); + + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); if (ret) IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); @@ -795,31 +1283,66 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to update quotas\n"); return; } - iwl_mvm_configure_mcast_filter(mvm, vif); + + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { + /* + * If we're restarting then the firmware will + * obviously have lost synchronisation with + * the AP. It will attempt to synchronise by + * itself, but we can make it more reliable by + * scheduling a session protection time event. + * + * The firmware needs to receive a beacon to + * catch up with synchronisation, use 110% of + * the beacon interval. + * + * Set a large maximum delay to allow for more + * than a single interface. + */ + u32 dur = (11 * vif->bss_conf.beacon_int) / 10; + iwl_mvm_protect_session(mvm, vif, dur, dur, + 5 * dur); + } + + iwl_mvm_sf_update(mvm, vif, false); + iwl_mvm_power_vif_assoc(mvm, vif); + if (vif->p2p) + iwl_mvm_ref(mvm, IWL_MVM_REF_P2P_CLIENT); } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + /* + * If update fails - SF might be running in associated + * mode while disassociated - which is forbidden. + */ + WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), + "Failed to update SF upon disassociation\n"); + /* remove AP station now that the MAC is unassoc */ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); if (ret) IWL_ERR(mvm, "failed to remove AP station\n"); + + if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; /* remove quota for this interface */ ret = iwl_mvm_update_quotas(mvm, NULL); if (ret) IWL_ERR(mvm, "failed to update quotas\n"); + + if (vif->p2p) + iwl_mvm_unref(mvm, IWL_MVM_REF_P2P_CLIENT); } + iwl_mvm_recalc_multicast(mvm); + iwl_mvm_configure_bcast_filter(mvm, vif); + /* reset rssi values */ mvmvif->bf_data.ave_beacon_signal = 0; - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) { - /* Workaround for FW bug, otherwise FW disables device - * power save upon disassociation - */ - ret = iwl_mvm_power_update_mode(mvm, vif); - if (ret) - IWL_ERR(mvm, "failed to update power mode\n"); - } - iwl_mvm_bt_coex_vif_assoc(mvm, vif); + iwl_mvm_bt_coex_vif_change(mvm); + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_TT, + IEEE80211_SMPS_AUTOMATIC); } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so @@ -827,8 +1350,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) { - ret = iwl_mvm_power_update_mode(mvm, vif); + iwl_mvm_sf_update(mvm, vif, false); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); + } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | + BSS_CHANGED_QOS)) { + ret = iwl_mvm_power_update_mac(mvm); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } @@ -839,16 +1365,25 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, } if (changes & BSS_CHANGED_CQM) { - IWL_DEBUG_MAC80211(mvm, "cqm info_changed"); + IWL_DEBUG_MAC80211(mvm, "cqm info_changed\n"); /* reset cqm events tracking */ mvmvif->bf_data.last_cqm_event = 0; - ret = iwl_mvm_update_beacon_filter(mvm, vif); - if (ret) - IWL_ERR(mvm, "failed to update CQM thresholds\n"); + if (mvmvif->bf_data.bf_enabled) { + ret = iwl_mvm_enable_beacon_filter(mvm, vif, 0); + if (ret) + IWL_ERR(mvm, + "failed to update CQM thresholds\n"); + } + } + + if (changes & BSS_CHANGED_ARP_FILTER) { + IWL_DEBUG_MAC80211(mvm, "arp filter changed\n"); + iwl_mvm_configure_bcast_filter(mvm, vif); } } -static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -861,6 +1396,13 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) if (ret) goto out_unlock; + /* + * Re-calculate the tsf id, as the master-slave relations depend on the + * beacon interval, which was not known when the AP interface was added. + */ + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif); + /* Add the mac context */ ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) @@ -871,26 +1413,36 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) if (ret) goto out_remove; - mvmvif->ap_active = true; - /* Send the bcast station. At this stage the TBTT and DTIM time events * are added and applied to the scheduler */ ret = iwl_mvm_send_bcast_sta(mvm, vif, &mvmvif->bcast_sta); if (ret) goto out_unbind; + /* must be set before quota calculations */ + mvmvif->ap_ibss_active = true; + + /* power updated needs to be done before quotas */ + iwl_mvm_power_update_mac(mvm); + ret = iwl_mvm_update_quotas(mvm, vif); if (ret) - goto out_rm_bcast; + goto out_quota_failed; - /* Need to update the P2P Device MAC */ + /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); + + iwl_mvm_ref(mvm, IWL_MVM_REF_AP_IBSS); + + iwl_mvm_bt_coex_vif_change(mvm); mutex_unlock(&mvm->mutex); return 0; -out_rm_bcast: +out_quota_failed: + iwl_mvm_power_update_mac(mvm); + mvmvif->ap_ibss_active = false; iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); out_unbind: iwl_mvm_binding_remove_vif(mvm, vif); @@ -901,7 +1453,8 @@ out_unlock: return ret; } -static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -910,30 +1463,48 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mutex_lock(&mvm->mutex); - mvmvif->ap_active = false; + mvmvif->ap_ibss_active = false; - /* Need to update the P2P Device MAC */ + iwl_mvm_bt_coex_vif_change(mvm); + + iwl_mvm_unref(mvm, IWL_MVM_REF_AP_IBSS); + + /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) - iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); + iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif, false); iwl_mvm_update_quotas(mvm, NULL); iwl_mvm_send_rm_bcast_sta(mvm, &mvmvif->bcast_sta); iwl_mvm_binding_remove_vif(mvm, vif); + + iwl_mvm_power_update_mac(mvm); + iwl_mvm_mac_ctxt_remove(mvm, vif); mutex_unlock(&mvm->mutex); } -static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) +static void +iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* Changes will be applied when the AP/IBSS is started */ + if (!mvmvif->ap_ibss_active) + return; + + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | + BSS_CHANGED_BANDWIDTH) && + iwl_mvm_mac_ctxt_changed(mvm, vif, false)) + IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr); + /* Need to send a new beacon template to the FW */ - if (changes & BSS_CHANGED_BEACON) { - if (iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) - IWL_WARN(mvm, "Failed updating beacon data\n"); - } + if (changes & BSS_CHANGED_BEACON && + iwl_mvm_mac_ctxt_beacon_changed(mvm, vif)) + IWL_WARN(mvm, "Failed updating beacon data\n"); } static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, @@ -945,12 +1516,16 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); + if (changes & BSS_CHANGED_IDLE && !bss_conf->idle) + iwl_mvm_sched_scan_stop(mvm, true); + switch (vif->type) { case NL80211_IFTYPE_STATION: iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); break; case NL80211_IFTYPE_AP: - iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes); + case NL80211_IFTYPE_ADHOC: + iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); break; default: /* shouldn't happen */ @@ -972,13 +1547,30 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); - if (mvm->scan_status == IWL_MVM_SCAN_NONE) - ret = iwl_mvm_scan_request(mvm, vif, req); - else + switch (mvm->scan_status) { + case IWL_MVM_SCAN_SCHED: + ret = iwl_mvm_sched_scan_stop(mvm, true); + if (ret) { + ret = -EBUSY; + goto out; + } + break; + case IWL_MVM_SCAN_NONE: + break; + default: ret = -EBUSY; + goto out; + } - mutex_unlock(&mvm->mutex); + iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN); + ret = iwl_mvm_scan_request(mvm, vif, req); + if (ret) + 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; } @@ -996,20 +1588,32 @@ static void iwl_mvm_mac_cancel_hw_scan(struct ieee80211_hw *hw, static void iwl_mvm_mac_allow_buffered_frames(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, u16 tid, + struct ieee80211_sta *sta, u16 tids, int num_frames, enum ieee80211_frame_release_type reason, bool more_data) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - /* TODO: how do we tell the fw to send frames for a specific TID */ + /* Called when we need to transmit (a) frame(s) from mac80211 */ - /* - * The fw will send EOSP notification when the last frame will be - * transmitted. - */ - iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames); + iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, + tids, more_data, false); +} + +static void +iwl_mvm_mac_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u16 tids, + int num_frames, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + /* Called when we need to transmit (a) frame(s) from agg queue */ + + iwl_mvm_sta_modify_sleep_tx_count(mvm, sta, reason, num_frames, + tids, more_data, true); } static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, @@ -1018,12 +1622,26 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + int tid; switch (cmd) { case STA_NOTIFY_SLEEP: if (atomic_read(&mvm->pending_frames[mvmsta->sta_id]) > 0) ieee80211_sta_block_awake(hw, sta, true); + spin_lock_bh(&mvmsta->lock); + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct iwl_mvm_tid_data *tid_data; + + tid_data = &mvmsta->tid_data[tid]; + if (tid_data->state != IWL_AGG_ON && + tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA) + continue; + if (iwl_mvm_tid_queued(tid_data) == 0) + continue; + ieee80211_sta_set_buffered(sta, tid, true); + } + spin_unlock_bh(&mvmsta->lock); /* * The fw updates the STA to be asleep. Tx packets on the Tx * queues to this station will not be transmitted. The fw will @@ -1040,6 +1658,28 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, } } +static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + + /* + * This is called before mac80211 does RCU synchronisation, + * so here we already invalidate our internal RCU-protected + * station pointer. The rest of the code will thus no longer + * be able to find the station this way, and we don't rely + * on further RCU synchronisation after the sta_state() + * callback deleted the station. + */ + mutex_lock(&mvm->mutex); + if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) + rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], + ERR_PTR(-ENOENT)); + mutex_unlock(&mvm->mutex); +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1081,22 +1721,28 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ret = iwl_mvm_add_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_AUTH) { + /* + * EBS may be disabled due to previous failures reported by FW. + * Reset EBS status here assuming environment has been changed. + */ + mvm->last_ebs_successful = true; ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { ret = iwl_mvm_update_sta(mvm, vif, sta); if (ret == 0) iwl_mvm_rs_rate_init(mvm, sta, - mvmvif->phy_ctxt->channel->band); + mvmvif->phy_ctxt->channel->band, + true); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { /* enable beacon filtering */ - WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif)); + WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0)); ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { /* disable beacon filtering */ - WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif)); + WARN_ON(iwl_mvm_disable_beacon_filter(mvm, vif, 0)); ret = 0; } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH) { @@ -1125,6 +1771,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return 0; } +static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u32 changed) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + if (vif->type == NL80211_IFTYPE_STATION && + changed & IEEE80211_RC_NSS_CHANGED) + iwl_mvm_sf_update(mvm, vif, false); +} + static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params) @@ -1142,7 +1799,7 @@ static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, int ret; mutex_lock(&mvm->mutex); - ret = iwl_mvm_mac_ctxt_changed(mvm, vif); + ret = iwl_mvm_mac_ctxt_changed(mvm, vif, false); mutex_unlock(&mvm->mutex); return ret; } @@ -1163,8 +1820,83 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); /* Try really hard to protect the session and hear a beacon */ - iwl_mvm_protect_session(mvm, vif, duration, min_duration); + iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500); + mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + + if (!iwl_mvm_is_idle(mvm)) { + ret = -EBUSY; + goto out; + } + + 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; + } + + mvm->scan_status = IWL_MVM_SCAN_SCHED; + + ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); + if (ret) + goto err; + + ret = iwl_mvm_config_sched_scan_profiles(mvm, req); + if (ret) + goto err; + + ret = iwl_mvm_sched_scan_start(mvm, req); + if (!ret) + goto out; +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; +} + +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); + ret = iwl_mvm_sched_scan_stop(mvm, false); mutex_unlock(&mvm->mutex); + iwl_mvm_wait_for_async_handlers(mvm); + + return ret; } static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, @@ -1200,15 +1932,25 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, */ return 0; default: - return -EOPNOTSUPP; + /* currently FW supports only one optional cipher scheme */ + if (hw->n_cipher_schemes && + hw->cipher_schemes->cipher == key->cipher) + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + else + return -EOPNOTSUPP; } mutex_lock(&mvm->mutex); switch (cmd) { case SET_KEY: - if (vif->type == NL80211_IFTYPE_AP && !sta) { - /* GTK on AP interface is a TX-only key, return 0 */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP) && !sta) { + /* + * GTK on AP interface is a TX-only key, return 0; + * on IBSS they're per-station and because we're lazy + * we don't support them for RX, so do the same. + */ ret = 0; key->hw_key_idx = STA_KEY_IDX_INVALID; break; @@ -1252,6 +1994,9 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) + return; + iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key); } @@ -1398,7 +2143,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, goto out; } - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, ctx->rx_chains_static, ctx->rx_chains_dynamic); if (ret) { @@ -1436,13 +2181,15 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, if (WARN_ONCE((phy_ctxt->ref > 1) && (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH | IEEE80211_CHANCTX_CHANGE_RX_CHAINS | - IEEE80211_CHANCTX_CHANGE_RADAR)), + IEEE80211_CHANCTX_CHANGE_RADAR | + IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)), "Cannot change PHY. Ref=%d, changed=0x%X\n", phy_ctxt->ref, changed)) return; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + iwl_mvm_bt_coex_vif_change(mvm); + iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, ctx->rx_chains_static, ctx->rx_chains_dynamic); mutex_unlock(&mvm->mutex); @@ -1464,14 +2211,19 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_AP: + /* Unless it's a CSA flow we have nothing to do here */ + if (vif->csa_active) { + mvmvif->ap_ibss_active = true; + break; + } + case NL80211_IFTYPE_ADHOC: /* * The AP binding flow is handled as part of the start_ap flow - * (in bss_info_changed). + * (in bss_info_changed), similarly for IBSS. */ ret = 0; goto out_unlock; case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MONITOR: break; default: @@ -1484,7 +2236,12 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, goto out_unlock; /* - * Setting the quota at this stage is only required for monitor + * Power state must be updated before quotas, + * otherwise fw will complain. + */ + iwl_mvm_power_update_mac(mvm); + + /* Setting the quota at this stage is only required for monitor * interfaces. For the other types, the bss_info changed flow * will handle quota settings. */ @@ -1495,10 +2252,17 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, goto out_remove_binding; } + /* Handle binding during CSA */ + if (vif->type == NL80211_IFTYPE_AP) { + iwl_mvm_update_quotas(mvm, vif); + iwl_mvm_mac_ctxt_changed(mvm, vif, false); + } + goto out_unlock; out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); + iwl_mvm_power_update_mac(mvm); out_unlock: mutex_unlock(&mvm->mutex); if (ret) @@ -1517,21 +2281,30 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - if (vif->type == NL80211_IFTYPE_AP) - goto out_unlock; - switch (vif->type) { + case NL80211_IFTYPE_ADHOC: + goto out_unlock; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; iwl_mvm_update_quotas(mvm, NULL); break; + case NL80211_IFTYPE_AP: + /* This part is triggered only during CSA */ + if (!vif->csa_active || !mvmvif->ap_ibss_active) + goto out_unlock; + + mvmvif->ap_ibss_active = false; + iwl_mvm_update_quotas(mvm, NULL); + /*TODO: bt_coex notification here? */ default: break; } iwl_mvm_binding_remove_vif(mvm, vif); + out_unlock: mvmvif->phy_ctxt = NULL; + iwl_mvm_power_update_mac(mvm); mutex_unlock(&mvm->mutex); } @@ -1550,16 +2323,121 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw, return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); } -static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw, +#ifdef CONFIG_NL80211_TESTMODE +static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { + [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, + [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, + [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, +}; + +static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event) + void *data, int len) +{ + struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; + int err; + u32 noa_duration; + + err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); + if (err) + return err; + + if (!tb[IWL_MVM_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { + case IWL_MVM_TM_CMD_SET_NOA: + if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || + !vif->bss_conf.enable_beacon || + !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) + return -EINVAL; + + noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); + if (noa_duration >= vif->bss_conf.beacon_int) + return -EINVAL; + + mvm->noa_duration = noa_duration; + mvm->noa_vif = vif; + + return iwl_mvm_update_quotas(mvm, NULL); + case IWL_MVM_TM_CMD_SET_BEACON_FILTER: + /* must be associated client vif - ignore authorized */ + if (!vif || vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc || !vif->bss_conf.dtim_period || + !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) + return -EINVAL; + + if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) + return iwl_mvm_enable_beacon_filter(mvm, vif, 0); + return iwl_mvm_disable_beacon_filter(mvm, vif, 0); + } + + return -EOPNOTSUPP; +} + +static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int err; + + mutex_lock(&mvm->mutex); + err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); + mutex_unlock(&mvm->mutex); + + return err; +} +#endif + +static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active, + "Another CSA is already in progress")) + goto out_unlock; + + IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n", + chandef->center_freq1); + mvm->csa_vif = vif; + +out_unlock: + mutex_unlock(&mvm->mutex); +} + +static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 queues, bool drop) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_vif *mvmvif; + struct iwl_mvm_sta *mvmsta; + + if (!vif || vif->type != NL80211_IFTYPE_STATION) + return; + + mutex_lock(&mvm->mutex); + mvmvif = iwl_mvm_vif_from_mac80211(vif); + mvmsta = iwl_mvm_sta_from_staid_protected(mvm, mvmvif->ap_sta_id); - iwl_mvm_bt_rssi_event(mvm, vif, rssi_event); + if (WARN_ON_ONCE(!mvmsta)) + goto done; + + if (drop) { + if (iwl_mvm_flush_tx_path(mvm, mvmsta->tfd_queue_msk, true)) + IWL_ERR(mvm, "flush request fail\n"); + } else { + iwl_trans_wait_tx_queue_empty(mvm->trans, + mvmsta->tfd_queue_msk); + } +done: + mutex_unlock(&mvm->mutex); } -struct ieee80211_ops iwl_mvm_hw_ops = { +const struct ieee80211_ops iwl_mvm_hw_ops = { .tx = iwl_mvm_mac_tx, .ampdu_action = iwl_mvm_mac_ampdu_action, .start = iwl_mvm_mac_start, @@ -1568,33 +2446,44 @@ struct ieee80211_ops iwl_mvm_hw_ops = { .add_interface = iwl_mvm_mac_add_interface, .remove_interface = iwl_mvm_mac_remove_interface, .config = iwl_mvm_mac_config, + .prepare_multicast = iwl_mvm_prepare_multicast, .configure_filter = iwl_mvm_configure_filter, .bss_info_changed = iwl_mvm_bss_info_changed, .hw_scan = iwl_mvm_mac_hw_scan, .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, + .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, .sta_state = iwl_mvm_mac_sta_state, .sta_notify = iwl_mvm_mac_sta_notify, .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, + .release_buffered_frames = iwl_mvm_mac_release_buffered_frames, .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, + .sta_rc_update = iwl_mvm_sta_rc_update, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, + .flush = iwl_mvm_mac_flush, + .sched_scan_start = iwl_mvm_mac_sched_scan_start, + .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, .set_key = iwl_mvm_mac_set_key, .update_tkip_key = iwl_mvm_mac_update_tkip_key, .remain_on_channel = iwl_mvm_roc, .cancel_remain_on_channel = iwl_mvm_cancel_roc, - .rssi_callback = iwl_mvm_mac_rssi_callback, - .add_chanctx = iwl_mvm_add_chanctx, .remove_chanctx = iwl_mvm_remove_chanctx, .change_chanctx = iwl_mvm_change_chanctx, .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, - .start_ap = iwl_mvm_start_ap, - .stop_ap = iwl_mvm_stop_ap, + .start_ap = iwl_mvm_start_ap_ibss, + .stop_ap = iwl_mvm_stop_ap_ibss, + .join_ibss = iwl_mvm_start_ap_ibss, + .leave_ibss = iwl_mvm_stop_ap_ibss, .set_tim = iwl_mvm_set_tim, + .channel_switch_beacon = iwl_mvm_channel_switch_beacon, + + CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) + #ifdef CONFIG_PM_SLEEP /* look at d3.c */ .suspend = iwl_mvm_suspend, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index b0389279cc1..fcc6c29482d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,7 +73,6 @@ #include "iwl-trans.h" #include "iwl-notif-wait.h" #include "iwl-eeprom-parse.h" -#include "iwl-trans.h" #include "sta.h" #include "fw-api.h" #include "constants.h" @@ -82,6 +81,7 @@ #define IWL_MVM_MAX_ADDRESSES 5 /* RSSI offset for WkP */ #define IWL_RSSI_OFFSET 50 +#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8 enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_BK = 0, @@ -91,9 +91,7 @@ enum iwl_mvm_tx_fifo { IWL_MVM_TX_FIFO_MCAST = 5, }; -extern struct ieee80211_ops iwl_mvm_hw_ops; -extern const struct iwl_mvm_power_ops pm_legacy_ops; -extern const struct iwl_mvm_power_ops pm_mac_ops; +extern const struct ieee80211_ops iwl_mvm_hw_ops; /** * struct iwl_mvm_mod_params - module parameters for iwlmvm @@ -152,24 +150,13 @@ enum iwl_power_scheme { IWL_POWER_SCHEME_LP }; -#define IWL_CONN_MAX_LISTEN_INTERVAL 70 +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 #define IWL_UAPSD_AC_INFO (IEEE80211_WMM_IE_STA_QOSINFO_AC_VO |\ IEEE80211_WMM_IE_STA_QOSINFO_AC_VI |\ IEEE80211_WMM_IE_STA_QOSINFO_AC_BK |\ IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) #define IWL_UAPSD_MAX_SP IEEE80211_WMM_IE_STA_QOSINFO_SP_2 -struct iwl_mvm_power_ops { - int (*power_update_mode)(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); - int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -#ifdef CONFIG_IWLWIFI_DEBUGFS - int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - char *buf, int bufsz); -#endif -}; - - #ifdef CONFIG_IWLWIFI_DEBUGFS enum iwl_dbgfs_pm_mask { MVM_DEBUGFS_PM_KEEP_ALIVE = BIT(0), @@ -177,10 +164,10 @@ enum iwl_dbgfs_pm_mask { MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS = BIT(2), MVM_DEBUGFS_PM_RX_DATA_TIMEOUT = BIT(3), MVM_DEBUGFS_PM_TX_DATA_TIMEOUT = BIT(4), - MVM_DEBUGFS_PM_DISABLE_POWER_OFF = BIT(5), MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), + MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9), }; struct iwl_dbgfs_pm { @@ -189,10 +176,10 @@ struct iwl_dbgfs_pm { u32 tx_data_timeout; bool skip_over_dtim; u8 skip_dtim_periods; - bool disable_power_off; bool lprx_ena; u32 lprx_rssi_threshold; bool snooze_ena; + bool uapsd_misbehaving; int mask; }; @@ -234,6 +221,20 @@ enum iwl_mvm_smps_type_request { NUM_IWL_MVM_SMPS_REQ, }; +enum iwl_mvm_ref_type { + IWL_MVM_REF_UCODE_DOWN, + IWL_MVM_REF_SCAN, + IWL_MVM_REF_ROC, + 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_EXIT_WORK, + + IWL_MVM_REF_COUNT, +}; + /** * struct iwl_mvm_vif_bf_data - beacon filtering related data * @bf_enabled: indicates if beacon filtering is enabled @@ -241,12 +242,18 @@ enum iwl_mvm_smps_type_request { * @last_beacon_signal: last beacon rssi signal in dbm * @ave_beacon_signal: average beacon signal * @last_cqm_event: rssi of the last cqm event +* @bt_coex_min_thold: minimum threshold for BT coex +* @bt_coex_max_thold: maximum threshold for BT coex +* @last_bt_coex_event: rssi of the last BT coex event */ struct iwl_mvm_vif_bf_data { bool bf_enabled; bool ba_enabled; s8 ave_beacon_signal; s8 last_cqm_event; + s8 bt_coex_min_thold; + s8 bt_coex_max_thold; + s8 last_bt_coex_event; }; /** @@ -255,16 +262,19 @@ struct iwl_mvm_vif_bf_data { * @color: to solve races upon MAC addition and removal * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA * @uploaded: indicates the MAC context has been added to the device - * @ap_active: indicates that ap context is configured, and that the interface - * should get quota etc. + * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface + * should get quota etc. + * @pm_enabled - Indicate if MAC power management is allowed * @monitor_active: indicates that monitor context is configured, and that the - * interface should get quota etc. + * interface should get quota etc. + * @low_latency: indicates that this interface is in low-latency mode + * (VMACLowLatencyMode) * @queue_params: QoS params for this MAC * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. * @beacon_skb: the skb used to hold the AP/GO beacon template - * @smps_requests: the requests of of differents parts of the driver, regard - the desired smps mode. + * @smps_requests: the SMPS requests of differents parts of the driver, + * combined on update to yield the overall request to mac80211. */ struct iwl_mvm_vif { u16 id; @@ -272,8 +282,10 @@ struct iwl_mvm_vif { u8 ap_sta_id; bool uploaded; - bool ap_active; + bool ap_ibss_active; + bool pm_enabled; bool monitor_active; + bool low_latency; struct iwl_mvm_vif_bf_data bf_data; u32 ap_beacon_time; @@ -306,22 +318,29 @@ struct iwl_mvm_vif { int tx_key_idx; + 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; struct dentry *dbgfs_dir; struct dentry *dbgfs_slink; - void *dbgfs_data; struct iwl_dbgfs_pm dbgfs_pm; struct iwl_dbgfs_bf dbgfs_bf; + struct iwl_mac_power_cmd mac_pwr_cmd; #endif enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; + + /* FW identified misbehaving AP */ + u8 uapsd_misbehaving_bssid[ETH_ALEN]; }; static inline struct iwl_mvm_vif * @@ -330,9 +349,12 @@ 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, + IWL_MVM_SCAN_SCHED, }; /** @@ -395,6 +417,7 @@ struct iwl_tt_params { * @ct_kill_exit: worker to exit thermal kill * @dynamic_smps: Is thermal throttling enabled dynamic_smps? * @tx_backoff: The current thremal throttling tx backoff in uSec. + * @min_backoff: The minimal tx backoff due to power restrictions * @params: Parameters to configure the thermal throttling algorithm. * @throttle: Is thermal throttling is active? */ @@ -402,10 +425,38 @@ struct iwl_mvm_tt_mgmt { struct delayed_work ct_kill_exit; bool dynamic_smps; u32 tx_backoff; + u32 min_backoff; const struct iwl_tt_params *params; bool throttle; }; +#define IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES 8 + +struct iwl_mvm_frame_stats { + u32 legacy_frames; + u32 ht_frames; + u32 vht_frames; + u32 bw_20_frames; + u32 bw_40_frames; + u32 bw_80_frames; + u32 bw_160_frames; + u32 sgi_frames; + u32 ngi_frames; + u32 siso_frames; + u32 mimo2_frames; + u32 agg_frames; + u32 ampdu_count; + u32 success_frames; + u32 fail_frames; + u32 last_rates[IWL_MVM_NUM_LAST_FRAMES_UCODE_RATES]; + int last_frame_idx; +}; + +enum { + D0I3_DEFER_WAKEUP, + D0I3_PENDING_WAKEUP, +}; + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -434,9 +485,12 @@ struct iwl_mvm { enum iwl_ucode_type cur_ucode; bool ucode_loaded; - bool init_ucode_run; + bool init_ucode_complete; u32 error_event_table; u32 log_event_table; + u32 umac_error_event_table; + bool support_umac_log; + struct iwl_sf_region sf_space; u32 ampdu_ref; @@ -448,9 +502,10 @@ struct iwl_mvm { u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; atomic_t queue_stop_count[IWL_MAX_HW_QUEUES]; + const char *nvm_file_name; struct iwl_nvm_data *nvm_data; /* NVM sections */ - struct iwl_nvm_section nvm_sections[NVM_NUM_OF_SECTIONS]; + struct iwl_nvm_section nvm_sections[NVM_MAX_NUM_SECTIONS]; /* EEPROM MAC addresses */ struct mac_address addresses[IWL_MVM_MAX_ADDRESSES]; @@ -469,17 +524,47 @@ struct iwl_mvm { /* Scan status, cmd (pre-allocated) and auxiliary station */ enum iwl_scan_status scan_status; struct iwl_scan_cmd *scan_cmd; + struct iwl_mcast_filter_cmd *mcast_filter_cmd; + + /* rx chain antennas set through debugfs for the scan command */ + u8 scan_rx_ant; + +#ifdef CONFIG_IWLWIFI_BCAST_FILTERING + /* broadcast filters to configure for each associated station */ + const struct iwl_fw_bcast_filter *bcast_filters; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct { + u32 override; /* u32 for debugfs_create_bool */ + struct iwl_bcast_filter_cmd cmd; + } dbgfs_bcast_filtering; +#endif +#endif /* Internal station */ struct iwl_mvm_int_sta aux_sta; + bool last_ebs_successful; + u8 scan_last_antenna_idx; /* to toggle TX between antennas */ u8 mgmt_last_antenna_idx; + /* last smart fifo state that was successfully sent to firmware */ + enum iwl_sf_state sf_state; + #ifdef CONFIG_IWLWIFI_DEBUGFS struct dentry *debugfs_dir; u32 dbgfs_sram_offset, dbgfs_sram_len; - bool prevent_power_down_d3; + u32 dbgfs_prph_reg_addr; + bool disable_power_off; + bool disable_power_off_d3; + + struct debugfs_blob_wrapper nvm_hw_blob; + struct debugfs_blob_wrapper nvm_sw_blob; + struct debugfs_blob_wrapper nvm_calib_blob; + struct debugfs_blob_wrapper nvm_prod_blob; + + struct iwl_mvm_frame_stats drv_rx_stats; + spinlock_t drv_stats_lock; #endif struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; @@ -493,18 +578,22 @@ struct iwl_mvm { */ unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)]; - /* - * This counter of created interfaces is referenced only in conjunction - * with FW limitation related to power management. Currently PM is - * supported only on a single interface. - * IMPORTANT: this variable counts all interfaces except P2P device. - */ + /* A bitmap of reference types taken by the driver. */ + unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)]; + u8 vif_count; /* -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; + u32 *fw_error_rxf; + u32 fw_error_rxf_len; +#ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; +#endif struct ieee80211_vif *p2p_device_vif; @@ -517,18 +606,48 @@ struct iwl_mvm { bool store_d3_resume_sram; void *d3_resume_sram; u32 d3_test_pme_ptr; + struct ieee80211_vif *keep_vif; #endif #endif + /* d0i3 */ + u8 d0i3_ap_sta_id; + bool d0i3_offloading; + struct work_struct d0i3_exit_work; + struct sk_buff_head d0i3_tx; + /* protect d0i3_suspend_flags */ + struct mutex d0i3_suspend_mutex; + unsigned long d0i3_suspend_flags; + /* 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; s32 temperature; /* Celsius */ - const struct iwl_mvm_power_ops *pm_ops; +#ifdef CONFIG_NL80211_TESTMODE + u32 noa_duration; + struct ieee80211_vif *noa_vif; +#endif + + /* Tx queues */ + u8 aux_queue; + u8 first_agg_queue; + u8 last_agg_queue; + + /* Indicate if device power save is allowed */ + bool ps_disabled; + + struct ieee80211_vif *csa_vif; }; /* Extract MVM priv from op_mode and _hw */ @@ -543,6 +662,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) @@ -551,6 +671,30 @@ static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); } +static inline struct iwl_mvm_sta * +iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) +{ + struct ieee80211_sta *sta; + + if (sta_id >= ARRAY_SIZE(mvm->fw_id_to_mac_id)) + return NULL; + + sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return NULL; + + 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 { @@ -570,9 +714,16 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); /* Utils */ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + 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); +void iwl_mvm_fw_error_rxf_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); @@ -598,6 +749,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, @@ -607,12 +763,15 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd); /* NVM */ -int iwl_nvm_init(struct iwl_mvm *mvm); +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); +int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); int iwl_mvm_up(struct iwl_mvm *mvm); int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm); +bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, + struct iwl_bcast_filter_cmd *cmd); /* * FW notifications / CMD responses handlers @@ -628,6 +787,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, @@ -652,7 +814,8 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif); -int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_mac_ctxt_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool force_assoc_off); int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif); u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -664,6 +827,8 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -680,7 +845,24 @@ 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, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +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); +int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify); +int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); /* MVM debugfs */ #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -704,34 +886,36 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) #endif /* CONFIG_IWLWIFI_DEBUGFS */ /* rate scaling */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, - u8 flags, bool init); - -/* power managment */ -static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); +void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, + struct iwl_mvm_frame_stats *stats, + u32 rate, bool agg); +int rs_pretty_print_rate(char *buf, const u32 rate); + +/* power management */ +int iwl_mvm_power_update_device(struct iwl_mvm *mvm); +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm); +int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + char *buf, int bufsz); + +void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + +#ifdef CONFIG_IWLWIFI_LEDS +int iwl_mvm_leds_init(struct iwl_mvm *mvm); +void iwl_mvm_leds_exit(struct iwl_mvm *mvm); +#else +static inline int iwl_mvm_leds_init(struct iwl_mvm *mvm) { - return mvm->pm_ops->power_update_mode(mvm, vif); + return 0; } - -static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm) { - return mvm->pm_ops->power_disable(mvm, vif); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - char *buf, int bufsz) -{ - return mvm->pm_ops->power_dbgfs_read(mvm, vif, buf, bufsz); } #endif -int iwl_mvm_leds_init(struct iwl_mvm *mvm); -void iwl_mvm_leds_exit(struct iwl_mvm *mvm); - /* D3 (WoWLAN, NetDetect) */ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int iwl_mvm_resume(struct ieee80211_hw *hw); @@ -745,16 +929,53 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); extern const struct file_operations iwl_dbgfs_d3_test_ops; +#ifdef CONFIG_PM_SLEEP +void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +#else +static inline void +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); +int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); /* BT Coex */ -int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); int iwl_send_bt_init_conf(struct iwl_mvm *mvm); int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); 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_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); +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); +bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, + enum ieee80211_band band); +u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, + struct ieee80211_tx_info *info, u8 ac); + +enum iwl_bt_kill_msk { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_SCO_HID_A2DP, + BT_KILL_MSK_REDUCED_TXPOW, + BT_KILL_MSK_MAX, +}; +extern const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX]; +extern const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX]; /* beacon filtering */ #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -767,26 +988,55 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, struct iwl_beacon_filter_cmd *cmd) {} #endif +int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, u32 flags); int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); + struct ieee80211_vif *vif, + u32 flags); int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); -int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd); -int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, bool enable); -int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif); - + struct ieee80211_vif *vif, + u32 flags); /* SMPS */ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum iwl_mvm_smps_type_request req_type, enum ieee80211_smps_mode smps_request); +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm); + +/* 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) +{ + /* + * should this consider associated/active/... state? + * + * Normally low-latency should only be active on interfaces + * that are active, but at least with debugfs it can also be + * enabled on interfaces that aren't active. However, when + * interface aren't active then they aren't added into the + * binding, so this has no real impact. For now, just return + * the current desired low-latency state. + */ + + return mvmvif->low_latency; +} + +/* Assoc status */ +bool iwl_mvm_is_idle(struct iwl_mvm *mvm); /* Thermal management and CT-kill */ +void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); void iwl_mvm_tt_handler(struct iwl_mvm *mvm); -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm); +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff); void iwl_mvm_tt_exit(struct iwl_mvm *mvm); void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); +/* smart fifo */ +int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool added_vif); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index edb94ea3165..808f78f6fbf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -67,21 +67,19 @@ #include "iwl-eeprom-read.h" #include "iwl-nvm-parse.h" -/* list of NVM sections we are allowed/need to read */ -static const int nvm_to_read[] = { - NVM_SECTION_TYPE_HW, - NVM_SECTION_TYPE_SW, - NVM_SECTION_TYPE_CALIBRATION, - NVM_SECTION_TYPE_PRODUCTION, -}; - /* Default NVM size to read */ #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 6000 +#define IWL_MAX_NVM_SECTION_SIZE 7000 #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 +/* load nvm chunk response */ +enum { + READ_NVM_CHUNK_SUCCEED = 0, + READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 +}; + /* * prepare the NVM host command w/ the pointers to the nvm buffer * and send it to fw @@ -98,7 +96,7 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, .len = { sizeof(struct iwl_nvm_access_cmd), length }, - .flags = CMD_SYNC | CMD_SEND_IN_RFKILL, + .flags = CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, data }, /* data may come from vmalloc, so use _DUP */ .dataflags = { 0, IWL_HCMD_DFL_DUP }, @@ -120,7 +118,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = NVM_ACCESS_CMD, - .flags = CMD_SYNC | CMD_WANT_SKB | CMD_SEND_IN_RFKILL, + .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; int ret, bytes_read, offset_read; @@ -147,10 +145,26 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, offset_read = le16_to_cpu(nvm_resp->offset); resp_data = nvm_resp->data; if (ret) { - IWL_ERR(mvm, - "NVM access command failed with status %d (device: %s)\n", - ret, mvm->cfg->name); - ret = -EINVAL; + if ((offset != 0) && + (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { + /* + * meaning of NOT_VALID_ADDRESS: + * driver try to read chunk from address that is + * multiple of 2K and got an error since addr is empty. + * meaning of (offset != 0): driver already + * read valid data from another chunk so this case + * is not an error. + */ + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", + offset); + ret = 0; + } else { + IWL_DEBUG_EEPROM(mvm->trans->dev, + "NVM access command failed with status %d (device: %s)\n", + ret, mvm->cfg->name); + ret = -EIO; + } goto exit; } @@ -219,9 +233,9 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, while (ret == length) { ret = iwl_nvm_read_chunk(mvm, section, offset, length, data); if (ret < 0) { - IWL_ERR(mvm, - "Cannot read NVM from section %d offset %d, length %d\n", - section, offset, length); + IWL_DEBUG_EEPROM(mvm->trans->dev, + "Cannot read NVM from section %d offset %d, length %d\n", + section, offset, length); return ret; } offset += ret; @@ -236,29 +250,53 @@ static struct iwl_nvm_data * iwl_parse_nvm_sections(struct iwl_mvm *mvm) { struct iwl_nvm_section *sections = mvm->nvm_sections; - const __le16 *hw, *sw, *calib; + const __le16 *hw, *sw, *calib, *regulatory, *mac_override; /* Checking for required sections */ - if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[NVM_SECTION_TYPE_HW].data) { - IWL_ERR(mvm, "Can't parse empty NVM sections\n"); - return NULL; + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || + !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { + IWL_ERR(mvm, "Can't parse empty NVM sections\n"); + return NULL; + } + } else { + /* SW and REGULATORY sections are mandatory */ + if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || + !mvm->nvm_sections[NVM_SECTION_TYPE_REGULATORY].data) { + IWL_ERR(mvm, + "Can't parse empty family 8000 NVM sections\n"); + return NULL; + } + /* MAC_OVERRIDE or at least HW section must exist */ + if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && + !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { + IWL_ERR(mvm, + "Can't parse mac_address, empty sections\n"); + return NULL; + } } if (WARN_ON(!mvm->cfg)) return NULL; - hw = (const __le16 *)sections[NVM_SECTION_TYPE_HW].data; + hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; + regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; + mac_override = + (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; + return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, - iwl_fw_valid_tx_ant(mvm->fw), - iwl_fw_valid_rx_ant(mvm->fw)); + regulatory, mac_override, + mvm->fw->valid_tx_ant, + mvm->fw->valid_rx_ant); } #define MAX_NVM_FILE_LEN 16384 /* + * Reads external NVM from a file into mvm->nvm_sections + * * HOW TO CREATE THE NVM FILE FORMAT: * ------------------------------ * 1. create hex file, format: @@ -277,19 +315,24 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) * * 4. save as "iNVM_xxx.bin" under /lib/firmware */ -static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) +static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) { - int ret, section_id, section_size; + int ret, section_size; + u16 section_id; const struct firmware *fw_entry; const struct { __le16 word1; __le16 word2; u8 data[]; } *file_sec; - const u8 *eof; + const u8 *eof, *temp; #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) #define NVM_WORD2_ID(x) (x >> 12) +#define NVM_WORD2_LEN_FAMILY_8000(x) (2 * ((x & 0xFF) << 8 | x >> 8)) +#define NVM_WORD1_ID_FAMILY_8000(x) (x >> 4) + + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); /* * Obtain NVM image via request_firmware. Since we already used @@ -297,16 +340,16 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) * get here after that we assume the NVM request can be satisfied * synchronously. */ - ret = request_firmware(&fw_entry, iwlwifi_mod_params.nvm_file, + ret = request_firmware(&fw_entry, mvm->nvm_file_name, mvm->trans->dev); if (ret) { IWL_ERR(mvm, "ERROR: %s isn't available %d\n", - iwlwifi_mod_params.nvm_file, ret); + mvm->nvm_file_name, ret); return ret; } IWL_INFO(mvm, "Loaded NVM file %s (%zu bytes)\n", - iwlwifi_mod_params.nvm_file, fw_entry->size); + mvm->nvm_file_name, fw_entry->size); if (fw_entry->size < sizeof(*file_sec)) { IWL_ERR(mvm, "NVM file too small\n"); @@ -338,8 +381,16 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) break; } - section_size = 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); - section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + section_size = + 2 * NVM_WORD1_LEN(le16_to_cpu(file_sec->word1)); + section_id = NVM_WORD2_ID(le16_to_cpu(file_sec->word2)); + } else { + section_size = 2 * NVM_WORD2_LEN_FAMILY_8000( + le16_to_cpu(file_sec->word2)); + section_id = NVM_WORD1_ID_FAMILY_8000( + le16_to_cpu(file_sec->word1)); + } if (section_size > IWL_MAX_NVM_SECTION_SIZE) { IWL_ERR(mvm, "ERROR - section too large (%d)\n", @@ -362,12 +413,19 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) break; } - ret = iwl_nvm_write_section(mvm, section_id, file_sec->data, - section_size); - if (ret < 0) { - IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + if (WARN(section_id >= NVM_MAX_NUM_SECTIONS, + "Invalid NVM section ID %d\n", section_id)) { + ret = -EINVAL; + break; + } + + temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; break; } + mvm->nvm_sections[section_id].data = temp; + mvm->nvm_sections[section_id].length = section_size; /* advance to the next section */ file_sec = (void *)(file_sec->data + section_size); @@ -377,45 +435,92 @@ out: return ret; } -int iwl_nvm_init(struct iwl_mvm *mvm) +/* Loads the NVM data stored in mvm->nvm_sections into the NIC */ +int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) +{ + int i, ret = 0; + struct iwl_nvm_section *sections = mvm->nvm_sections; + + IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); + + for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) { + if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length) + continue; + ret = iwl_nvm_write_section(mvm, i, sections[i].data, + sections[i].length); + if (ret < 0) { + IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + break; + } + } + return ret; +} + +int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) { - int ret, i, section; + int ret, section; u8 *nvm_buffer, *temp; + if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) + return -EINVAL; + + /* load NVM values from nic */ + if (read_nvm_from_nic) { + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + + nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, + GFP_KERNEL); + if (!nvm_buffer) + return -ENOMEM; + for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) { + /* we override the constness for initial read */ + ret = iwl_nvm_read_section(mvm, section, nvm_buffer); + if (ret < 0) + continue; + temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } + mvm->nvm_sections[section].data = temp; + mvm->nvm_sections[section].length = ret; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + switch (section) { + case NVM_SECTION_TYPE_SW: + mvm->nvm_sw_blob.data = temp; + mvm->nvm_sw_blob.size = ret; + break; + case NVM_SECTION_TYPE_CALIBRATION: + mvm->nvm_calib_blob.data = temp; + mvm->nvm_calib_blob.size = ret; + break; + case NVM_SECTION_TYPE_PRODUCTION: + mvm->nvm_prod_blob.data = temp; + mvm->nvm_prod_blob.size = ret; + break; + default: + if (section == mvm->cfg->nvm_hw_section_num) { + mvm->nvm_hw_blob.data = temp; + mvm->nvm_hw_blob.size = ret; + break; + } + } +#endif + } + kfree(nvm_buffer); + } + /* load external NVM if configured */ - if (iwlwifi_mod_params.nvm_file) { + if (mvm->nvm_file_name) { /* move to External NVM flow */ - ret = iwl_mvm_load_external_nvm(mvm); + ret = iwl_mvm_read_external_nvm(mvm); if (ret) return ret; } - /* Read From FW NVM */ - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - - /* TODO: find correct NVM max size for a section */ - nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, - GFP_KERNEL); - if (!nvm_buffer) - return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { - section = nvm_to_read[i]; - /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer); - if (ret < 0) - break; - temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; - } - mvm->nvm_sections[section].data = temp; - mvm->nvm_sections[section].length = ret; - } - kfree(nvm_buffer); - if (ret < 0) - return ret; - + /* parse the relevant nvm sections */ mvm->nvm_data = iwl_parse_nvm_sections(mvm); if (!mvm->nvm_data) return -ENODATA; 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 2fcc8ef88a6..cc2f7de396d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -61,6 +61,7 @@ * *****************************************************************************/ #include <linux/module.h> +#include <linux/vmalloc.h> #include <net/mac80211.h> #include "iwl-notif-wait.h" @@ -79,6 +80,7 @@ #include "rs.h" #include "fw-api-scan.h" #include "time-event.h" +#include "iwl-fw-error-dump.h" /* * module name, copyright, version, etc. @@ -185,9 +187,10 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ - iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); } struct iwl_rx_handlers { @@ -217,13 +220,21 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(BA_NOTIF, iwl_mvm_rx_ba_notif, false), RX_HANDLER(BT_PROFILE_NOTIFICATION, iwl_mvm_rx_bt_coex_notif, true), - RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, false), + RX_HANDLER(BEACON_NOTIFICATION, iwl_mvm_rx_beacon_notif, true), 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, + false), RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), @@ -232,11 +243,13 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { false), RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), + RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, + iwl_mvm_power_uapsd_misbehaving_ap_notif, false), }; #undef RX_HANDLER #define CMD(x) [x] = #x -static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { +static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(MVM_ALIVE), CMD(REPLY_ERROR), CMD(INIT_COMPLETE_NOTIF), @@ -249,6 +262,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(TIME_EVENT_NOTIFICATION), CMD(BINDING_CONTEXT_CMD), CMD(TIME_QUOTA_CMD), + CMD(NON_QOS_TX_COUNTER_CMD), CMD(RADIO_VERSION_NOTIFICATION), CMD(SCAN_REQUEST_CMD), CMD(SCAN_ABORT_CMD), @@ -260,10 +274,12 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(CALIB_RES_NOTIF_PHY_DB), CMD(SET_CALIB_DEFAULT_CMD), CMD(CALIBRATION_COMPLETE_NOTIFICATION), + CMD(ADD_STA_KEY), CMD(ADD_STA), CMD(REMOVE_STA), CMD(LQ_CMD), CMD(SCAN_OFFLOAD_CONFIG_CMD), + CMD(MATCH_FOUND_NOTIFICATION), CMD(SCAN_OFFLOAD_REQUEST_CMD), CMD(SCAN_OFFLOAD_ABORT_CMD), CMD(SCAN_OFFLOAD_COMPLETE), @@ -275,9 +291,11 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BEACON_NOTIFICATION), CMD(BEACON_TEMPLATE_CMD), CMD(STATISTICS_NOTIFICATION), + CMD(EOSP_NOTIFICATION), CMD(REDUCE_TX_POWER_CMD), CMD(TX_ANT_CONFIGURATION_CMD), CMD(D3_CONFIG_CMD), + CMD(D0I3_END_CMD), CMD(PROT_OFFLOAD_CONFIG_CMD), CMD(OFFLOADS_QUERY_CMD), CMD(REMOTE_WAKE_CONFIG_CMD), @@ -300,14 +318,37 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(BT_PROFILE_NOTIFICATION), CMD(BT_CONFIG), CMD(MCAST_FILTER_CMD), + CMD(BCAST_FILTER_CMD), + CMD(REPLY_SF_CFG_CMD), CMD(REPLY_BEACON_FILTERING_CMD), CMD(REPLY_THERMAL_MNG_BACKOFF), CMD(MAC_PM_POWER_TABLE), + CMD(BT_COEX_CI), + CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), + CMD(ANTENNA_COUPLING_NOTIFICATION), }; #undef CMD /* this forward declaration can avoid to export the function */ static void iwl_mvm_async_handlers_wk(struct work_struct *wk); +static void iwl_mvm_d0i3_exit_work(struct work_struct *wk); + +static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) +{ + const struct iwl_pwr_tx_backoff *pwr_tx_backoff = cfg->pwr_tx_backoffs; + + if (!pwr_tx_backoff) + return 0; + + while (pwr_tx_backoff->pwr) { + if (trans->dflt_pwr_limit >= pwr_tx_backoff->pwr) + return pwr_tx_backoff->backoff; + + pwr_tx_backoff++; + } + + return 0; +} static struct iwl_op_mode * iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, @@ -321,6 +362,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, TX_CMD, }; int err, scan_size; + u32 min_backoff; + + /* + * We use IWL_MVM_STATION_COUNT to check the validity of the station + * index all over the driver - check that its value corresponds to the + * array size. + */ + BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); /******************************** * 1. Allocating and configuring HW data @@ -333,7 +382,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, op_mode = hw->priv; op_mode->ops = &iwl_mvm_ops; - op_mode->trans = trans; mvm = IWL_OP_MODE_GET_MVM(op_mode); mvm->dev = trans->dev; @@ -344,7 +392,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; + mvm->aux_queue = 15; + mvm->first_agg_queue = 16; + mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; + if (mvm->cfg->base_params->num_of_queues == 16) { + mvm->aux_queue = 11; + mvm->first_agg_queue = 12; + } + mvm->sf_state = SF_UNINIT; + mutex_init(&mvm->mutex); + mutex_init(&mvm->d0i3_suspend_mutex); spin_lock_init(&mvm->async_handlers_lock); INIT_LIST_HEAD(&mvm->time_event_list); INIT_LIST_HEAD(&mvm->async_handlers_list); @@ -353,6 +411,11 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); 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); @@ -401,24 +464,43 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, IWL_INFO(mvm, "Detected %s, REV=0x%X\n", mvm->cfg->name, mvm->trans->hw_rev); - err = iwl_trans_start_hw(mvm->trans); - if (err) - goto out_free; - - iwl_mvm_tt_initialize(mvm); + min_backoff = calc_min_backoff(trans, cfg); + iwl_mvm_tt_initialize(mvm, min_backoff); + /* set the nvm_file_name according to priority */ + if (iwlwifi_mod_params.nvm_file) + mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; + else + mvm->nvm_file_name = mvm->cfg->default_nvm_file; - mutex_lock(&mvm->mutex); - err = iwl_run_init_mvm_ucode(mvm, true); - mutex_unlock(&mvm->mutex); - /* returns 0 if successful, 1 if success but in rfkill */ - if (err < 0 && !iwlmvm_mod_params.init_dbg) { - IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); + if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, + "not allowing power-up and not having nvm_file\n")) goto out_free; - } - /* Stop the hw after the ALIVE and NVM has been read */ - if (!iwlmvm_mod_params.init_dbg) - iwl_trans_stop_hw(mvm->trans, false); + /* + * Even if nvm exists in the nvm_file driver should read agin the nvm + * from the nic because there might be entries that exist in the OTP + * and not in the file. + * for nics with no_power_up_nic_in_init: rely completley on nvm_file + */ + if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { + err = iwl_nvm_init(mvm, false); + if (err) + goto out_free; + } else { + err = iwl_trans_start_hw(mvm->trans); + if (err) + goto out_free; + + mutex_lock(&mvm->mutex); + err = iwl_run_init_mvm_ucode(mvm, true); + iwl_trans_stop_device(trans); + mutex_unlock(&mvm->mutex); + /* returns 0 if successful, 1 if success but in rfkill */ + if (err < 0 && !iwlmvm_mod_params.init_dbg) { + IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); + goto out_free; + } + } scan_size = sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + @@ -435,21 +517,21 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_unregister; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD) - mvm->pm_ops = &pm_mac_ops; - else - mvm->pm_ops = &pm_legacy_ops; - memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); + /* rpm starts with a taken ref. only set the appropriate bit here. */ + set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap); + return op_mode; out_unregister: ieee80211_unregister_hw(mvm->hw); + iwl_mvm_leds_exit(mvm); out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); - iwl_trans_stop_hw(trans, true); + if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) + iwl_trans_op_mode_leave(trans); ieee80211_free_hw(mvm->hw); return NULL; } @@ -466,18 +548,23 @@ 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->fw_error_rxf); + kfree(mvm->mcast_filter_cmd); + mvm->mcast_filter_cmd = NULL; #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) kfree(mvm->d3_resume_sram); #endif - iwl_trans_stop_hw(mvm->trans, true); + iwl_trans_op_mode_leave(mvm->trans); iwl_phy_db_free(mvm->phy_db); mvm->phy_db = NULL; iwl_free_nvm_data(mvm->nvm_data); - for (i = 0; i < NVM_NUM_OF_SECTIONS; i++) + for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) kfree(mvm->nvm_sections[i].data); ieee80211_free_hw(mvm->hw); @@ -627,7 +714,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); @@ -637,6 +724,8 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); 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) @@ -670,6 +759,29 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) iwl_abort_notification_waits(&mvm->notif_wait); /* + * This is a bit racy, but worst case we tell mac80211 about + * a stopped/aborted scan when that was already done which + * is not a problem. It is necessary to abort any os scan + * here because mac80211 requires having the scan cleared + * before restarting. + * We'll reset the scan_status to NONE in restart cleanup in + * the next start() call from mac80211. If restart isn't called + * (no fw restart) scan status will stay busy. + */ + switch (mvm->scan_status) { + case IWL_MVM_SCAN_NONE: + break; + case IWL_MVM_SCAN_OS: + ieee80211_scan_completed(mvm->hw, true); + break; + case IWL_MVM_SCAN_SCHED: + /* Sched scan will be restarted by mac80211 in restart_hw. */ + if (!mvm->restart_fw) + ieee80211_sched_scan_stopped(mvm->hw); + break; + } + + /* * If we're restarting already, don't cycle restarts. * If INIT fw asserted, it will likely fail again. * If WoWLAN fw asserted, don't restart either, mac80211 @@ -700,22 +812,8 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); schedule_work(&reprobe->work); } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) { - /* - * This is a bit racy, but worst case we tell mac80211 about - * a stopped/aborted (sched) scan when that was already done - * which is not a problem. It is necessary to abort any scan - * here because mac80211 requires having the scan cleared - * before restarting. - * We'll reset the scan_status to NONE in restart cleanup in - * the next start() call from mac80211. - */ - switch (mvm->scan_status) { - case IWL_MVM_SCAN_NONE: - break; - case IWL_MVM_SCAN_OS: - ieee80211_scan_completed(mvm->hw, true); - break; - } + /* don't let the transport/FW power down */ + iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); if (mvm->restart_fw > 0) mvm->restart_fw--; @@ -723,13 +821,81 @@ 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; + u32 trans_len; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->fw_error_dump) + return; + + file_len = mvm->fw_error_sram_len + + mvm->fw_error_rxf_len + + sizeof(*dump_file) + + sizeof(*dump_data) * 2; + + trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); + if (trans_len) + file_len += trans_len; + + 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 = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len); + memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len); + + dump_data = iwl_mvm_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(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); + + kfree(mvm->fw_error_rxf); + mvm->fw_error_rxf = NULL; + mvm->fw_error_rxf_len = 0; + + kfree(mvm->fw_error_sram); + mvm->fw_error_sram = NULL; + mvm->fw_error_sram_len = 0; + + if (trans_len) { + void *buf = iwl_mvm_fw_error_next_data(dump_data); + u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, + trans_len); + dump_data = (void *)((u8 *)buf + real_trans_len); + dump_file->file_len = + cpu_to_le32(file_len - trans_len + real_trans_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); + iwl_mvm_fw_error_rxf_dump(mvm); +#endif iwl_mvm_nic_restart(mvm); } @@ -742,6 +908,351 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode) iwl_mvm_nic_restart(mvm); } +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) +{ + struct iwl_d0i3_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; + + IWL_DEBUG_RPM(mvm, "entering D0i3 - vif %pM\n", vif->addr); + if (vif->type != NL80211_IFTYPE_STATION || + !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 + * and REPLY_MCAST_FILTER_CMD, so currently don't + * reconfigure them (we might want to use different + * params later on, though). + */ + data->ap_sta_id = mvmvif->ap_sta_id; + 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); + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; + int ret; + struct iwl_d0i3_iter_data d0i3_iter_data = { + .mvm = mvm, + }; + 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), + }; + + 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); + if (ret) + return ret; + + return iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, + flags | CMD_MAKE_TRANS_IDLE, + sizeof(d3_cfg_cmd), &d3_cfg_cmd); +} + +static void iwl_mvm_exit_d0i3_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = _data; + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO; + + IWL_DEBUG_RPM(mvm, "exiting D0i3 - vif %pM\n", vif->addr); + if (vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc) + return; + + iwl_mvm_update_d0i3_power_mode(mvm, vif, false, flags); +} + +static void iwl_mvm_d0i3_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc && + mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) + 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); + struct iwl_host_cmd get_status_cmd = { + .id = WOWLAN_GET_STATUSES, + .flags = CMD_HIGH_PRIO | CMD_WANT_SKB, + }; + struct iwl_wowlan_status *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); + if (ret) + goto out; + + if (!get_status_cmd.resp_pkt) + goto out; + + 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); + + disconnection_reasons = + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH; + if (wakeup_reasons & disconnection_reasons) + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d0i3_disconnect_iter, mvm); + + iwl_free_resp(&get_status_cmd); +out: + iwl_mvm_d0i3_enable_tx(mvm, qos_seq); + iwl_mvm_unref(mvm, IWL_MVM_REF_EXIT_WORK); + mutex_unlock(&mvm->mutex); +} + +int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm) +{ + u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | + CMD_WAKE_UP_TRANS; + int ret; + + IWL_DEBUG_RPM(mvm, "MVM exiting D0i3\n"); + + mutex_lock(&mvm->d0i3_suspend_mutex); + if (test_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags)) { + IWL_DEBUG_RPM(mvm, "Deferring d0i3 exit until resume\n"); + __set_bit(D0I3_PENDING_WAKEUP, &mvm->d0i3_suspend_flags); + mutex_unlock(&mvm->d0i3_suspend_mutex); + return 0; + } + mutex_unlock(&mvm->d0i3_suspend_mutex); + + ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); + if (ret) + goto out; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_exit_d0i3_iterator, + mvm); +out: + schedule_work(&mvm->d0i3_exit_work); + return ret; +} + +static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + iwl_mvm_ref(mvm, IWL_MVM_REF_EXIT_WORK); + return _iwl_mvm_exit_d0i3(mvm); +} + +static void iwl_mvm_napi_add(struct iwl_op_mode *op_mode, + struct napi_struct *napi, + struct net_device *napi_dev, + int (*poll)(struct napi_struct *, int), + int weight) +{ + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + ieee80211_napi_add(mvm->hw, napi, napi_dev, poll, weight); +} + static const struct iwl_op_mode_ops iwl_mvm_ops = { .start = iwl_op_mode_mvm_start, .stop = iwl_op_mode_mvm_stop, @@ -753,4 +1264,7 @@ static const struct iwl_op_mode_ops iwl_mvm_ops = { .nic_error = iwl_mvm_nic_error, .cmd_queue_full = iwl_mvm_cmd_queue_full, .nic_config = iwl_mvm_nic_config, + .enter_d0i3 = iwl_mvm_enter_d0i3, + .exit_d0i3 = iwl_mvm_exit_d0i3, + .napi_add = iwl_mvm_napi_add, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index a8652ddd6be..539f3a942d4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -156,13 +156,25 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, idle_cnt = chains_static; active_cnt = chains_dynamic; - cmd->rxchain_info = cpu_to_le32(iwl_fw_valid_rx_ant(mvm->fw) << + /* In scenarios where we only ever use a single-stream rates, + * i.e. legacy 11b/g/a associations, single-stream APs or even + * static SMPS, enable both chains to get diversity, improving + * the case where we're far enough from the AP that attenuation + * between the two antennas is sufficiently different to impact + * performance. + */ + if (active_cnt == 1 && iwl_mvm_rx_diversity_allowed(mvm)) { + idle_cnt = 2; + active_cnt = 2; + } + + cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant << PHY_RX_CHAIN_VALID_POS); cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); cmd->rxchain_info |= cpu_to_le32(active_cnt << PHY_RX_CHAIN_MIMO_CNT_POS); - cmd->txchain_info = cpu_to_le32(iwl_fw_valid_tx_ant(mvm->fw)); + cmd->txchain_info = cpu_to_le32(mvm->fw->valid_tx_ant); } /* @@ -187,7 +199,7 @@ static int iwl_mvm_phy_ctxt_apply(struct iwl_mvm *mvm, iwl_mvm_phy_ctxt_cmd_data(mvm, &cmd, chandef, chains_static, chains_dynamic); - ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, PHY_CONTEXT_CMD, 0, sizeof(struct iwl_phy_context_cmd), &cmd); if (ret) @@ -202,18 +214,15 @@ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, struct cfg80211_chan_def *chandef, u8 chains_static, u8 chains_dynamic) { - int ret; - WARN_ON(!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && ctxt->ref); lockdep_assert_held(&mvm->mutex); ctxt->channel = chandef->chan; - ret = iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, - chains_static, chains_dynamic, - FW_CTXT_ACTION_ADD, 0); - return ret; + return iwl_mvm_phy_ctxt_apply(mvm, ctxt, chandef, + chains_static, chains_dynamic, + FW_CTXT_ACTION_ADD, 0); } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index d58e393324e..c182a8baf68 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,7 +64,6 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> -#include <linux/init.h> #include <net/mac80211.h> @@ -75,39 +74,36 @@ #define POWER_KEEP_ALIVE_PERIOD_SEC 25 +static int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, - struct iwl_beacon_filter_cmd *cmd) + struct iwl_beacon_filter_cmd *cmd, + u32 flags) { - int ret; - - ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC, - sizeof(struct iwl_beacon_filter_cmd), cmd); - - if (!ret) { - IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", - le32_to_cpu(cmd->ba_enable_beacon_abort)); - IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", - le32_to_cpu(cmd->ba_escape_timer)); - IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", - le32_to_cpu(cmd->bf_debug_flag)); - IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", - le32_to_cpu(cmd->bf_enable_beacon_filter)); - IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", - le32_to_cpu(cmd->bf_energy_delta)); - IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", - le32_to_cpu(cmd->bf_escape_timer)); - IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", - le32_to_cpu(cmd->bf_roaming_energy_delta)); - IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", - le32_to_cpu(cmd->bf_roaming_state)); - IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", - le32_to_cpu(cmd->bf_temp_threshold)); - IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", - le32_to_cpu(cmd->bf_temp_fast_filter)); - IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", - le32_to_cpu(cmd->bf_temp_slow_filter)); - } - return ret; + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", + le32_to_cpu(cmd->ba_enable_beacon_abort)); + IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", + le32_to_cpu(cmd->ba_escape_timer)); + IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n", + le32_to_cpu(cmd->bf_debug_flag)); + IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n", + le32_to_cpu(cmd->bf_enable_beacon_filter)); + IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n", + le32_to_cpu(cmd->bf_energy_delta)); + IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n", + le32_to_cpu(cmd->bf_escape_timer)); + IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n", + le32_to_cpu(cmd->bf_roaming_energy_delta)); + IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n", + le32_to_cpu(cmd->bf_roaming_state)); + IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n", + le32_to_cpu(cmd->bf_temp_threshold)); + IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n", + le32_to_cpu(cmd->bf_temp_fast_filter)); + IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", + le32_to_cpu(cmd->bf_temp_slow_filter)); + + return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, + sizeof(struct iwl_beacon_filter_cmd), cmd); } static @@ -127,28 +123,6 @@ void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm, cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled); } -int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, bool enable) -{ - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - .ba_enable_beacon_abort = cpu_to_le32(enable), - }; - - if (!mvmvif->bf_data.bf_enabled) - return 0; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN) - cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); - - mvmvif->bf_data.ba_enabled = enable; - iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); -} - static void iwl_mvm_power_log(struct iwl_mvm *mvm, struct iwl_mac_power_cmd *cmd) { @@ -186,6 +160,143 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, } } +static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + enum ieee80211_ac_numbers ac; + bool tid_found = false; + + for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { + if (!mvmvif->queue_params[ac].uapsd) + continue; + + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !mvmvif->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } + + if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); + + if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | + BIT(IEEE80211_AC_VI) | + BIT(IEEE80211_AC_BE) | + BIT(IEEE80211_AC_BK))) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); + cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? + cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : + cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); + } + + cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); + } + + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; + } else { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; + } + cmd->heavy_tx_thld_percentage = + IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; + cmd->heavy_rx_thld_percentage = + IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; +} + +static void iwl_mvm_binding_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned long *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!mvmvif->phy_ctxt) + return; + + if (vif->type == NL80211_IFTYPE_STATION || + vif->type == NL80211_IFTYPE_AP) + __set_bit(mvmvif->phy_ctxt->id, data); +} + +static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + unsigned long phy_ctxt_counter = 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_binding_iterator, + &phy_ctxt_counter); + + if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, + ETH_ALEN)) + return false; + + if (vif->p2p && + !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) + return false; + /* + * Avoid using uAPSD if P2P client is associated to GO that uses + * opportunistic power save. This is due to current FW limitation. + */ + if (vif->p2p && + (vif->bss_conf.p2p_noa_attr.oppps_ctwindow & + IEEE80211_P2P_OPPPS_ENABLE_BIT)) + return false; + + /* + * Avoid using uAPSD if client is in DCM - + * low latency issue in Miracast + */ + if (hweight8(phy_ctxt_counter) >= 2) + return false; + + return true; +} + static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_power_cmd *cmd) @@ -198,8 +309,6 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, bool radar_detect = false; struct iwl_mvm_vif *mvmvif __maybe_unused = iwl_mvm_vif_from_mac80211(vif); - enum ieee80211_ac_numbers ac; - bool tid_found = false; cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); @@ -217,17 +326,13 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); cmd->keep_alive_seconds = cpu_to_le16(keep_alive); - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + if (mvm->ps_disabled) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && - mvmvif->dbgfs_pm.disable_power_off) - cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); -#endif - if (!vif->bss_conf.ps) + if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) || + !mvmvif->pm_enabled) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); @@ -269,65 +374,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); } - for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { - if (!mvmvif->queue_params[ac].uapsd) - continue; - - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); - - cmd->uapsd_ac_flags |= BIT(ac); - - /* QNDP TID - the highest TID with no admission control */ - if (!tid_found && !mvmvif->queue_params[ac].acm) { - tid_found = true; - switch (ac) { - case IEEE80211_AC_VO: - cmd->qndp_tid = 6; - break; - case IEEE80211_AC_VI: - cmd->qndp_tid = 5; - break; - case IEEE80211_AC_BE: - cmd->qndp_tid = 0; - break; - case IEEE80211_AC_BK: - cmd->qndp_tid = 1; - break; - } - } - } - - if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); - - if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | - BIT(IEEE80211_AC_VI) | - BIT(IEEE80211_AC_BE) | - BIT(IEEE80211_AC_BK))) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); - cmd->snooze_interval = - cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); - cmd->snooze_window = - (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? - cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : - cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); - } - - cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; - cmd->heavy_tx_thld_percentage = - IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; - cmd->heavy_rx_thld_percentage = - IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; - } + if (iwl_mvm_power_allow_uapsd(mvm, vif)) + iwl_mvm_power_configure_uapsd(mvm, vif, cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) @@ -365,85 +413,248 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, cmd->flags &= cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) { + u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK; + if (mvmvif->dbgfs_pm.uapsd_misbehaving) + cmd->flags |= cpu_to_le16(flag); + else + cmd->flags &= cpu_to_le16(flag); + } #endif /* CONFIG_IWLWIFI_DEBUGFS */ } -static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm, +static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { - int ret; - bool ba_enable; struct iwl_mac_power_cmd cmd = {}; - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + iwl_mvm_power_log(mvm, &cmd); +#ifdef CONFIG_IWLWIFI_DEBUGFS + memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd)); +#endif - /* - * TODO: The following vif_count verification is temporary condition. - * Avoid power mode update if more than one interface is currently - * active. Remove this condition when FW will support power management - * on multiple MACs. + return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0, + sizeof(cmd), &cmd); +} + +int iwl_mvm_power_update_device(struct iwl_mvm *mvm) +{ + struct iwl_device_power_cmd cmd = { + .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), + }; + + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + mvm->ps_disabled = true; + + if (mvm->ps_disabled) + cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : + mvm->disable_power_off) + cmd.flags &= + cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif + IWL_DEBUG_POWER(mvm, + "Sending device power command with flags = 0x%X\n", + cmd.flags); + + return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd), + &cmd); +} + +void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, + ETH_ALEN)) + memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN); +} + +static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u8 *ap_sta_id = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* The ap_sta_id is not expected to change during current association + * so no explicit protection is needed */ - IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count > 1) - return 0; + if (mvmvif->ap_sta_id == *ap_sta_id) + memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, + ETH_ALEN); +} - iwl_mvm_power_build_cmd(mvm, vif, &cmd); - iwl_mvm_power_log(mvm, &cmd); +int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; + u8 ap_sta_id = le32_to_cpu(notif->sta_id); - ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC, - sizeof(cmd), &cmd); - if (ret) - return ret; + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); + + return 0; +} - ba_enable = !!(cmd.flags & - cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); +struct iwl_power_vifs { + struct ieee80211_vif *bf_vif; + struct ieee80211_vif *bss_vif; + struct ieee80211_vif *p2p_vif; + struct ieee80211_vif *ap_vif; + struct ieee80211_vif *monitor_vif; + bool p2p_active; + bool bss_active; + bool ap_active; + bool monitor_active; +}; - return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); +static void iwl_mvm_power_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_power_vifs *power_iterator = _data; + + mvmvif->pm_enabled = false; + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_P2P_DEVICE: + break; + + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_AP: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->ap_vif); + power_iterator->ap_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->ap_active = true; + break; + + case NL80211_IFTYPE_MONITOR: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->monitor_vif); + power_iterator->monitor_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->monitor_active = true; + break; + + case NL80211_IFTYPE_P2P_CLIENT: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->p2p_vif); + power_iterator->p2p_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->p2p_active = true; + break; + + case NL80211_IFTYPE_STATION: + /* only a single MAC of the same type */ + WARN_ON(power_iterator->bss_vif); + power_iterator->bss_vif = vif; + if (mvmvif->phy_ctxt) + if (mvmvif->phy_ctxt->id < MAX_PHYS) + power_iterator->bss_active = true; + + if (mvmvif->bf_data.bf_enabled && + !WARN_ON(power_iterator->bf_vif)) + power_iterator->bf_vif = vif; + + break; + + default: + break; + } } -static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +static void +iwl_mvm_power_set_pm(struct iwl_mvm *mvm, + struct iwl_power_vifs *vifs) { - struct iwl_mac_power_cmd cmd = {}; - struct iwl_mvm_vif *mvmvif __maybe_unused = - iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif *bss_mvmvif = NULL; + struct iwl_mvm_vif *p2p_mvmvif = NULL; + struct iwl_mvm_vif *ap_mvmvif = NULL; + bool client_same_channel = false; + bool ap_same_channel = false; - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; + lockdep_assert_held(&mvm->mutex); - cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, - mvmvif->color)); + /* get vifs info + set pm_enable to false */ + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_iterator, vifs); - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) - cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + if (vifs->bss_vif) + bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif); -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && - mvmvif->dbgfs_pm.disable_power_off) - cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); -#endif - iwl_mvm_power_log(mvm, &cmd); + if (vifs->p2p_vif) + p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif); - return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC, - sizeof(cmd), &cmd); + if (vifs->ap_vif) + ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif); + + /* enable PM on bss if bss stand alone */ + if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) { + bss_mvmvif->pm_enabled = true; + return; + } + + /* enable PM on p2p if p2p stand alone */ + if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) { + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) + p2p_mvmvif->pm_enabled = true; + return; + } + + if (vifs->bss_active && vifs->p2p_active) + client_same_channel = (bss_mvmvif->phy_ctxt->id == + p2p_mvmvif->phy_ctxt->id); + if (vifs->bss_active && vifs->ap_active) + ap_same_channel = (bss_mvmvif->phy_ctxt->id == + ap_mvmvif->phy_ctxt->id); + + /* clients are not stand alone: enable PM if DCM */ + if (!(client_same_channel || ap_same_channel) && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) { + if (vifs->bss_active) + bss_mvmvif->pm_enabled = true; + if (vifs->p2p_active && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)) + p2p_mvmvif->pm_enabled = true; + return; + } + + /* + * There is only one channel in the system and there are only + * bss and p2p clients that share it + */ + if (client_same_channel && !vifs->ap_active && + (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) { + /* share same channel*/ + bss_mvmvif->pm_enabled = true; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM) + p2p_mvmvif->pm_enabled = true; + } } #ifdef CONFIG_IWLWIFI_DEBUGFS -static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, char *buf, - int bufsz) +int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, char *buf, + int bufsz) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_power_cmd cmd = {}; int pos = 0; - iwl_mvm_power_build_cmd(mvm, vif, &cmd); + mutex_lock(&mvm->mutex); + memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd)); + mutex_unlock(&mvm->mutex); - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? - 0 : 1); pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", iwlmvm_mod_params.power_scheme); pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", @@ -451,70 +662,58 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", le16_to_cpu(cmd.keep_alive_seconds)); - if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { - pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? - 1 : 0); - pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", - cmd.skip_dtim_periods); - if (!(cmd.flags & - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { - pos += scnprintf(buf+pos, bufsz-pos, - "rx_data_timeout = %d\n", - le32_to_cpu(cmd.rx_data_timeout)); - pos += scnprintf(buf+pos, bufsz-pos, - "tx_data_timeout = %d\n", - le32_to_cpu(cmd.tx_data_timeout)); - } - if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - pos += scnprintf(buf+pos, bufsz-pos, - "lprx_rssi_threshold = %d\n", - cmd.lprx_rssi_threshold); - if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - pos += - scnprintf(buf+pos, bufsz-pos, - "rx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.rx_data_timeout_uapsd)); - pos += - scnprintf(buf+pos, bufsz-pos, - "tx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.tx_data_timeout_uapsd)); - pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", - cmd.qndp_tid); - pos += scnprintf(buf+pos, bufsz-pos, - "uapsd_ac_flags = 0x%x\n", - cmd.uapsd_ac_flags); - pos += scnprintf(buf+pos, bufsz-pos, - "uapsd_max_sp = %d\n", - cmd.uapsd_max_sp); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_tx_thld_packets = %d\n", - cmd.heavy_tx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_rx_thld_packets = %d\n", - cmd.heavy_rx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_tx_thld_percentage = %d\n", - cmd.heavy_tx_thld_percentage); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_rx_thld_percentage = %d\n", - cmd.heavy_rx_thld_percentage); - pos += - scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ? - 1 : 0); - } - if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - pos += scnprintf(buf+pos, bufsz-pos, - "snooze_interval = %d\n", - cmd.snooze_interval); - pos += scnprintf(buf+pos, bufsz-pos, - "snooze_window = %d\n", - cmd.snooze_window); - } + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + cmd.skip_dtim_periods); + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); } + if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + pos += scnprintf(buf+pos, bufsz-pos, + "lprx_rssi_threshold = %d\n", + cmd.lprx_rssi_threshold); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.rx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.tx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n", + cmd.uapsd_ac_flags); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n", + cmd.uapsd_max_sp); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n", + cmd.heavy_tx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n", + cmd.heavy_rx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n", + cmd.heavy_tx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n", + cmd.heavy_rx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ? + 1 : 0); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n", + cmd.snooze_interval); + pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n", + cmd.snooze_window); + return pos; } @@ -553,42 +752,75 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif, } #endif -int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_beacon_filter_cmd *cmd, + u32 cmd_flags, + bool d0i3) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_beacon_filter_cmd cmd = { - IWL_BF_CMD_CONFIG_DEFAULTS, - .bf_enable_beacon_filter = cpu_to_le32(1), - }; int ret; - if (mvmvif != mvm->bf_allowed_vif || + if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period || vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; - iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd); - iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); - ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd); + if (!d0i3) + iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags); - if (!ret) + /* don't change bf_enabled in case of temporary d0i3 configuration */ + if (!ret && !d0i3) mvmvif->bf_data.bf_enabled = true; return ret; } +int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 flags) +{ + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false); +} + +static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + + if (!mvmvif->bf_data.bf_enabled) + return 0; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); + + mvmvif->bf_data.ba_enabled = enable; + return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false); +} + int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif, + u32 flags) { struct iwl_beacon_filter_cmd cmd = {}; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int ret; - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) || - vif->type != NL80211_IFTYPE_STATION || vif->p2p) + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) return 0; - ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd); + ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags); if (!ret) mvmvif->bf_data.bf_enabled = false; @@ -596,21 +828,109 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm, return ret; } -int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +int iwl_mvm_power_update_mac(struct iwl_mvm *mvm) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_vif *mvmvif; + struct iwl_power_vifs vifs = {}; + bool ba_enable; + int ret; - if (!mvmvif->bf_data.bf_enabled) + lockdep_assert_held(&mvm->mutex); + + iwl_mvm_power_set_pm(mvm, &vifs); + + /* disable PS if CAM */ + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) { + mvm->ps_disabled = true; + } else { + /* don't update device power state unless we add / remove monitor */ + if (vifs.monitor_vif) { + if (vifs.monitor_active) + mvm->ps_disabled = true; + ret = iwl_mvm_power_update_device(mvm); + if (ret) + return ret; + } + } + + if (vifs.bss_vif) { + ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif); + if (ret) + return ret; + } + + if (vifs.p2p_vif) { + ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif); + if (ret) + return ret; + } + + if (!vifs.bf_vif) return 0; - return iwl_mvm_enable_beacon_filter(mvm, vif); + mvmvif = iwl_mvm_vif_from_mac80211(vifs.bf_vif); + + ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled || + !vifs.bf_vif->bss_conf.ps || + iwl_mvm_vif_low_latency(mvmvif)); + + return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable); } -const struct iwl_mvm_power_ops pm_mac_ops = { - .power_update_mode = iwl_mvm_power_mac_update_mode, - .power_disable = iwl_mvm_power_mac_disable, +int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, u32 flags) +{ + int ret; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_power_cmd cmd = {}; + + if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + return 0; + + if (!vif->bss_conf.assoc) + return 0; + + iwl_mvm_power_build_cmd(mvm, vif, &cmd); + if (enable) { + /* configure skip over dtim up to 300 msec */ + int dtimper = mvm->hw->conf.ps_dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + if (WARN_ON(!dtimper_msec)) + return 0; + + cmd.skip_dtim_periods = 300 / dtimper_msec; + if (cmd.skip_dtim_periods) + cmd.flags |= + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); + } + iwl_mvm_power_log(mvm, &cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS - .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read, + memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd)); #endif -}; + ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags, + sizeof(cmd), &cmd); + if (ret) + return ret; + + /* configure beacon filtering */ + if (mvmvif != mvm->bf_allowed_vif) + return 0; + + if (enable) { + struct iwl_beacon_filter_cmd cmd_bf = { + IWL_BF_CMD_CONFIG_D0I3, + .bf_enable_beacon_filter = cpu_to_le32(1), + }; + ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf, + flags, true); + } else { + if (mvmvif->bf_data.bf_enabled) + ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags); + else + ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags); + } + + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c b/drivers/net/wireless/iwlwifi/mvm/power_legacy.c deleted file mode 100644 index 2ce79bad584..00000000000 --- a/drivers/net/wireless/iwlwifi/mvm/power_legacy.c +++ /dev/null @@ -1,319 +0,0 @@ -/****************************************************************************** - * - * 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 - 2013 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 - 2013 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 <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/init.h> - -#include <net/mac80211.h> - -#include "iwl-debug.h" -#include "mvm.h" -#include "iwl-modparams.h" -#include "fw-api-power.h" - -#define POWER_KEEP_ALIVE_PERIOD_SEC 25 - -static void iwl_mvm_power_log(struct iwl_mvm *mvm, - struct iwl_powertable_cmd *cmd) -{ - IWL_DEBUG_POWER(mvm, - "Sending power table command for power level %d, flags = 0x%X\n", - iwlmvm_mod_params.power_scheme, - le16_to_cpu(cmd->flags)); - IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n", cmd->keep_alive_seconds); - - if (cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { - IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n", - le32_to_cpu(cmd->rx_data_timeout)); - IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n", - le32_to_cpu(cmd->tx_data_timeout)); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) - IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n", - le32_to_cpu(cmd->skip_dtim_periods)); - if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n", - le32_to_cpu(cmd->lprx_rssi_threshold)); - } -} - -static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct iwl_powertable_cmd *cmd) -{ - struct ieee80211_hw *hw = mvm->hw; - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *chan; - int dtimper, dtimper_msec; - int keep_alive; - bool radar_detect = false; - struct iwl_mvm_vif *mvmvif __maybe_unused = - iwl_mvm_vif_from_mac80211(vif); - - /* - * Regardless of power management state the driver must set - * keep alive period. FW will use it for sending keep alive NDPs - * immediately after association. - */ - cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC; - - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) - return; - - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); - if (!vif->bss_conf.assoc) - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && - mvmvif->dbgfs_pm.disable_power_off) - cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); -#endif - if (!vif->bss_conf.ps) - return; - - cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); - - if (vif->bss_conf.beacon_rate && - (vif->bss_conf.beacon_rate->bitrate == 10 || - vif->bss_conf.beacon_rate->bitrate == 60)) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - cmd->lprx_rssi_threshold = - cpu_to_le32(POWER_LPRX_RSSI_THRESHOLD); - } - - dtimper = hw->conf.ps_dtim_period ?: 1; - - /* Check if radar detection is required on current channel */ - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - WARN_ON(!chanctx_conf); - if (chanctx_conf) { - chan = chanctx_conf->def.chan; - radar_detect = chan->flags & IEEE80211_CHAN_RADAR; - } - rcu_read_unlock(); - - /* Check skip over DTIM conditions */ - if (!radar_detect && (dtimper <= 10) && - (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_LP || - mvm->cur_ucode == IWL_UCODE_WOWLAN)) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - cmd->skip_dtim_periods = cpu_to_le32(3); - } - - /* Check that keep alive period is at least 3 * DTIM */ - dtimper_msec = dtimper * vif->bss_conf.beacon_int; - keep_alive = max_t(int, 3 * dtimper_msec, - MSEC_PER_SEC * cmd->keep_alive_seconds); - keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); - cmd->keep_alive_seconds = keep_alive; - - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) { - cmd->rx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(100 * USEC_PER_MSEC); - } else { - cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); - cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC); - } - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) - cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds; - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) { - if (mvmvif->dbgfs_pm.skip_over_dtim) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); - else - cmd->flags &= - cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT) - cmd->rx_data_timeout = - cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT) - cmd->tx_data_timeout = - cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS) - cmd->skip_dtim_periods = - cpu_to_le32(mvmvif->dbgfs_pm.skip_dtim_periods); - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) { - if (mvmvif->dbgfs_pm.lprx_ena) - cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); - else - cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK); - } - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD) - cmd->lprx_rssi_threshold = - cpu_to_le32(mvmvif->dbgfs_pm.lprx_rssi_threshold); -#endif /* CONFIG_IWLWIFI_DEBUGFS */ -} - -static int iwl_mvm_power_legacy_update_mode(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - int ret; - bool ba_enable; - struct iwl_powertable_cmd cmd = {}; - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - /* - * TODO: The following vif_count verification is temporary condition. - * Avoid power mode update if more than one interface is currently - * active. Remove this condition when FW will support power management - * on multiple MACs. - */ - IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count > 1) - return 0; - - iwl_mvm_power_build_cmd(mvm, vif, &cmd); - iwl_mvm_power_log(mvm, &cmd); - - ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, - sizeof(cmd), &cmd); - if (ret) - return ret; - - ba_enable = !!(cmd.flags & - cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); - - return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable); -} - -static int iwl_mvm_power_legacy_disable(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_powertable_cmd cmd = {}; - struct iwl_mvm_vif *mvmvif __maybe_unused = - iwl_mvm_vif_from_mac80211(vif); - - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) - return 0; - - if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) - cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF && - mvmvif->dbgfs_pm.disable_power_off) - cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); -#endif - iwl_mvm_power_log(mvm, &cmd); - - return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_ASYNC, - sizeof(cmd), &cmd); -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -static int iwl_mvm_power_legacy_dbgfs_read(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, char *buf, - int bufsz) -{ - struct iwl_powertable_cmd cmd = {}; - int pos = 0; - - iwl_mvm_power_build_cmd(mvm, vif, &cmd); - - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? - 0 : 1); - pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", - le32_to_cpu(cmd.skip_dtim_periods)); - pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", - iwlmvm_mod_params.power_scheme); - pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", - le16_to_cpu(cmd.flags)); - pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", - cmd.keep_alive_seconds); - - if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { - pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? - 1 : 0); - pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", - le32_to_cpu(cmd.rx_data_timeout)); - pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", - le32_to_cpu(cmd.tx_data_timeout)); - if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - pos += scnprintf(buf+pos, bufsz-pos, - "lprx_rssi_threshold = %d\n", - le32_to_cpu(cmd.lprx_rssi_threshold)); - } - return pos; -} -#endif - -const struct iwl_mvm_power_ops pm_legacy_ops = { - .power_update_mode = iwl_mvm_power_legacy_update_mode, - .power_disable = iwl_mvm_power_legacy_disable, -#ifdef CONFIG_IWLWIFI_DEBUGFS - .power_dbgfs_read = iwl_mvm_power_legacy_dbgfs_read, -#endif -}; diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 5c6ae16ec52..ba68d7b8450 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -65,9 +65,14 @@ #include "fw-api.h" #include "mvm.h" +#define QUOTA_100 IWL_MVM_MAX_QUOTA +#define QUOTA_LOWLAT_MIN ((QUOTA_100 * IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT) / 100) + struct iwl_mvm_quota_iterator_data { int n_interfaces[MAX_BINDINGS]; int colors[MAX_BINDINGS]; + int low_latency[MAX_BINDINGS]; + int n_low_latency_bindings; struct ieee80211_vif *new_vif; }; @@ -107,32 +112,69 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, switch (vif->type) { case NL80211_IFTYPE_STATION: if (vif->bss_conf.assoc) - data->n_interfaces[id]++; - break; + break; + return; case NL80211_IFTYPE_AP: - if (mvmvif->ap_active) - data->n_interfaces[id]++; - break; + case NL80211_IFTYPE_ADHOC: + if (mvmvif->ap_ibss_active) + break; + return; case NL80211_IFTYPE_MONITOR: if (mvmvif->monitor_active) - data->n_interfaces[id]++; - break; + break; + return; case NL80211_IFTYPE_P2P_DEVICE: - break; - case NL80211_IFTYPE_ADHOC: - if (vif->bss_conf.ibss_joined) - data->n_interfaces[id]++; - break; + return; default: WARN_ON_ONCE(1); - break; + return; + } + + data->n_interfaces[id]++; + + if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) { + data->n_low_latency_bindings++; + data->low_latency[id] = true; + } +} + +static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, + struct iwl_time_quota_cmd *cmd) +{ +#ifdef CONFIG_NL80211_TESTMODE + struct iwl_mvm_vif *mvmvif; + int i, phy_id = -1, beacon_int = 0; + + if (!mvm->noa_duration || !mvm->noa_vif) + return; + + mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); + if (!mvmvif->ap_ibss_active) + return; + + phy_id = mvmvif->phy_ctxt->id; + beacon_int = mvm->noa_vif->bss_conf.beacon_int; + + for (i = 0; i < MAX_BINDINGS; i++) { + u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color); + u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; + u32 quota = le32_to_cpu(cmd->quotas[i].quota); + + if (id != phy_id) + continue; + + quota *= (beacon_int - mvm->noa_duration); + quota /= beacon_int; + + cmd->quotas[i].quota = cpu_to_le32(quota); } +#endif } int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) { struct iwl_time_quota_cmd cmd = {}; - int i, idx, ret, num_active_macs, quota, quota_rem; + int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat; struct iwl_mvm_quota_iterator_data data = { .n_interfaces = {}, .colors = { -1, -1, -1, -1 }, @@ -167,11 +209,39 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) num_active_macs += data.n_interfaces[i]; } - quota = 0; - quota_rem = 0; - if (num_active_macs) { - quota = IWL_MVM_MAX_QUOTA / num_active_macs; - quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs; + n_non_lowlat = num_active_macs; + + if (data.n_low_latency_bindings == 1) { + for (i = 0; i < MAX_BINDINGS; i++) { + if (data.low_latency[i]) { + n_non_lowlat -= data.n_interfaces[i]; + break; + } + } + } + + if (data.n_low_latency_bindings == 1 && n_non_lowlat) { + /* + * Reserve quota for the low latency binding in case that + * there are several data bindings but only a single + * low latency one. Split the rest of the quota equally + * between the other data interfaces. + */ + quota = (QUOTA_100 - QUOTA_LOWLAT_MIN) / n_non_lowlat; + quota_rem = QUOTA_100 - n_non_lowlat * quota - + QUOTA_LOWLAT_MIN; + } else if (num_active_macs) { + /* + * There are 0 or more than 1 low latency bindings, or all the + * data interfaces belong to the single low latency binding. + * Split the quota equally between the data interfaces. + */ + quota = QUOTA_100 / num_active_macs; + quota_rem = QUOTA_100 % num_active_macs; + } else { + /* values don't really matter - won't be used */ + quota = 0; + quota_rem = 0; } for (idx = 0, i = 0; i < MAX_BINDINGS; i++) { @@ -181,22 +251,41 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) cmd.quotas[idx].id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(i, data.colors[i])); - if (data.n_interfaces[i] <= 0) { + if (data.n_interfaces[i] <= 0) cmd.quotas[idx].quota = cpu_to_le32(0); - cmd.quotas[idx].max_duration = cpu_to_le32(0); - } else { + else if (data.n_low_latency_bindings == 1 && n_non_lowlat && + data.low_latency[i]) + /* + * There is more than one binding, but only one of the + * bindings is in low latency. For this case, allocate + * the minimal required quota for the low latency + * binding. + */ + cmd.quotas[idx].quota = cpu_to_le32(QUOTA_LOWLAT_MIN); + else cmd.quotas[idx].quota = cpu_to_le32(quota * data.n_interfaces[i]); - cmd.quotas[idx].max_duration = - cpu_to_le32(IWL_MVM_MAX_QUOTA); - } + + WARN_ONCE(le32_to_cpu(cmd.quotas[idx].quota) > QUOTA_100, + "Binding=%d, quota=%u > max=%u\n", + idx, le32_to_cpu(cmd.quotas[idx].quota), QUOTA_100); + + cmd.quotas[idx].max_duration = cpu_to_le32(0); + idx++; } - /* Give the remainder of the session to the first binding */ - le32_add_cpu(&cmd.quotas[0].quota, quota_rem); + /* Give the remainder of the session to the first data binding */ + for (i = 0; i < MAX_BINDINGS; i++) { + if (le32_to_cpu(cmd.quotas[i].quota) != 0) { + le32_add_cpu(&cmd.quotas[i].quota, quota_rem); + break; + } + } + + iwl_mvm_adjust_quota_for_noa(mvm, &cmd); - ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send quota: %d\n", ret); diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 4ffaa3fa153..306a6caa486 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 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 @@ -24,7 +24,6 @@ * *****************************************************************************/ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <net/mac80211.h> @@ -42,33 +41,37 @@ #define RS_NAME "iwl-mvm-rs" -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_NUMBER_TRY 1 -#define IWL_HT_NUMBER_TRY 3 +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define RS_LEGACY_RETRIES_PER_RATE 1 +#define RS_HT_VHT_RETRIES_PER_RATE 2 +#define RS_HT_VHT_RETRIES_PER_RATE_TW 1 +#define RS_INITIAL_MIMO_NUM_RATES 3 +#define RS_INITIAL_SISO_NUM_RATES 3 +#define RS_INITIAL_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM +#define RS_SECONDARY_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM +#define RS_SECONDARY_SISO_NUM_RATES 3 +#define RS_SECONDARY_SISO_RETRIES 1 #define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_FAILURE_TH 3 /* min failures to calc tpt */ #define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ /* max allowed rate miss before sync LQ cmd */ #define IWL_MISSED_RATE_MAX 15 -/* max time to accum history 2 seconds */ -#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) +#define RS_STAY_IN_COLUMN_TIMEOUT (5*HZ) +#define RS_IDLE_TIMEOUT (5*HZ) static u8 rs_ht_to_legacy[] = { - [IWL_RATE_1M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_2M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_5M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_11M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_6M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_9M_INDEX] = IWL_RATE_6M_INDEX, - [IWL_RATE_12M_INDEX] = IWL_RATE_9M_INDEX, - [IWL_RATE_18M_INDEX] = IWL_RATE_12M_INDEX, - [IWL_RATE_24M_INDEX] = IWL_RATE_18M_INDEX, - [IWL_RATE_36M_INDEX] = IWL_RATE_24M_INDEX, - [IWL_RATE_48M_INDEX] = IWL_RATE_36M_INDEX, - [IWL_RATE_54M_INDEX] = IWL_RATE_48M_INDEX, - [IWL_RATE_60M_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, + [IWL_RATE_MCS_1_INDEX] = IWL_RATE_9M_INDEX, + [IWL_RATE_MCS_2_INDEX] = IWL_RATE_12M_INDEX, + [IWL_RATE_MCS_3_INDEX] = IWL_RATE_18M_INDEX, + [IWL_RATE_MCS_4_INDEX] = IWL_RATE_24M_INDEX, + [IWL_RATE_MCS_5_INDEX] = IWL_RATE_36M_INDEX, + [IWL_RATE_MCS_6_INDEX] = IWL_RATE_48M_INDEX, + [IWL_RATE_MCS_7_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_8_INDEX] = IWL_RATE_54M_INDEX, + [IWL_RATE_MCS_9_INDEX] = IWL_RATE_54M_INDEX, }; static const u8 ant_toggle_lookup[] = { @@ -82,13 +85,24 @@ static const u8 ant_toggle_lookup[] = { [ANT_ABC] = ANT_ABC, }; -#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ - [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ - IWL_RATE_SISO_##s##M_PLCP, \ - IWL_RATE_MIMO2_##s##M_PLCP,\ - IWL_RATE_##rp##M_INDEX, \ +#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ + [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ + IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\ + IWL_RATE_##rp##M_INDEX, \ IWL_RATE_##rn##M_INDEX } +#define IWL_DECLARE_MCS_RATE(s) \ + [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \ + IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_INVM_INDEX, \ + IWL_RATE_INVM_INDEX } + /* * Parameter order: * rate, ht rate, prev rate, next rate @@ -102,16 +116,223 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 6, 5, 11), /* 6mbps */ - IWL_DECLARE_RATE_INFO(9, 6, 6, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 12, 11, 18), /* 12mbps */ - IWL_DECLARE_RATE_INFO(18, 18, 12, 24), /* 18mbps */ - IWL_DECLARE_RATE_INFO(24, 24, 18, 36), /* 24mbps */ - IWL_DECLARE_RATE_INFO(36, 36, 24, 48), /* 36mbps */ - IWL_DECLARE_RATE_INFO(48, 48, 36, 54), /* 48mbps */ - IWL_DECLARE_RATE_INFO(54, 54, 48, INV), /* 54mbps */ - IWL_DECLARE_RATE_INFO(60, 60, 48, INV), /* 60mbps */ - /* FIXME:RS: ^^ should be INV (legacy) */ + IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */ + IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */ + IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */ + IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */ + IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */ + IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */ + IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */ + IWL_DECLARE_MCS_RATE(7), /* MCS 7 */ + IWL_DECLARE_MCS_RATE(8), /* MCS 8 */ + IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ +}; + +enum rs_action { + RS_ACTION_STAY = 0, + RS_ACTION_DOWNSCALE = -1, + RS_ACTION_UPSCALE = 1, +}; + +enum rs_column_mode { + RS_INVALID = 0, + RS_LEGACY, + RS_SISO, + RS_MIMO2, +}; + +#define MAX_NEXT_COLUMNS 7 +#define MAX_COLUMN_CHECKS 3 + +typedef bool (*allow_column_func_t) (struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl); + +struct rs_tx_column { + enum rs_column_mode mode; + u8 ant; + bool sgi; + enum rs_column next_columns[MAX_NEXT_COLUMNS]; + allow_column_func_t checks[MAX_COLUMN_CHECKS]; +}; + +static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + if (!sta->ht_cap.ht_supported) + return false; + + if (sta->smps_mode == IEEE80211_SMPS_STATIC) + return false; + + if (num_of_ant(mvm->fw->valid_tx_ant) < 2) + return false; + + if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + return false; + + return true; +} + +static bool rs_siso_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + if (!sta->ht_cap.ht_supported) + return false; + + return true; +} + +static bool rs_sgi_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + struct rs_rate *rate = &tbl->rate; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + + if (is_ht20(rate) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + return true; + if (is_ht40(rate) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + return true; + if (is_ht80(rate) && (vht_cap->cap & + IEEE80211_VHT_CAP_SHORT_GI_80)) + return true; + + return false; +} + +static const struct rs_tx_column rs_tx_columns[] = { + [RS_COLUMN_LEGACY_ANT_A] = { + .mode = RS_LEGACY, + .ant = ANT_A, + .next_columns = { + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + }, + [RS_COLUMN_LEGACY_ANT_B] = { + .mode = RS_LEGACY, + .ant = ANT_B, + .next_columns = { + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + }, + [RS_COLUMN_SISO_ANT_A] = { + .mode = RS_SISO, + .ant = ANT_A, + .next_columns = { + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_MIMO2, + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + }, + }, + [RS_COLUMN_SISO_ANT_B] = { + .mode = RS_SISO, + .ant = ANT_B, + .next_columns = { + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2, + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + }, + }, + [RS_COLUMN_SISO_ANT_A_SGI] = { + .mode = RS_SISO, + .ant = ANT_A, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_B_SGI, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_sgi_allow, + }, + }, + [RS_COLUMN_SISO_ANT_B_SGI] = { + .mode = RS_SISO, + .ant = ANT_B, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_SISO_ANT_B, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_siso_allow, + rs_sgi_allow, + }, + }, + [RS_COLUMN_MIMO2] = { + .mode = RS_MIMO2, + .ant = ANT_AB, + .next_columns = { + RS_COLUMN_SISO_ANT_A, + RS_COLUMN_MIMO2_SGI, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_mimo_allow, + }, + }, + [RS_COLUMN_MIMO2_SGI] = { + .mode = RS_MIMO2, + .ant = ANT_AB, + .sgi = true, + .next_columns = { + RS_COLUMN_SISO_ANT_A_SGI, + RS_COLUMN_MIMO2, + RS_COLUMN_LEGACY_ANT_A, + RS_COLUMN_LEGACY_ANT_B, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + RS_COLUMN_INVALID, + }, + .checks = { + rs_mimo_allow, + rs_sgi_allow, + }, + }, }; static inline u8 rs_extract_rate(u32 rate_n_flags) @@ -124,50 +345,46 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) { int idx = 0; - /* HT rate format */ if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = rs_extract_rate(rate_n_flags); - - WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP); - if (idx >= IWL_RATE_MIMO2_6M_PLCP) - idx = idx - IWL_RATE_MIMO2_6M_PLCP; + idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK; + idx += IWL_RATE_MCS_0_INDEX; - idx += IWL_FIRST_OFDM_RATE; - /* skip 9M not supported in ht*/ + /* skip 9M not supported in HT*/ if (idx >= IWL_RATE_9M_INDEX) idx += 1; - if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) + if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE)) return idx; + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; + idx += IWL_RATE_MCS_0_INDEX; - /* legacy rate format, search for match in table */ + /* skip 9M not supported in VHT*/ + if (idx >= IWL_RATE_9M_INDEX) + idx++; + if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE)) + return idx; } else { + /* legacy rate format, search for match in table */ + + u8 legacy_rate = rs_extract_rate(rate_n_flags); for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) - if (iwl_rates[idx].plcp == - rs_extract_rate(rate_n_flags)) + if (iwl_rates[idx].plcp == legacy_rate) return idx; } - return -1; + return IWL_RATE_INVALID; } static void rs_rate_scale_perform(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta); -static void rs_fill_link_cmd(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, u32 rate_n_flags); +static void rs_fill_lq_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate); static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); - -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags); -#else -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags) -{} -#endif - /** * The following tables contain the expected throughput metrics for all rates * @@ -179,36 +396,53 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, * (2.4 GHz) band. */ -static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 +static const u16 expected_tpt_legacy[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0 +}; + +/* Expected TpT tables. 4 indexes: + * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI + */ +static const u16 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0}, + {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0}, + {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0}, + {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0}, }; -static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ - {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ - {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ - {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ +static const u16 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275}, + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280}, + {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173}, + {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284}, }; -static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ - {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ - {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ +static const u16 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308}, + {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312}, + {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466}, + {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, }; -static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ - {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ - {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ - {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ +static const u16 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, + {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, + {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0}, + {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0}, }; -static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ - {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ - {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ +static const u16 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300}, + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303}, + {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053}, + {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221}, +}; + +static const u16 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319}, + {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320}, + {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219}, + {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, }; /* mbps, mcs */ @@ -230,6 +464,52 @@ static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { #define MCS_INDEX_PER_STREAM (8) +static const char *rs_pretty_ant(u8 ant) +{ + static const char * const ant_name[] = { + [ANT_NONE] = "None", + [ANT_A] = "A", + [ANT_B] = "B", + [ANT_AB] = "AB", + [ANT_C] = "C", + [ANT_AC] = "AC", + [ANT_BC] = "BC", + [ANT_ABC] = "ABC", + }; + + if (ant > ANT_ABC) + return "UNKNOWN"; + + return ant_name[ant]; +} + +static const char *rs_pretty_lq_type(enum iwl_table_type type) +{ + static const char * const lq_types[] = { + [LQ_NONE] = "NONE", + [LQ_LEGACY_A] = "LEGACY_A", + [LQ_LEGACY_G] = "LEGACY_G", + [LQ_HT_SISO] = "HT SISO", + [LQ_HT_MIMO2] = "HT MIMO", + [LQ_VHT_SISO] = "VHT SISO", + [LQ_VHT_MIMO2] = "VHT MIMO", + }; + + if (type < LQ_NONE || type >= LQ_MAX) + return "UNKNOWN"; + + return lq_types[type]; +} + +static inline void rs_dump_rate(struct iwl_mvm *mvm, const struct rs_rate *rate, + const char *prefix) +{ + IWL_DEBUG_RATE(mvm, "%s: (%s: %d) ANT: %s BW: %d SGI: %d\n", + prefix, rs_pretty_lq_type(rate->type), + rate->index, rs_pretty_ant(rate->ant), + rate->bw, rate->sgi); +} + static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) { window->data = 0; @@ -237,37 +517,25 @@ static void rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) window->success_ratio = IWL_INVALID_VALUE; window->counter = 0; window->average_tpt = IWL_INVALID_VALUE; - window->stamp = 0; } -static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +static void rs_rate_scale_clear_tbl_windows(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl) { - return (ant_type & valid_antenna) == ant_type; -} + int i; -#ifdef CONFIG_MAC80211_DEBUGFS -/** - * Program the device to use fixed rate for frame transmit - * This is for debugging/testing only - * once the device start use fixed rate, we need to reload the module - * to being back the normal operation. - */ -static void rs_program_fix_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta) -{ - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + 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]); - IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + for (i = 0; i < ARRAY_SIZE(tbl->tpc_win); i++) + rs_rate_scale_clear_window(&tbl->tpc_win[i]); +} - if (lq_sta->dbg_fixed_rate) { - rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); - } +static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return (ant_type & valid_antenna) == ant_type; } -#endif static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_data, u8 tid, @@ -275,17 +543,6 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, { int ret = -EAGAIN; - /* - * Don't create TX aggregation sessions when in high - * BT traffic, as they would just be disrupted by BT. - */ - if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) { - IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n", - BT_MBOX_MSG(&mvm->last_bt_notif, - 3, TRAFFIC_LOAD)); - return ret; - } - IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); ret = ieee80211_start_tx_ba_session(sta, tid, 5000); @@ -338,19 +595,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); @@ -405,187 +656,204 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, else window->average_tpt = IWL_INVALID_VALUE; - /* Tag this window as having been updated */ - window->stamp = jiffies; - return 0; } -/* - * Fill uCode API rate_n_flags field, based on "search" or "active" table. - */ -/* FIXME:RS:remove this function and put the flags statically in the table */ -static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, - int index, u8 use_green) +static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes, + u8 reduced_txp) +{ + struct iwl_rate_scale_data *window = NULL; + int ret; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + if (tbl->column != RS_COLUMN_INVALID) { + lq_sta->tx_stats[tbl->column][scale_index].total += attempts; + lq_sta->tx_stats[tbl->column][scale_index].success += successes; + } + + /* Select window for current tx bit rate */ + window = &(tbl->win[scale_index]); + + ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes, + window); + if (ret) + return ret; + + if (WARN_ON_ONCE(reduced_txp > TPC_MAX_REDUCTION)) + return -EINVAL; + + window = &tbl->tpc_win[reduced_txp]; + 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) { - u32 rate_n_flags = 0; + u32 ucode_rate = 0; + int index = rate->index; + + ucode_rate |= ((rate->ant << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); - if (is_legacy(tbl->lq_type)) { - rate_n_flags = iwl_rates[index].plcp; + if (is_legacy(rate)) { + ucode_rate |= iwl_rates[index].plcp; if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) - rate_n_flags |= RATE_MCS_CCK_MSK; - } else if (is_Ht(tbl->lq_type)) { - if (index > IWL_LAST_OFDM_RATE) { + ucode_rate |= RATE_MCS_CCK_MSK; + return ucode_rate; + } + + if (is_ht(rate)) { + if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { IWL_ERR(mvm, "Invalid HT rate index %d\n", index); - index = IWL_LAST_OFDM_RATE; + index = IWL_LAST_HT_RATE; } - rate_n_flags = RATE_MCS_HT_MSK; + ucode_rate |= RATE_MCS_HT_MSK; - if (is_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_siso; - else if (is_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_mimo2; + if (is_ht_siso(rate)) + ucode_rate |= iwl_rates[index].plcp_ht_siso; + else if (is_ht_mimo2(rate)) + ucode_rate |= iwl_rates[index].plcp_ht_mimo2; + else + WARN_ON_ONCE(1); + } else if (is_vht(rate)) { + if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { + IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); + index = IWL_LAST_VHT_RATE; + } + ucode_rate |= RATE_MCS_VHT_MSK; + if (is_vht_siso(rate)) + ucode_rate |= iwl_rates[index].plcp_vht_siso; + else if (is_vht_mimo2(rate)) + ucode_rate |= iwl_rates[index].plcp_vht_mimo2; else WARN_ON_ONCE(1); + } else { - IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type); + IWL_ERR(mvm, "Invalid rate->type %d\n", rate->type); } - rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_Ht(tbl->lq_type)) { - if (tbl->is_ht40) - rate_n_flags |= RATE_MCS_CHAN_WIDTH_40; - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; + ucode_rate |= rate->bw; + if (rate->sgi) + ucode_rate |= RATE_MCS_SGI_MSK; - if (use_green) { - rate_n_flags |= RATE_HT_MCS_GF_MSK; - if (is_siso(tbl->lq_type) && tbl->is_SGI) { - rate_n_flags &= ~RATE_MCS_SGI_MSK; - IWL_ERR(mvm, "GF was set with SGI:SISO\n"); - } - } - } - return rate_n_flags; + return ucode_rate; } -/* - * Interpret uCode API's rate_n_flags format, - * fill "search" or "active" tx mode table. - */ -static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, - enum ieee80211_band band, - struct iwl_scale_tbl_info *tbl, - int *rate_idx) +/* Convert a ucode rate into an rs_rate object */ +static int rs_rate_from_ucode_rate(const u32 ucode_rate, + enum ieee80211_band band, + struct rs_rate *rate) { - u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); - u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); - u8 mcs; + u32 ant_msk = ucode_rate & RATE_MCS_ANT_ABC_MSK; + u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate); + u8 nss; - memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win)); - *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); + memset(rate, 0, sizeof(*rate)); + rate->index = iwl_hwrate_to_plcp_idx(ucode_rate); - if (*rate_idx == IWL_RATE_INVALID) { - *rate_idx = -1; + if (rate->index == IWL_RATE_INVALID) return -EINVAL; - } - tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_ht40 = 0; - tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); - tbl->lq_type = LQ_NONE; - tbl->max_search = IWL_MAX_SEARCH; - /* legacy rate format */ - if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + rate->ant = (ant_msk >> RATE_MCS_ANT_POS); + + /* Legacy */ + if (!(ucode_rate & RATE_MCS_HT_MSK) && + !(ucode_rate & RATE_MCS_VHT_MSK)) { if (num_of_ant == 1) { if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; + rate->type = LQ_LEGACY_A; else - tbl->lq_type = LQ_G; + rate->type = LQ_LEGACY_G; } - /* HT rate format */ - } else { - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; - - if (rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */ - tbl->is_ht40 = 1; - - mcs = rs_extract_rate(rate_n_flags); - - /* SISO */ - if (mcs <= IWL_RATE_SISO_60M_PLCP) { - if (num_of_ant == 1) - tbl->lq_type = LQ_SISO; /*else NONE*/ - /* MIMO2 */ - } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { - if (num_of_ant == 2) - tbl->lq_type = LQ_MIMO2; + + return 0; + } + + /* HT or VHT */ + if (ucode_rate & RATE_MCS_SGI_MSK) + rate->sgi = true; + + rate->bw = ucode_rate & RATE_MCS_CHAN_WIDTH_MSK; + + if (ucode_rate & RATE_MCS_HT_MSK) { + nss = ((ucode_rate & RATE_HT_MCS_NSS_MSK) >> + RATE_HT_MCS_NSS_POS) + 1; + + if (nss == 1) { + rate->type = LQ_HT_SISO; + WARN_ON_ONCE(num_of_ant != 1); + } else if (nss == 2) { + rate->type = LQ_HT_MIMO2; + WARN_ON_ONCE(num_of_ant != 2); } else { - WARN_ON_ONCE(num_of_ant == 3); + WARN_ON_ONCE(1); + } + } else if (ucode_rate & RATE_MCS_VHT_MSK) { + nss = ((ucode_rate & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + + if (nss == 1) { + rate->type = LQ_VHT_SISO; + WARN_ON_ONCE(num_of_ant != 1); + } else if (nss == 2) { + rate->type = LQ_VHT_MIMO2; + WARN_ON_ONCE(num_of_ant != 2); + } else { + WARN_ON_ONCE(1); } } + + WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_160); + WARN_ON_ONCE(rate->bw == RATE_MCS_CHAN_WIDTH_80 && + !is_vht(rate)); + return 0; } /* switch to another antenna/antennas and return 1 */ /* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, - struct iwl_scale_tbl_info *tbl) +static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate) { u8 new_ant_type; - if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + if (!rate->ant || rate->ant > ANT_ABC) return 0; - if (!rs_is_valid_ant(valid_ant, tbl->ant_type)) + if (!rs_is_valid_ant(valid_ant, rate->ant)) return 0; - new_ant_type = ant_toggle_lookup[tbl->ant_type]; + new_ant_type = ant_toggle_lookup[rate->ant]; - while ((new_ant_type != tbl->ant_type) && + while ((new_ant_type != rate->ant) && !rs_is_valid_ant(valid_ant, new_ant_type)) new_ant_type = ant_toggle_lookup[new_ant_type]; - if (new_ant_type == tbl->ant_type) + if (new_ant_type == rate->ant) return 0; - tbl->ant_type = new_ant_type; - *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; - *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; - return 1; -} + rate->ant = new_ant_type; -/** - * Green-field mode is valid if the station supports it and - * there are no non-GF stations present in the BSS. - */ -static bool rs_use_green(struct ieee80211_sta *sta) -{ - /* - * There's a bug somewhere in this code that causes the - * scaling to get stuck because GF+SGI can't be combined - * in SISO rates. Until we find that bug, disable GF, it - * has only limited benefit and we still interoperate with - * GF APs since we can always receive GF transmissions. - */ - return false; + return 1; } -/** - * rs_get_supported_rates - get the available rates - * - * if management frame or broadcast frame only return - * basic available rates. - * - */ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, - struct ieee80211_hdr *hdr, - enum iwl_table_type rate_type) + struct rs_rate *rate) { - if (is_legacy(rate_type)) { + if (is_legacy(rate)) return lq_sta->active_legacy_rate; - } else { - if (is_siso(rate_type)) - return lq_sta->active_siso_rate; - else { - WARN_ON_ONCE(!is_mimo2(rate_type)); - return lq_sta->active_mimo2_rate; - } - } + else if (is_siso(rate)) + return lq_sta->active_siso_rate; + else if (is_mimo2(rate)) + return lq_sta->active_mimo2_rate; + + WARN_ON_ONCE(1); + return 0; } static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, @@ -596,7 +864,7 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, /* 802.11A or ht walks to the next literal adjacent rate in * the rate table */ - if (is_a_band(rate_type) || !is_legacy(rate_type)) { + if (is_type_a_band(rate_type) || !is_type_legacy(rate_type)) { int i; u32 mask; @@ -644,74 +912,92 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, return (high << 8) | low; } -static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - u8 scale_index, u8 ht_possible) +static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) { - s32 low; - u16 rate_mask; + return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate); +} + +/* Get the next supported lower rate in the current column. + * Return true if bottom rate in the current column was reached + */ +static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + u8 low; u16 high_low; - u8 switch_to_legacy = 0; - u8 is_green = lq_sta->is_green; + u16 rate_mask; struct iwl_mvm *mvm = lq_sta->drv; - /* check if we need to switch from HT to legacy rates. - * assumption is that mandatory rates (1Mbps or 6Mbps) - * are always supported (spec demand) */ - if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { - switch_to_legacy = 1; - scale_index = rs_ht_to_legacy[scale_index]; - if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; - else - tbl->lq_type = LQ_G; + rate_mask = rs_get_supported_rates(lq_sta, rate); + high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, + rate->type); + low = high_low & 0xff; - if (num_of_ant(tbl->ant_type) > 1) - tbl->ant_type = - first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); + /* Bottom rate of column reached */ + if (low == IWL_RATE_INVALID) + return true; - tbl->is_ht40 = 0; - tbl->is_SGI = 0; - tbl->max_search = IWL_MAX_SEARCH; - } + rate->index = low; + return false; +} - rate_mask = rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); +/* Get the next rate to use following a column downgrade */ +static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + struct iwl_mvm *mvm = lq_sta->drv; - /* Mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - /* supp_rates has no CCK bits in A mode */ + if (is_legacy(rate)) { + /* No column to downgrade from Legacy */ + return; + } else if (is_siso(rate)) { + /* Downgrade to Legacy if we were in SISO */ if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = (u16)(rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + rate->type = LQ_LEGACY_A; else - rate_mask = (u16)(rate_mask & lq_sta->supp_rates); - } + rate->type = LQ_LEGACY_G; - /* If we switched from HT to legacy, check current rate */ - if (switch_to_legacy && (rate_mask & (1 << scale_index))) { - low = scale_index; - goto out; + rate->bw = RATE_MCS_CHAN_WIDTH_20; + + WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX || + rate->index > IWL_RATE_MCS_9_INDEX); + + rate->index = rs_ht_to_legacy[rate->index]; + } else { + /* Downgrade to SISO with same MCS if in MIMO */ + rate->type = is_vht_mimo2(rate) ? + LQ_VHT_SISO : LQ_HT_SISO; } - high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, - tbl->lq_type); - low = high_low & 0xff; - if (low == IWL_RATE_INVALID) - low = scale_index; + if (num_of_ant(rate->ant) > 1) + rate->ant = first_antenna(mvm->fw->valid_tx_ant); -out: - return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); + /* Relevant in both switching to SISO or Legacy */ + rate->sgi = false; + + if (!rs_rate_supported(lq_sta, rate)) + rs_get_lower_rate_in_column(lq_sta, rate); } -/* - * Simple function to compare two rate scale table types - */ -static bool table_type_matches(struct iwl_scale_tbl_info *a, - struct iwl_scale_tbl_info *b) +/* Simple function to compare two rate scale table types */ +static inline bool rs_rate_match(struct rs_rate *a, + struct rs_rate *b) { - return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && - (a->is_SGI == b->is_SGI); + return (a->type == b->type) && (a->ant == b->ant) && (a->sgi == b->sgi); +} + +static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags) +{ + if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + return RATE_MCS_CHAN_WIDTH_40; + else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + return RATE_MCS_CHAN_WIDTH_80; + else if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + return RATE_MCS_CHAN_WIDTH_160; + + return RATE_MCS_CHAN_WIDTH_20; } /* @@ -723,7 +1009,7 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, { int legacy_success; int retries; - int rs_index, mac_index, i; + int mac_index, i; struct iwl_lq_sta *lq_sta = priv_sta; struct iwl_lq_cmd *table; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -731,12 +1017,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); enum mac80211_rate_control_flags mac_flags; - u32 tx_rate; - struct iwl_scale_tbl_info tbl_type; + u32 ucode_rate; + struct rs_rate rate; struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; - - IWL_DEBUG_RATE_LIMIT(mvm, - "get frame ack response, update rate scale window\n"); + u8 reduced_txp = (uintptr_t)info->status.status_driver_data[0]; /* Treat uninitialized rate scaling data same as non-existing. */ if (!lq_sta) { @@ -747,6 +1031,13 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, return; } +#ifdef CONFIG_MAC80211_DEBUGFS + /* Disable last tx check if we are debugging with fixed rate */ + if (lq_sta->dbg_fixed_rate) { + IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n"); + return; + } +#endif if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; @@ -765,10 +1056,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, * to a new "search" mode (which might become the new "active" mode). */ table = &lq_sta->lq; - tx_rate = le32_to_cpu(table->rs_table[0]); - rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type, &rs_index); + ucode_rate = le32_to_cpu(table->rs_table[0]); + rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); if (info->band == IEEE80211_BAND_5GHZ) - rs_index -= IWL_FIRST_OFDM_RATE; + rate.index -= IWL_FIRST_OFDM_RATE; mac_flags = info->status.rates[0].flags; mac_index = info->status.rates[0].idx; /* For HT packets, map MCS to PLCP */ @@ -783,20 +1074,39 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, */ if (info->band == IEEE80211_BAND_2GHZ) mac_index += IWL_FIRST_OFDM_RATE; + } else if (mac_flags & IEEE80211_TX_RC_VHT_MCS) { + mac_index &= RATE_VHT_MCS_RATE_CODE_MSK; + if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) + mac_index++; + } + + if (time_after(jiffies, + (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) { + int tid; + IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + ieee80211_stop_tx_ba_session(sta, tid); + + iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); + return; } + lq_sta->last_tx = jiffies; + /* Here we actually compare this rate to the latest LQ command */ if ((mac_index < 0) || - (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || - (tbl_type.ant_type != info->status.antenna) || - (!!(tx_rate & RATE_MCS_HT_MSK) != - !!(mac_flags & IEEE80211_TX_RC_MCS)) || - (!!(tx_rate & RATE_HT_MCS_GF_MSK) != - !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || - (rs_index != mac_index)) { + (rate.sgi != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || + (rate.bw != rs_ch_width_from_mac_flags(mac_flags)) || + (rate.ant != info->status.antenna) || + (!!(ucode_rate & RATE_MCS_HT_MSK) != + !!(mac_flags & IEEE80211_TX_RC_MCS)) || + (!!(ucode_rate & RATE_MCS_VHT_MSK) != + !!(mac_flags & IEEE80211_TX_RC_VHT_MCS)) || + (!!(ucode_rate & RATE_HT_MCS_GF_MSK) != + !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || + (rate.index != mac_index)) { IWL_DEBUG_RATE(mvm, "initial rate %d does not match %d (0x%x)\n", - mac_index, rs_index, tx_rate); + mac_index, rate.index, ucode_rate); /* * Since rates mis-match, the last LQ command may have failed. * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with @@ -805,7 +1115,10 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); + IWL_DEBUG_RATE(mvm, + "Too many rates mismatch. Send sync LQ. rs_state %d\n", + lq_sta->rs_state); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } /* Regardless, ignore this status info for outdated rate */ return; @@ -814,28 +1127,23 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, lq_sta->missed_rate_counter = 0; /* Figure out if rate scale algorithm is in active or search table */ - if (table_type_matches(&tbl_type, - &(lq_sta->lq_info[lq_sta->active_tbl]))) { + if (rs_rate_match(&rate, + &(lq_sta->lq_info[lq_sta->active_tbl].rate))) { curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - } else if (table_type_matches( - &tbl_type, &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + } else if (rs_rate_match(&rate, + &lq_sta->lq_info[1 - lq_sta->active_tbl].rate)) { curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); } else { IWL_DEBUG_RATE(mvm, "Neither active nor search matches tx rate\n"); tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - IWL_DEBUG_RATE(mvm, "active- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, - tmp_tbl->is_SGI); + rs_dump_rate(mvm, &tmp_tbl->rate, "ACTIVE"); tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); - IWL_DEBUG_RATE(mvm, "search- lq:%x, ant:%x, SGI:%d\n", - tmp_tbl->lq_type, tmp_tbl->ant_type, - tmp_tbl->is_SGI); - IWL_DEBUG_RATE(mvm, "actual- lq:%x, ant:%x, SGI:%d\n", - tbl_type.lq_type, tbl_type.ant_type, - tbl_type.is_SGI); + rs_dump_rate(mvm, &tmp_tbl->rate, "SEARCH"); + rs_dump_rate(mvm, &rate, "ACTUAL"); + /* * no matching table found, let's by-pass the data collection * and continue to perform rate scale to find the rate table @@ -852,15 +1160,15 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, * first index into rate scale table. */ if (info->flags & IEEE80211_TX_STAT_AMPDU) { - tx_rate = le32_to_cpu(table->rs_table[0]); - rs_get_tbl_info_from_mcs(tx_rate, info->band, &tbl_type, - &rs_index); - rs_collect_tx_data(curr_tbl, rs_index, + ucode_rate = le32_to_cpu(table->rs_table[0]); + rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); + rs_collect_tx_data(lq_sta, curr_tbl, rate.index, info->status.ampdu_len, - info->status.ampdu_ack_len); + info->status.ampdu_ack_len, + reduced_txp); /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { lq_sta->total_success += info->status.ampdu_ack_len; lq_sta->total_failed += (info->status.ampdu_len - info->status.ampdu_ack_len); @@ -877,31 +1185,33 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); /* Collect data for each rate used during failed TX attempts */ for (i = 0; i <= retries; ++i) { - tx_rate = le32_to_cpu(table->rs_table[i]); - rs_get_tbl_info_from_mcs(tx_rate, info->band, - &tbl_type, &rs_index); + ucode_rate = le32_to_cpu(table->rs_table[i]); + rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); /* * Only collect stats if retried rate is in the same RS * table as active/search. */ - if (table_type_matches(&tbl_type, curr_tbl)) + if (rs_rate_match(&rate, &curr_tbl->rate)) tmp_tbl = curr_tbl; - else if (table_type_matches(&tbl_type, other_tbl)) + else if (rs_rate_match(&rate, &other_tbl->rate)) tmp_tbl = other_tbl; else continue; - rs_collect_tx_data(tmp_tbl, rs_index, 1, - i < retries ? 0 : legacy_success); + + rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1, + i < retries ? 0 : legacy_success, + reduced_txp); } /* Update success/fail counts if not searching for new mode */ - if (lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { lq_sta->total_success += legacy_success; lq_sta->total_failed += retries + (1 - legacy_success); } } /* The last TX rate is cached in lq_sta; it's set in if/else above */ - lq_sta->last_rate_n_flags = tx_rate; + lq_sta->last_rate_n_flags = ucode_rate; + IWL_DEBUG_RATE(mvm, "reduced txpower: %d\n", reduced_txp); done: /* See if there's a better rate or modulation mode to try. */ if (sta && sta->supp_rates[sband->band]) @@ -919,8 +1229,8 @@ done: static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, struct iwl_lq_sta *lq_sta) { - IWL_DEBUG_RATE(mvm, "we are staying in the same table\n"); - lq_sta->stay_in_tbl = 1; /* only place this gets set */ + IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); + lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; if (is_legacy) { lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; @@ -934,643 +1244,151 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = jiffies; - lq_sta->action_counter = 0; + lq_sta->visited_columns = 0; } -/* - * Find correct throughput table for given mode of modulation - */ -static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl) -{ - /* Used to choose among HT tables */ - s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - - /* Check for invalid LQ type */ - if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Legacy rates have only one table */ - if (is_legacy(tbl->lq_type)) { - tbl->expected_tpt = expected_tpt_legacy; - return; - } - - /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2), channel width (20/40), SGI, and aggregation - * status */ - if (is_siso(tbl->lq_type) && !tbl->is_ht40) - ht_tbl_pointer = expected_tpt_siso20MHz; - else if (is_siso(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_siso40MHz; - else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40) - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - else { - WARN_ON_ONCE(!is_mimo2(tbl->lq_type)); - ht_tbl_pointer = expected_tpt_mimo2_40MHz; - } - - if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ - tbl->expected_tpt = ht_tbl_pointer[0]; - else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ - tbl->expected_tpt = ht_tbl_pointer[1]; - else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ - tbl->expected_tpt = ht_tbl_pointer[2]; - else /* AGG+SGI */ - tbl->expected_tpt = ht_tbl_pointer[3]; -} - -/* - * Find starting rate for new "search" high-throughput mode of modulation. - * Goal is to find lowest expected rate (under perfect conditions) that is - * above the current measured throughput of "active" mode, to give new mode - * a fair chance to prove itself without too many challenges. - * - * This gets called when transitioning to more aggressive modulation - * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive - * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need - * to decrease to match "active" throughput. When moving from MIMO to SISO, - * bit rate will typically need to increase, but not if performance was bad. - */ -static s32 rs_get_best_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, /* "search" */ - u16 rate_mask, s8 index) -{ - /* "active" values */ - struct iwl_scale_tbl_info *active_tbl = - &(lq_sta->lq_info[lq_sta->active_tbl]); - s32 active_sr = active_tbl->win[index].success_ratio; - s32 active_tpt = active_tbl->expected_tpt[index]; - - /* expected "search" throughput */ - s32 *tpt_tbl = tbl->expected_tpt; - - s32 new_rate, high, low, start_hi; - u16 high_low; - s8 rate = index; - - new_rate = high = low = start_hi = IWL_RATE_INVALID; - - while (1) { - high_low = rs_get_adjacent_rate(mvm, rate, rate_mask, - tbl->lq_type); - - low = high_low & 0xff; - high = (high_low >> 8) & 0xff; - - /* - * Lower the "search" bit rate, to give new "search" mode - * approximately the same throughput as "active" if: - * - * 1) "Active" mode has been working modestly well (but not - * great), and expected "search" throughput (under perfect - * conditions) at candidate rate is above the actual - * measured "active" throughput (but less than expected - * "active" throughput under perfect conditions). - * OR - * 2) "Active" mode has been working perfectly or very well - * and expected "search" throughput (under perfect - * conditions) at candidate rate is above expected - * "active" throughput (under perfect conditions). - */ - if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && - ((active_sr > IWL_RATE_DECREASE_TH) && - (active_sr <= IWL_RATE_HIGH_TH) && - (tpt_tbl[rate] <= active_tpt))) || - ((active_sr >= IWL_RATE_SCALE_SWITCH) && - (tpt_tbl[rate] > active_tpt))) { - /* (2nd or later pass) - * If we've already tried to raise the rate, and are - * now trying to lower it, use the higher rate. */ - if (start_hi != IWL_RATE_INVALID) { - new_rate = start_hi; - break; - } - - new_rate = rate; - - /* Loop again with lower rate */ - if (low != IWL_RATE_INVALID) - rate = low; - - /* Lower rate not available, use the original */ - else - break; - - /* Else try to raise the "search" rate to match "active" */ - } else { - /* (2nd or later pass) - * If we've already tried to lower the rate, and are - * now trying to raise it, use the lower rate. */ - if (new_rate != IWL_RATE_INVALID) - break; - - /* Loop again with higher rate */ - else if (high != IWL_RATE_INVALID) { - start_hi = high; - rate = high; - - /* Higher rate not available, use the original */ - } else { - new_rate = rate; - break; - } - } - } - - return new_rate; -} - -static bool iwl_is_ht40_tx_allowed(struct ieee80211_sta *sta) +static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, + const struct rs_tx_column *column) { - return sta->bandwidth >= IEEE80211_STA_RX_BW_40; -} - -/* - * Set up search table for MIMO2 - */ -static int rs_switch_to_mimo2(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - s32 rate; - s8 is_green = lq_sta->is_green; - - if (!sta->ht_cap.ht_supported) - return -1; - - if (sta->smps_mode == IEEE80211_SMPS_STATIC) - return -1; - - /* Need both Tx chains/antennas to support MIMO */ - if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) < 2) - return -1; - - IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n"); - - tbl->lq_type = LQ_MIMO2; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_mimo2_rate; - - if (iwl_is_ht40_tx_allowed(sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - rs_set_expected_tpt_table(lq_sta, tbl); - - rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); - - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 best rate %d mask %X\n", - rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(mvm, "Can't switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; + switch (column->mode) { + case RS_LEGACY: + return lq_sta->max_legacy_rate_idx; + case RS_SISO: + return lq_sta->max_siso_rate_idx; + case RS_MIMO2: + return lq_sta->max_mimo2_rate_idx; + default: + WARN_ON_ONCE(1); } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green); - - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; -} - -/* - * Set up search table for SISO - */ -static int rs_switch_to_siso(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - struct iwl_scale_tbl_info *tbl, int index) -{ - u16 rate_mask; - u8 is_green = lq_sta->is_green; - s32 rate; - - if (!sta->ht_cap.ht_supported) - return -1; - - IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n"); - - tbl->lq_type = LQ_SISO; - tbl->action = 0; - tbl->max_search = IWL_MAX_SEARCH; - rate_mask = lq_sta->active_siso_rate; - - if (iwl_is_ht40_tx_allowed(sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - if (is_green) - tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ - - rs_set_expected_tpt_table(lq_sta, tbl); - rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); - IWL_DEBUG_RATE(mvm, "LQ: get best rate %d mask %X\n", rate, rate_mask); - if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { - IWL_DEBUG_RATE(mvm, - "can not switch with index %d rate mask %x\n", - rate, rate_mask); - return -1; - } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green); - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); - return 0; + return lq_sta->max_legacy_rate_idx; } -/* - * Try to switch to new modulation mode from legacy - */ -static int rs_move_legacy_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, - int index) +static const u16 *rs_get_expected_tpt_table(struct iwl_lq_sta *lq_sta, + const struct rs_tx_column *column, + u32 bw) { - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); - int ret; - u8 update_search_tbl_counter = 0; - - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_LEGACY_SWITCH_ANTENNA1: - case IWL_LEGACY_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n"); - - if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; + /* Used to choose among HT tables */ + const u16 (*ht_tbl_pointer)[IWL_RATE_COUNT]; - /* Don't change antenna if success has been great */ - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; + if (WARN_ON_ONCE(column->mode != RS_LEGACY && + column->mode != RS_SISO && + column->mode != RS_MIMO2)) + return expected_tpt_legacy; - /* Set up search table to try other antenna */ - memcpy(search_tbl, tbl, sz); + /* Legacy rates have only one table */ + if (column->mode == RS_LEGACY) + return expected_tpt_legacy; - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - rs_set_expected_tpt_table(lq_sta, search_tbl); - goto out; - } + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation + * status */ + if (column->mode == RS_SISO) { + switch (bw) { + case RATE_MCS_CHAN_WIDTH_20: + ht_tbl_pointer = expected_tpt_siso_20MHz; break; - case IWL_LEGACY_SWITCH_SISO: - IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to SISO\n"); - - /* Set up search table to try SISO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - ret = rs_switch_to_siso(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } - + case RATE_MCS_CHAN_WIDTH_40: + ht_tbl_pointer = expected_tpt_siso_40MHz; break; - case IWL_LEGACY_SWITCH_MIMO2: - IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO2\n"); - - /* Set up search table to try MIMO */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_AB; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) { - lq_sta->action_counter = 0; - goto out; - } + case RATE_MCS_CHAN_WIDTH_80: + ht_tbl_pointer = expected_tpt_siso_80MHz; break; default: WARN_ON_ONCE(1); } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - - if (tbl->action == start_action) + } else if (column->mode == RS_MIMO2) { + switch (bw) { + case RATE_MCS_CHAN_WIDTH_20: + ht_tbl_pointer = expected_tpt_mimo2_20MHz; break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - -out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - return 0; -} - -/* - * Try to switch to new modulation mode from SISO - */ -static int rs_move_siso_to_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, int index) -{ - u8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); - u8 update_search_tbl_counter = 0; - int ret; - - switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_MIMO2; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - valid_tx_ant = - first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - break; - default: - IWL_ERR(mvm, "Invalid BT load %d", - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); - break; - } - - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_SISO_SWITCH_ANTENNA1: - case IWL_SISO_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n"); - if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO && - BT_MBOX_MSG(&mvm->last_bt_notif, 3, - TRAFFIC_LOAD) == 0) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } + case RATE_MCS_CHAN_WIDTH_40: + ht_tbl_pointer = expected_tpt_mimo2_40MHz; break; - case IWL_SISO_SWITCH_MIMO2: - IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO2\n"); - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = 0; - - search_tbl->ant_type = ANT_AB; - - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_mimo2(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; + case RATE_MCS_CHAN_WIDTH_80: + ht_tbl_pointer = expected_tpt_mimo2_80MHz; break; - case IWL_SISO_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; - - IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n"); - - memcpy(search_tbl, tbl, sz); - if (is_green) { - if (!tbl->is_SGI) - break; - else - IWL_ERR(mvm, - "SGI was set in GF+SISO\n"); - } - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; default: WARN_ON_ONCE(1); } - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_GI) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - - if (tbl->action == start_action) - break; + } else { + WARN_ON_ONCE(1); } - search_tbl->lq_type = LQ_NONE; - return 0; - - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_GI) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; - return 0; + if (!column->sgi && !lq_sta->is_agg) /* Normal */ + return ht_tbl_pointer[0]; + else if (column->sgi && !lq_sta->is_agg) /* SGI */ + return ht_tbl_pointer[1]; + else if (!column->sgi && lq_sta->is_agg) /* AGG */ + return ht_tbl_pointer[2]; + else /* AGG+SGI */ + return ht_tbl_pointer[3]; } -/* - * Try to switch to new modulation mode from MIMO2 - */ -static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, - struct ieee80211_sta *sta, int index) +static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) { - s8 is_green = lq_sta->is_green; - struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - struct iwl_scale_tbl_info *search_tbl = - &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - u32 sz = (sizeof(struct iwl_scale_tbl_info) - - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); - u8 start_action; - u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); - u8 update_search_tbl_counter = 0; - int ret; + struct rs_rate *rate = &tbl->rate; + const struct rs_tx_column *column = &rs_tx_columns[tbl->column]; - switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - default: - IWL_ERR(mvm, "Invalid BT load %d", - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); - break; - } + tbl->expected_tpt = rs_get_expected_tpt_table(lq_sta, column, rate->bw); +} - start_action = tbl->action; - while (1) { - lq_sta->action_counter++; - switch (tbl->action) { - case IWL_MIMO2_SWITCH_ANTENNA1: - case IWL_MIMO2_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle Antennas\n"); +static s32 rs_get_best_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, /* "search" */ + unsigned long rate_mask, s8 index) +{ + struct iwl_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 success_ratio = active_tbl->win[index].success_ratio; + u16 expected_current_tpt = active_tbl->expected_tpt[index]; + const u16 *tpt_tbl = tbl->expected_tpt; + u16 high_low; + u32 target_tpt; + int rate_idx; - if (tx_chains_num <= 2) - break; + if (success_ratio > RS_SR_NO_DECREASE) { + target_tpt = 100 * expected_current_tpt; + IWL_DEBUG_RATE(mvm, + "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", + success_ratio, target_tpt); + } else { + target_tpt = lq_sta->last_tpt; + IWL_DEBUG_RATE(mvm, + "SR %d not thag good. Find rate exceeding ACTUAL_TPT %d\n", + success_ratio, target_tpt); + } - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; + rate_idx = find_first_bit(&rate_mask, BITS_PER_LONG); - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } + while (rate_idx != IWL_RATE_INVALID) { + if (target_tpt < (100 * tpt_tbl[rate_idx])) break; - case IWL_MIMO2_SWITCH_SISO_A: - case IWL_MIMO2_SWITCH_SISO_B: - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n"); - - /* Set up new search table for SISO */ - memcpy(search_tbl, tbl, sz); - - if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) - search_tbl->ant_type = ANT_A; - else /* tbl->action == IWL_MIMO2_SWITCH_SISO_B */ - search_tbl->ant_type = ANT_B; - if (!rs_is_valid_ant(valid_tx_ant, - search_tbl->ant_type)) - break; - - ret = rs_switch_to_siso(mvm, lq_sta, sta, - search_tbl, index); - if (!ret) - goto out; + high_low = rs_get_adjacent_rate(mvm, rate_idx, rate_mask, + tbl->rate.type); - break; - - case IWL_MIMO2_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) - break; + rate_idx = (high_low >> 8) & 0xff; + } - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n"); + IWL_DEBUG_RATE(mvm, "Best rate found %d target_tp %d expected_new %d\n", + rate_idx, target_tpt, + rate_idx != IWL_RATE_INVALID ? + 100 * tpt_tbl[rate_idx] : IWL_INVALID_VALUE); - /* Set up new search table for MIMO2 */ - memcpy(search_tbl, tbl, sz); - search_tbl->is_SGI = !tbl->is_SGI; - rs_set_expected_tpt_table(lq_sta, search_tbl); - /* - * If active table already uses the fastest possible - * modulation (dual stream with short guard interval), - * and it's working well, there's no need to look - * for a better type of modulation! - */ - if (tbl->is_SGI) { - s32 tpt = lq_sta->last_tpt / 100; - if (tpt >= search_tbl->expected_tpt[index]) - break; - } - search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, - index, is_green); - update_search_tbl_counter = 1; - goto out; - default: - WARN_ON_ONCE(1); - } - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_GI) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + return rate_idx; +} - if (tbl->action == start_action) - break; - } - search_tbl->lq_type = LQ_NONE; - return 0; - out: - lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_GI) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; - if (update_search_tbl_counter) - search_tbl->action = tbl->action; +static u32 rs_bw_from_sta_bw(struct ieee80211_sta *sta) +{ + if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) + return RATE_MCS_CHAN_WIDTH_80; + else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) + return RATE_MCS_CHAN_WIDTH_40; - return 0; + return RATE_MCS_CHAN_WIDTH_20; } /* @@ -1583,7 +1401,6 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, 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; @@ -1594,13 +1411,13 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) tbl = &(lq_sta->lq_info[active_tbl]); /* If we've been disallowing search, see if we should now allow it */ - if (lq_sta->stay_in_tbl) { + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) { /* Elapsed time using current modulation mode */ if (lq_sta->flush_timer) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_sta->flush_timer + - IWL_RATE_SCALE_FLUSH_INTVL)); + RS_STAY_IN_COLUMN_TIMEOUT)); /* * Check if we should allow search for new modulation mode. @@ -1622,10 +1439,14 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) flush_interval_passed); /* Allow search for new mode */ - lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_STARTED; + IWL_DEBUG_RATE(mvm, + "Moving to RS_STATE_SEARCH_CYCLE_STARTED\n"); lq_sta->total_failed = 0; lq_sta->total_success = 0; lq_sta->flush_timer = 0; + /* mark the current column as visited */ + lq_sta->visited_columns = BIT(tbl->column); /* * Else if we've used this modulation mode enough repetitions * (regardless of elapsed time or success/failure), reset @@ -1640,18 +1461,15 @@ 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(mvm, tbl); } } /* If transitioning to allow "search", reset all history * bitmaps and stats in active table (this will become the new * "search" table). */ - if (!lq_sta->stay_in_tbl) { - for (i = 0; i < IWL_RATE_COUNT; i++) - rs_rate_scale_clear_window(&(tbl->win[i])); + if (lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED) { + rs_rate_scale_clear_tbl_windows(mvm, tbl); } } } @@ -1660,16 +1478,12 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) * setup rate table in uCode */ static void rs_update_rate_tbl(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - struct iwl_scale_tbl_info *tbl, - int index, u8 is_green) + struct rs_rate *rate) { - u32 rate; - - /* Update uCode's rate table. */ - rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green); - rs_fill_link_cmd(mvm, lq_sta, rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); + rs_fill_lq_cmd(mvm, sta, lq_sta, rate); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } static u8 rs_get_tid(struct iwl_lq_sta *lq_data, @@ -1688,6 +1502,439 @@ static u8 rs_get_tid(struct iwl_lq_sta *lq_data, return tid; } +static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl) +{ + int i, j, max_rate; + enum rs_column next_col_id; + const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; + const struct rs_tx_column *next_col; + allow_column_func_t allow_func; + u8 valid_ants = mvm->fw->valid_tx_ant; + const u16 *expected_tpt_tbl; + u16 tpt, max_expected_tpt; + + for (i = 0; i < MAX_NEXT_COLUMNS; i++) { + next_col_id = curr_col->next_columns[i]; + + if (next_col_id == RS_COLUMN_INVALID) + continue; + + if (lq_sta->visited_columns & BIT(next_col_id)) { + IWL_DEBUG_RATE(mvm, "Skip already visited column %d\n", + next_col_id); + continue; + } + + next_col = &rs_tx_columns[next_col_id]; + + if (!rs_is_valid_ant(valid_ants, next_col->ant)) { + IWL_DEBUG_RATE(mvm, + "Skip column %d as ANT config isn't supported by chip. valid_ants 0x%x column ant 0x%x\n", + next_col_id, valid_ants, next_col->ant); + continue; + } + + for (j = 0; j < MAX_COLUMN_CHECKS; j++) { + allow_func = next_col->checks[j]; + if (allow_func && !allow_func(mvm, sta, tbl)) + break; + } + + if (j != MAX_COLUMN_CHECKS) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: not allowed (check %d failed)\n", + next_col_id, j); + + continue; + } + + tpt = lq_sta->last_tpt / 100; + expected_tpt_tbl = rs_get_expected_tpt_table(lq_sta, next_col, + rs_bw_from_sta_bw(sta)); + if (WARN_ON_ONCE(!expected_tpt_tbl)) + continue; + + max_rate = rs_get_max_allowed_rate(lq_sta, next_col); + if (WARN_ON_ONCE(max_rate == IWL_RATE_INVALID)) + continue; + + max_expected_tpt = expected_tpt_tbl[max_rate]; + if (tpt >= max_expected_tpt) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: can't beat current TPT. Max expected %d current %d\n", + next_col_id, max_expected_tpt, tpt); + continue; + } + + IWL_DEBUG_RATE(mvm, + "Found potential column %d. Max expected %d current %d\n", + next_col_id, max_expected_tpt, tpt); + break; + } + + if (i == MAX_NEXT_COLUMNS) + return RS_COLUMN_INVALID; + + return next_col_id; +} + +static int rs_switch_to_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta *sta, + enum rs_column col_id) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct rs_rate *rate = &search_tbl->rate; + const struct rs_tx_column *column = &rs_tx_columns[col_id]; + const struct rs_tx_column *curr_column = &rs_tx_columns[tbl->column]; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + unsigned long rate_mask = 0; + u32 rate_idx = 0; + + memcpy(search_tbl, tbl, sz); + + rate->sgi = column->sgi; + rate->ant = column->ant; + + if (column->mode == RS_LEGACY) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; + + rate_mask = lq_sta->active_legacy_rate; + } else if (column->mode == RS_SISO) { + rate->type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; + rate_mask = lq_sta->active_siso_rate; + } else if (column->mode == RS_MIMO2) { + rate->type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; + rate_mask = lq_sta->active_mimo2_rate; + } else { + WARN_ON_ONCE("Bad column mode"); + } + + rate->bw = rs_bw_from_sta_bw(sta); + search_tbl->column = col_id; + rs_set_expected_tpt_table(lq_sta, search_tbl); + + lq_sta->visited_columns |= BIT(col_id); + + /* Get the best matching rate if we're changing modes. e.g. + * SISO->MIMO, LEGACY->SISO, MIMO->SISO + */ + if (curr_column->mode != column->mode) { + rate_idx = rs_get_best_rate(mvm, lq_sta, search_tbl, + rate_mask, rate->index); + + if ((rate_idx == IWL_RATE_INVALID) || + !(BIT(rate_idx) & rate_mask)) { + IWL_DEBUG_RATE(mvm, + "can not switch with index %d" + " rate mask %lx\n", + rate_idx, rate_mask); + + goto err; + } + + rate->index = rate_idx; + } + + IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", + col_id, rate->index); + + return 0; + +err: + rate->type = LQ_NONE; + return -1; +} + +static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl, + s32 sr, int low, int high, + int current_tpt, + int low_tpt, int high_tpt) +{ + enum rs_action action = RS_ACTION_STAY; + + if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) { + IWL_DEBUG_RATE(mvm, + "Decrease rate because of low SR\n"); + return RS_ACTION_DOWNSCALE; + } + + if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE) && + (high != IWL_RATE_INVALID)) { + IWL_DEBUG_RATE(mvm, + "No data about high/low rates. Increase rate\n"); + return RS_ACTION_UPSCALE; + } + + if ((high_tpt == IWL_INVALID_VALUE) && + (high != IWL_RATE_INVALID) && + (low_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt)) { + IWL_DEBUG_RATE(mvm, + "No data about high rate and low rate is worse. Increase rate\n"); + return RS_ACTION_UPSCALE; + } + + if ((high_tpt != IWL_INVALID_VALUE) && + (high_tpt > current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Higher rate is better. Increate rate\n"); + return RS_ACTION_UPSCALE; + } + + if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Both high and low are worse. Maintain rate\n"); + return RS_ACTION_STAY; + } + + if ((low_tpt != IWL_INVALID_VALUE) && + (low_tpt > current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Lower rate is better\n"); + action = RS_ACTION_DOWNSCALE; + goto out; + } + + if ((low_tpt == IWL_INVALID_VALUE) && + (low != IWL_RATE_INVALID)) { + IWL_DEBUG_RATE(mvm, + "No data about lower rate\n"); + action = RS_ACTION_DOWNSCALE; + goto out; + } + + IWL_DEBUG_RATE(mvm, "Maintain rate\n"); + +out: + if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { + if (sr >= RS_SR_NO_DECREASE) { + IWL_DEBUG_RATE(mvm, + "SR is above NO DECREASE. Avoid downscale\n"); + action = RS_ACTION_STAY; + } else if (current_tpt > (100 * tbl->expected_tpt[low])) { + IWL_DEBUG_RATE(mvm, + "Current TPT is higher than max expected in low rate. Avoid downscale\n"); + action = RS_ACTION_STAY; + } else { + IWL_DEBUG_RATE(mvm, "Decrease rate\n"); + } + } + + return action; +} + +static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, + int *weaker, int *stronger) +{ + *weaker = index + TPC_TX_POWER_STEP; + if (*weaker > TPC_MAX_REDUCTION) + *weaker = TPC_INVALID; + + *stronger = index - TPC_TX_POWER_STEP; + if (*stronger < 0) + *stronger = TPC_INVALID; +} + +static bool rs_tpc_allowed(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + struct rs_rate *rate, enum ieee80211_band band) +{ + int index = rate->index; + bool cam = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM); + bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && + !vif->bss_conf.ps); + + IWL_DEBUG_RATE(mvm, "cam: %d sta_ps_disabled %d\n", + cam, sta_ps_disabled); + /* + * allow tpc only if power management is enabled, or bt coex + * activity grade allows it and we are on 2.4Ghz. + */ + if ((cam || sta_ps_disabled) && + !iwl_mvm_bt_coex_is_tpc_allowed(mvm, band)) + return false; + + IWL_DEBUG_RATE(mvm, "check rate, table type: %d\n", rate->type); + if (is_legacy(rate)) + return index == IWL_RATE_54M_INDEX; + if (is_ht(rate)) + return index == IWL_RATE_MCS_7_INDEX; + if (is_vht(rate)) + return index == IWL_RATE_MCS_7_INDEX || + index == IWL_RATE_MCS_8_INDEX || + index == IWL_RATE_MCS_9_INDEX; + + WARN_ON_ONCE(1); + return false; +} + +enum tpc_action { + TPC_ACTION_STAY, + TPC_ACTION_DECREASE, + TPC_ACTION_INCREASE, + TPC_ACTION_NO_RESTIRCTION, +}; + +static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, + s32 sr, int weak, int strong, + int current_tpt, + int weak_tpt, int strong_tpt) +{ + /* stay until we have valid tpt */ + if (current_tpt == IWL_INVALID_VALUE) { + IWL_DEBUG_RATE(mvm, "no current tpt. stay.\n"); + return TPC_ACTION_STAY; + } + + /* Too many failures, increase txp */ + if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) { + IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); + return TPC_ACTION_NO_RESTIRCTION; + } + + /* try decreasing first if applicable */ + if (weak != TPC_INVALID) { + if (weak_tpt == IWL_INVALID_VALUE && + (strong_tpt == IWL_INVALID_VALUE || + current_tpt >= strong_tpt)) { + IWL_DEBUG_RATE(mvm, + "no weak txp measurement. decrease txp\n"); + return TPC_ACTION_DECREASE; + } + + if (weak_tpt > current_tpt) { + IWL_DEBUG_RATE(mvm, + "lower txp has better tpt. decrease txp\n"); + return TPC_ACTION_DECREASE; + } + } + + /* next, increase if needed */ + if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) { + if (weak_tpt == IWL_INVALID_VALUE && + strong_tpt != IWL_INVALID_VALUE && + current_tpt < strong_tpt) { + IWL_DEBUG_RATE(mvm, + "higher txp has better tpt. increase txp\n"); + return TPC_ACTION_INCREASE; + } + + if (weak_tpt < current_tpt && + (strong_tpt == IWL_INVALID_VALUE || + strong_tpt > current_tpt)) { + IWL_DEBUG_RATE(mvm, + "lower txp has worse tpt. increase txp\n"); + return TPC_ACTION_INCREASE; + } + } + + IWL_DEBUG_RATE(mvm, "no need to increase or decrease txp - stay\n"); + return TPC_ACTION_STAY; +} + +static bool rs_tpc_perform(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct ieee80211_vif *vif = mvm_sta->vif; + struct ieee80211_chanctx_conf *chanctx_conf; + enum ieee80211_band band; + struct iwl_rate_scale_data *window; + struct rs_rate *rate = &tbl->rate; + enum tpc_action action; + s32 sr; + u8 cur = lq_sta->lq.reduced_tpc; + int current_tpt; + int weak, strong; + int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE; + +#ifdef CONFIG_MAC80211_DEBUGFS + if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) { + IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n", + lq_sta->dbg_fixed_txp_reduction); + lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction; + return cur != lq_sta->dbg_fixed_txp_reduction; + } +#endif + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + if (WARN_ON(!chanctx_conf)) + band = IEEE80211_NUM_BANDS; + else + band = chanctx_conf->def.chan->band; + rcu_read_unlock(); + + if (!rs_tpc_allowed(mvm, vif, rate, band)) { + IWL_DEBUG_RATE(mvm, + "tpc is not allowed. remove txp restrictions\n"); + lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; + return cur != TPC_NO_REDUCTION; + } + + rs_get_adjacent_txp(mvm, cur, &weak, &strong); + + /* Collect measured throughputs for current and adjacent rates */ + window = tbl->tpc_win; + sr = window[cur].success_ratio; + current_tpt = window[cur].average_tpt; + if (weak != TPC_INVALID) + weak_tpt = window[weak].average_tpt; + if (strong != TPC_INVALID) + strong_tpt = window[strong].average_tpt; + + IWL_DEBUG_RATE(mvm, + "(TPC: %d): cur_tpt %d SR %d weak %d strong %d weak_tpt %d strong_tpt %d\n", + cur, current_tpt, sr, weak, strong, + weak_tpt, strong_tpt); + + action = rs_get_tpc_action(mvm, sr, weak, strong, + current_tpt, weak_tpt, strong_tpt); + + /* override actions if we are on the edge */ + if (weak == TPC_INVALID && action == TPC_ACTION_DECREASE) { + IWL_DEBUG_RATE(mvm, "already in lowest txp, stay\n"); + action = TPC_ACTION_STAY; + } else if (strong == TPC_INVALID && + (action == TPC_ACTION_INCREASE || + action == TPC_ACTION_NO_RESTIRCTION)) { + IWL_DEBUG_RATE(mvm, "already in highest txp, stay\n"); + action = TPC_ACTION_STAY; + } + + switch (action) { + case TPC_ACTION_DECREASE: + lq_sta->lq.reduced_tpc = weak; + return true; + case TPC_ACTION_INCREASE: + lq_sta->lq.reduced_tpc = strong; + return true; + case TPC_ACTION_NO_RESTIRCTION: + lq_sta->lq.reduced_tpc = TPC_NO_REDUCTION; + return true; + case TPC_ACTION_STAY: + /* do nothing */ + break; + } + return false; +} + /* * Do rate scaling and search for new modulation mode. */ @@ -1701,27 +1948,24 @@ 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; int high_tpt = IWL_INVALID_VALUE; u32 fail_count; - s8 scale_action = 0; + enum rs_action scale_action = RS_ACTION_STAY; u16 rate_mask; u8 update_lq = 0; struct iwl_scale_tbl_info *tbl, *tbl1; - u16 rate_scale_index_msk = 0; - u8 is_green = 0; u8 active_tbl = 0; u8 done_search = 0; u16 high_low; s32 sr; u8 tid = IWL_MAX_TID_COUNT; + u8 prev_agg = lq_sta->is_agg; struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv; struct iwl_mvm_tid_data *tid_data; - - IWL_DEBUG_RATE(mvm, "rate scale calculate new rate for skb\n"); + struct rs_rate *rate; /* Send management frames and NO_ACK data using lowest rate. */ /* TODO: this could probably be improved.. */ @@ -1729,8 +1973,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, info->flags & IEEE80211_TX_CTL_NO_ACK) return; - lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; - tid = rs_get_tid(lq_sta, hdr); if ((tid != IWL_MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { @@ -1754,50 +1996,30 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); - if (is_legacy(tbl->lq_type)) - lq_sta->is_green = 0; - else - lq_sta->is_green = rs_use_green(sta); - is_green = lq_sta->is_green; + rate = &tbl->rate; + + if (prev_agg != lq_sta->is_agg) { + IWL_DEBUG_RATE(mvm, + "Aggregation changed: prev %d current %d. Update expected TPT table\n", + prev_agg, lq_sta->is_agg); + rs_set_expected_tpt_table(lq_sta, tbl); + rs_rate_scale_clear_tbl_windows(mvm, tbl); + } /* current tx rate */ index = lq_sta->last_txrate_idx; - IWL_DEBUG_RATE(mvm, "Rate scale index %d for type %d\n", index, - tbl->lq_type); - /* rates available for this association, and for modulation mode */ - rate_mask = rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); - - IWL_DEBUG_RATE(mvm, "mask 0x%04X\n", rate_mask); - - /* mask with station rate restriction */ - if (is_legacy(tbl->lq_type)) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - /* supp_rates has no CCK bits in A mode */ - rate_scale_index_msk = (u16) (rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_scale_index_msk = (u16) (rate_mask & - lq_sta->supp_rates); + rate_mask = rs_get_supported_rates(lq_sta, rate); - } else { - rate_scale_index_msk = rate_mask; - } - - if (!rate_scale_index_msk) - rate_scale_index_msk = rate_mask; - - if (!((1 << index) & rate_scale_index_msk)) { + if (!(BIT(index) & rate_mask)) { IWL_ERR(mvm, "Current Rate is not valid\n"); if (lq_sta->search_better_tbl) { /* revert to active table if search table is not valid*/ - tbl->lq_type = LQ_NONE; + rate->type = LQ_NONE; lq_sta->search_better_tbl = 0; tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - /* get "active" rate info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green); + rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); } return; } @@ -1814,6 +2036,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, index = lq_sta->max_rate_idx; update_lq = 1; window = &(tbl->win[index]); + IWL_DEBUG_RATE(mvm, + "Forcing user max rate %d\n", + index); goto lq_update; } @@ -1830,8 +2055,9 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { IWL_DEBUG_RATE(mvm, - "LQ: still below TH. succ=%d total=%d for index %d\n", - window->success_counter, window->counter, index); + "(%s: %d): Test Window: succ %d total %d\n", + rs_pretty_lq_type(rate->type), + index, window->success_counter, window->counter); /* Can't calculate this yet; not enough history */ window->average_tpt = IWL_INVALID_VALUE; @@ -1846,8 +2072,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * actual average throughput */ if (window->average_tpt != ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128)) { - IWL_ERR(mvm, - "expected_tpt should have been calculated by now\n"); window->average_tpt = ((window->success_ratio * tbl->expected_tpt[index] + 64) / 128); } @@ -1859,34 +2083,33 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * continuing to use the setup that we've been trying. */ if (window->average_tpt > lq_sta->last_tpt) { IWL_DEBUG_RATE(mvm, - "LQ: SWITCHING TO NEW TABLE suc=%d cur-tpt=%d old-tpt=%d\n", + "SWITCHING TO NEW TABLE SR: %d " + "cur-tpt %d old-tpt %d\n", window->success_ratio, window->average_tpt, lq_sta->last_tpt); - if (!is_legacy(tbl->lq_type)) - lq_sta->enable_counter = 1; - /* Swap tables; "search" becomes "active" */ lq_sta->active_tbl = active_tbl; current_tpt = window->average_tpt; /* Else poor success; go back to mode in "active" table */ } else { IWL_DEBUG_RATE(mvm, - "LQ: GOING BACK TO THE OLD TABLE suc=%d cur-tpt=%d old-tpt=%d\n", + "GOING BACK TO THE OLD TABLE: SR %d " + "cur-tpt %d old-tpt %d\n", window->success_ratio, window->average_tpt, lq_sta->last_tpt); /* Nullify "search" table */ - tbl->lq_type = LQ_NONE; + rate->type = LQ_NONE; /* Revert to "active" table */ active_tbl = lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); /* Revert to "active" rate and throughput info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + index = tbl->rate.index; current_tpt = lq_sta->last_tpt; /* Need to set up a new rate table in uCode */ @@ -1902,8 +2125,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, /* (Else) not in search of better modulation mode, try for better * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk, - tbl->lq_type); + high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; @@ -1921,118 +2143,61 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, if (high != IWL_RATE_INVALID) high_tpt = tbl->win[high].average_tpt; - scale_action = 0; - - /* Too many failures, decrease rate */ - if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { - IWL_DEBUG_RATE(mvm, - "decrease rate because of low success_ratio\n"); - scale_action = -1; - /* No throughput measured yet for adjacent rates; try increase. */ - } else if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE)) { - if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) - scale_action = 1; - else if (low != IWL_RATE_INVALID) - scale_action = 0; - } - - /* Both adjacent throughputs are measured, but neither one has better - * throughput; we're using the best rate, don't change it! */ - else if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) - scale_action = 0; - - /* At least one adjacent rate's throughput is measured, - * and may have better performance. */ - else { - /* Higher adjacent rate's throughput is measured */ - if (high_tpt != IWL_INVALID_VALUE) { - /* Higher rate has better throughput */ - if (high_tpt > current_tpt && - sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } else { - scale_action = 0; - } - - /* Lower adjacent rate's throughput is measured */ - } else if (low_tpt != IWL_INVALID_VALUE) { - /* Lower rate has better throughput */ - if (low_tpt > current_tpt) { - IWL_DEBUG_RATE(mvm, - "decrease rate because of low tpt\n"); - scale_action = -1; - } else if (sr >= IWL_RATE_INCREASE_TH) { - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if ((scale_action == -1) && (low != IWL_RATE_INVALID) && - ((sr > IWL_RATE_HIGH_TH) || - (current_tpt > (100 * tbl->expected_tpt[low])))) - scale_action = 0; + IWL_DEBUG_RATE(mvm, + "(%s: %d): cur_tpt %d SR %d low %d high %d low_tpt %d high_tpt %d\n", + rs_pretty_lq_type(rate->type), index, current_tpt, sr, + low, high, low_tpt, high_tpt); - if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= - IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) { - if (lq_sta->last_bt_traffic > - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - /* - * don't set scale_action, don't want to scale up if - * the rate scale doesn't otherwise think that is a - * good idea. - */ - } else if (lq_sta->last_bt_traffic <= - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - scale_action = -1; - } - } - lq_sta->last_bt_traffic = - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD); + scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, + current_tpt, low_tpt, high_tpt); - if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= - IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) { - /* search for a new modulation */ + /* Force a search in case BT doesn't like us being in MIMO */ + if (is_mimo(rate) && + !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) { + IWL_DEBUG_RATE(mvm, + "BT Coex forbids MIMO. Search for new config\n"); rs_stay_in_table(lq_sta, true); goto lq_update; } switch (scale_action) { - case -1: + case RS_ACTION_DOWNSCALE: /* Decrease starting rate, update uCode's rate table */ if (low != IWL_RATE_INVALID) { update_lq = 1; index = low; + } else { + IWL_DEBUG_RATE(mvm, + "At the bottom rate. Can't decrease\n"); } break; - case 1: + case RS_ACTION_UPSCALE: /* Increase starting rate, update uCode's rate table */ if (high != IWL_RATE_INVALID) { update_lq = 1; index = high; + } else { + IWL_DEBUG_RATE(mvm, + "At the top rate. Can't increase\n"); } break; - case 0: + case RS_ACTION_STAY: /* No change */ + if (lq_sta->rs_state == RS_STATE_STAY_IN_COLUMN) + update_lq = rs_tpc_perform(mvm, sta, lq_sta, tbl); + break; default: break; } - IWL_DEBUG_RATE(mvm, - "choose rate scale index %d action %d low %d high %d type %d\n", - index, scale_action, low, high, tbl->lq_type); - lq_update: /* Replace uCode's rate table for the destination station. */ - if (update_lq) - rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green); + if (update_lq) { + tbl->rate.index = index; + rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); + } rs_stay_in_table(lq_sta, false); @@ -2043,59 +2208,72 @@ lq_update: * 3) Allowing a new search */ if (!update_lq && !done_search && - !lq_sta->stay_in_tbl && window->counter) { + lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_STARTED + && window->counter) { + enum rs_column next_column; + /* Save current throughput to compare with "search" throughput*/ lq_sta->last_tpt = current_tpt; - /* Select a new "search" modulation mode to try. - * If one is found, set up the new "search" table. */ - if (is_legacy(tbl->lq_type)) - rs_move_legacy_other(mvm, lq_sta, sta, index); - else if (is_siso(tbl->lq_type)) - rs_move_siso_to_other(mvm, lq_sta, sta, index); - else if (is_mimo2(tbl->lq_type)) - rs_move_mimo2_to_other(mvm, lq_sta, sta, index); - else - WARN_ON_ONCE(1); + IWL_DEBUG_RATE(mvm, + "Start Search: update_lq %d done_search %d rs_state %d win->counter %d\n", + update_lq, done_search, lq_sta->rs_state, + window->counter); + + next_column = rs_get_next_column(mvm, lq_sta, sta, tbl); + if (next_column != RS_COLUMN_INVALID) { + int ret = rs_switch_to_column(mvm, lq_sta, sta, + next_column); + if (!ret) + lq_sta->search_better_tbl = 1; + } else { + IWL_DEBUG_RATE(mvm, + "No more columns to explore in search cycle. Go to RS_STATE_SEARCH_CYCLE_ENDED\n"); + lq_sta->rs_state = RS_STATE_SEARCH_CYCLE_ENDED; + } /* If new "search" mode was selected, set up in uCode table */ 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(mvm, tbl); /* Use new "search" start rate */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + index = tbl->rate.index; - IWL_DEBUG_RATE(mvm, - "Switch current mcs: %X index: %d\n", - tbl->current_rate, index); - rs_fill_link_cmd(mvm, lq_sta, tbl->current_rate); - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); + rs_dump_rate(mvm, &tbl->rate, + "Switch to SEARCH TABLE:"); + rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } else { done_search = 1; } } - if (done_search && !lq_sta->stay_in_tbl) { + if (done_search && lq_sta->rs_state == RS_STATE_SEARCH_CYCLE_ENDED) { /* If the "active" (non-search) mode was legacy, * and we've tried switching antennas, * but we haven't been able to try HT modes (not available), * stay with best antenna legacy modulation for a while * before next round of mode comparisons. */ tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); - if (is_legacy(tbl1->lq_type) && !sta->ht_cap.ht_supported && - lq_sta->action_counter > tbl1->max_search) { + if (is_legacy(&tbl1->rate)) { IWL_DEBUG_RATE(mvm, "LQ: STAY in legacy table\n"); - rs_set_stay_in_table(mvm, 1, lq_sta); - } + if (tid != IWL_MAX_TID_COUNT) { + tid_data = &sta_priv->tid_data[tid]; + if (tid_data->state != IWL_AGG_OFF) { + IWL_DEBUG_RATE(mvm, + "Stop aggregation on tid %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + } + rs_set_stay_in_table(mvm, 1, lq_sta); + } else { /* If we're in an HT mode, and all 3 mode switch actions * have been tried and compared, stay in this best modulation * mode for a while before next round of mode comparisons. */ - if (lq_sta->enable_counter && - (lq_sta->action_counter >= tbl1->max_search)) { if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != IWL_MAX_TID_COUNT)) { @@ -2113,7 +2291,6 @@ lq_update: } out: - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green); lq_sta->last_txrate_idx = index; } @@ -2134,13 +2311,12 @@ out: static void rs_initialize_lq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, - enum ieee80211_band band) + enum ieee80211_band band, + bool init) { struct iwl_scale_tbl_info *tbl; - int rate_idx; + struct rs_rate *rate; int i; - u32 rate; - u8 use_green = rs_use_green(sta); u8 active_tbl = 0; u8 valid_tx_ant; @@ -2149,7 +2325,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, i = lq_sta->last_txrate_idx; - valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); + valid_tx_ant = mvm->fw->valid_tx_ant; if (!lq_sta->search_better_tbl) active_tbl = lq_sta->active_tbl; @@ -2157,27 +2333,30 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); + rate = &tbl->rate; if ((i < 0) || (i >= IWL_RATE_COUNT)) i = 0; - rate = iwl_rates[i].plcp; - tbl->ant_type = first_antenna(valid_tx_ant); - rate |= tbl->ant_type << RATE_MCS_ANT_POS; - - if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) - rate |= RATE_MCS_CCK_MSK; + rate->index = i; + rate->ant = first_antenna(valid_tx_ant); + rate->sgi = false; + rate->bw = RATE_MCS_CHAN_WIDTH_20; + if (band == IEEE80211_BAND_5GHZ) + rate->type = LQ_LEGACY_A; + else + rate->type = LQ_LEGACY_G; - rs_get_tbl_info_from_mcs(rate, band, tbl, &rate_idx); - if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) - rs_toggle_antenna(valid_tx_ant, &rate, tbl); + WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); + if (rate->ant == ANT_A) + tbl->column = RS_COLUMN_LEGACY_ANT_A; + else + tbl->column = RS_COLUMN_LEGACY_ANT_B; - rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx, use_green); - tbl->current_rate = rate; rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, lq_sta, rate); + rs_fill_lq_cmd(mvm, sta, lq_sta, rate); /* TODO restore station should remember the lq cmd */ - iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true); + iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); } static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, @@ -2190,9 +2369,6 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = mvm_sta; - int rate_idx; - - IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n"); /* Get max rate if user set max rate */ if (lq_sta) { @@ -2215,36 +2391,9 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, if (rate_control_send_low(sta, mvm_sta, txrc)) return; - rate_idx = lq_sta->last_txrate_idx; - - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { - rate_idx -= IWL_FIRST_OFDM_RATE; - /* 6M and 9M shared same MCS index */ - rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; - WARN_ON_ONCE(rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO3_6M_PLCP); - if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO2_6M_PLCP) - rate_idx = rate_idx + MCS_INDEX_PER_STREAM; - info->control.rates[0].flags = IEEE80211_TX_RC_MCS; - if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; - if (lq_sta->last_rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */ - info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (lq_sta->last_rate_n_flags & RATE_HT_MCS_GF_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; - } else { - /* Check for invalid rates */ - if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || - ((sband->band == IEEE80211_BAND_5GHZ) && - (rate_idx < IWL_FIRST_OFDM_RATE))) - rate_idx = rate_lowest_index(sband, sta); - /* On valid 5 GHz rate, adjust index */ - else if (sband->band == IEEE80211_BAND_5GHZ) - rate_idx -= IWL_FIRST_OFDM_RATE; - info->control.rates[0].flags = 0; - } - info->control.rates[0].idx = rate_idx; + iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags, + info->band, &info->control.rates[0]); + info->control.rates[0].count = 1; } @@ -2261,15 +2410,141 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, return &sta_priv->lq_sta; } +static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, + int nss) +{ + u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & + (0x3 << (2 * (nss - 1))); + rx_mcs >>= (2 * (nss - 1)); + + if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7) + return IWL_RATE_MCS_7_INDEX; + else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8) + return IWL_RATE_MCS_8_INDEX; + else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9) + return IWL_RATE_MCS_9_INDEX; + + WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED); + return -1; +} + +static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, + struct ieee80211_sta_vht_cap *vht_cap, + struct iwl_lq_sta *lq_sta) +{ + int i; + int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); + + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ + if (i == IWL_RATE_MCS_9_INDEX && + sta->bandwidth == IEEE80211_STA_RX_BW_20) + continue; + + lq_sta->active_siso_rate |= BIT(i); + } + } + + if (sta->rx_nss < 2) + return; + + highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ + if (i == IWL_RATE_MCS_9_INDEX && + sta->bandwidth == IEEE80211_STA_RX_BW_20) + continue; + + lq_sta->active_mimo2_rate |= BIT(i); + } + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm, + struct iwl_mvm_frame_stats *stats) +{ + spin_lock_bh(&mvm->drv_stats_lock); + memset(stats, 0, sizeof(*stats)); + spin_unlock_bh(&mvm->drv_stats_lock); +} + +void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, + struct iwl_mvm_frame_stats *stats, + u32 rate, bool agg) +{ + u8 nss = 0, mcs = 0; + + spin_lock(&mvm->drv_stats_lock); + + if (agg) + stats->agg_frames++; + + stats->success_frames++; + + switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + stats->bw_20_frames++; + break; + case RATE_MCS_CHAN_WIDTH_40: + stats->bw_40_frames++; + break; + case RATE_MCS_CHAN_WIDTH_80: + stats->bw_80_frames++; + break; + default: + WARN_ONCE(1, "bad BW. rate 0x%x", rate); + } + + if (rate & RATE_MCS_HT_MSK) { + stats->ht_frames++; + mcs = rate & RATE_HT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; + } else if (rate & RATE_MCS_VHT_MSK) { + stats->vht_frames++; + mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + } else { + stats->legacy_frames++; + } + + if (nss == 1) + stats->siso_frames++; + else if (nss == 2) + stats->mimo2_frames++; + + if (rate & RATE_MCS_SGI_MSK) + stats->sgi_frames++; + else + stats->ngi_frames++; + + stats->last_rates[stats->last_frame_idx] = rate; + stats->last_frame_idx = (stats->last_frame_idx + 1) % + ARRAY_SIZE(stats->last_rates); + + spin_unlock(&mvm->drv_stats_lock); +} +#endif + /* * Called after adding a new station to initialize rate scaling */ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, - enum ieee80211_band band) + enum ieee80211_band band, bool init) { int i, j; struct ieee80211_hw *hw = mvm->hw; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; struct iwl_mvm_sta *sta_priv; struct iwl_lq_sta *lq_sta; struct ieee80211_supported_band *sband; @@ -2277,16 +2552,17 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; lq_sta = &sta_priv->lq_sta; + memset(lq_sta, 0, sizeof(*lq_sta)); + sband = hw->wiphy->bands[band]; 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(mvm, &lq_sta->lq_info[j]); lq_sta->flush_timer = 0; - lq_sta->supp_rates = sta->supp_rates[sband->band]; + lq_sta->last_tx = jiffies; IWL_DEBUG_RATE(mvm, "LQ: *** rate scale station global init for station %d ***\n", @@ -2298,7 +2574,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; - lq_sta->is_green = rs_use_green(sta); lq_sta->band = sband->band; /* * active legacy rates as per supported rates bitmap @@ -2308,38 +2583,50 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, for_each_set_bit(i, &supp, BITS_PER_LONG) lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); - /* - * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), - * supp_rates[] does not; shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + /* TODO: should probably account for rx_highest for both HT/VHT */ + if (!vht_cap || !vht_cap->vht_supported) { + /* active_siso_rate mask includes 9 MBits (bit 5), + * and CCK (bits 0-3), supp_rates[] does not; + * shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->is_vht = false; + } else { + rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); + lq_sta->is_vht = true; + } - /* Same here */ - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + lq_sta->max_legacy_rate_idx = find_last_bit(&lq_sta->active_legacy_rate, + BITS_PER_LONG); + lq_sta->max_siso_rate_idx = find_last_bit(&lq_sta->active_siso_rate, + BITS_PER_LONG); + lq_sta->max_mimo2_rate_idx = find_last_bit(&lq_sta->active_mimo2_rate, + BITS_PER_LONG); - IWL_DEBUG_RATE(mvm, - "SISO-RATE=%X MIMO2-RATE=%X\n", + IWL_DEBUG_RATE(mvm, "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d\n", + lq_sta->active_legacy_rate, lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate); + lq_sta->active_mimo2_rate, + lq_sta->is_vht); + IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", + lq_sta->max_legacy_rate_idx, + lq_sta->max_siso_rate_idx, + lq_sta->max_mimo2_rate_idx); /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = - first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - lq_sta->lq.dual_stream_ant_msk = - iwl_fw_valid_tx_ant(mvm->fw) & - ~first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - if (!lq_sta->lq.dual_stream_ant_msk) { - lq_sta->lq.dual_stream_ant_msk = ANT_AB; - } else if (num_of_ant(iwl_fw_valid_tx_ant(mvm->fw)) == 2) { - lq_sta->lq.dual_stream_ant_msk = - iwl_fw_valid_tx_ant(mvm->fw); - } + first_antenna(mvm->fw->valid_tx_ant); + lq_sta->lq.dual_stream_ant_msk = ANT_AB; /* as default allow aggregation for all tids */ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; @@ -2352,121 +2639,191 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->is_agg = 0; #ifdef CONFIG_MAC80211_DEBUGFS lq_sta->dbg_fixed_rate = 0; + lq_sta->dbg_fixed_txp_reduction = TPC_INVALID; +#endif +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats); #endif + rs_initialize_lq(mvm, sta, lq_sta, band, init); +} + +static void rs_rate_update(void *mvm_r, + struct ieee80211_supported_band *sband, + struct cfg80211_chan_def *chandef, + struct ieee80211_sta *sta, void *priv_sta, + u32 changed) +{ + u8 tid; + struct iwl_op_mode *op_mode = + (struct iwl_op_mode *)mvm_r; + struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); + + /* Stop any ongoing aggregations as rs starts off assuming no agg */ + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + ieee80211_stop_tx_ba_session(sta, tid); - rs_initialize_lq(mvm, sta, lq_sta, band); + iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); } -static void rs_fill_link_cmd(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta, u32 new_rate) +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, + struct iwl_lq_cmd *lq_cmd, + enum ieee80211_band band, + u32 ucode_rate) { - struct iwl_scale_tbl_info tbl_type; - int index = 0; - int rate_idx; - int repeat_rate = 0; - u8 ant_toggle_cnt = 0; - u8 use_ht_possible = 1; + struct rs_rate rate; + int i; + int num_rates = ARRAY_SIZE(lq_cmd->rs_table); + __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); + + for (i = 0; i < num_rates; i++) + lq_cmd->rs_table[i] = ucode_rate_le32; + + rs_rate_from_ucode_rate(ucode_rate, band, &rate); + + if (is_mimo(&rate)) + lq_cmd->mimo_delim = num_rates - 1; + else + lq_cmd->mimo_delim = 0; +} +#endif /* CONFIG_MAC80211_DEBUGFS */ + +static void rs_fill_rates_for_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct rs_rate *rate, + __le32 *rs_table, int *rs_table_index, + int num_rates, int num_retries, + u8 valid_tx_ant, bool toggle_ant) +{ + int i, j; + __le32 ucode_rate; + bool bottom_reached = false; + int prev_rate_idx = rate->index; + int end = LINK_QUAL_MAX_RETRY_NUM; + int index = *rs_table_index; + + for (i = 0; i < num_rates && index < end; i++) { + ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate)); + for (j = 0; j < num_retries && index < end; j++, index++) + rs_table[index] = ucode_rate; + + if (toggle_ant) + rs_toggle_antenna(valid_tx_ant, rate); + + prev_rate_idx = rate->index; + bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); + if (bottom_reached && !is_legacy(rate)) + break; + } + + if (!bottom_reached) + rate->index = prev_rate_idx; + + *rs_table_index = index; +} + +/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI + * column the rate table should look like this: + * + * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI + * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI + * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI + * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI + * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI + * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI + * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI + * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI + * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI + * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps + * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps + * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps + * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps + * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps + * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps + * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps + */ +static void rs_build_rates_table(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct rs_rate rate; + int num_rates, num_retries, index = 0; u8 valid_tx_ant = 0; struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + bool toggle_ant = false; - /* Override starting rate (index 0) if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate); + memcpy(&rate, initial_rate, sizeof(rate)); - /* Interpret new_rate (rate_n_flags) */ - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, - &tbl_type, &rate_idx); + valid_tx_ant = mvm->fw->valid_tx_ant; - /* How many times should we repeat the initial rate? */ - if (is_legacy(tbl_type.lq_type)) { - ant_toggle_cnt = 1; - repeat_rate = IWL_NUMBER_TRY; + if (is_siso(&rate)) { + num_rates = RS_INITIAL_SISO_NUM_RATES; + num_retries = RS_HT_VHT_RETRIES_PER_RATE; + } else if (is_mimo(&rate)) { + num_rates = RS_INITIAL_MIMO_NUM_RATES; + num_retries = RS_HT_VHT_RETRIES_PER_RATE; } else { - repeat_rate = min(IWL_HT_NUMBER_TRY, - LINK_QUAL_AGG_DISABLE_START_DEF - 1); - } - - lq_cmd->mimo_delim = is_mimo(tbl_type.lq_type) ? 1 : 0; - - /* Fill 1st table entry (index 0) */ - lq_cmd->rs_table[index] = cpu_to_le32(new_rate); - - if (num_of_ant(tbl_type.ant_type) == 1) - lq_cmd->single_stream_ant_msk = tbl_type.ant_type; - else if (num_of_ant(tbl_type.ant_type) == 2) - lq_cmd->dual_stream_ant_msk = tbl_type.ant_type; - /* otherwise we don't modify the existing value */ - - index++; - repeat_rate--; - if (mvm) - valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - - /* Fill rest of rate table */ - while (index < LINK_QUAL_MAX_RETRY_NUM) { - /* Repeat initial/next rate. - * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. - * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ - while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (mvm && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; - } + num_rates = RS_INITIAL_LEGACY_NUM_RATES; + num_retries = RS_LEGACY_RETRIES_PER_RATE; + toggle_ant = true; + } - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate); + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); - /* Fill next table entry */ - lq_cmd->rs_table[index] = - cpu_to_le32(new_rate); - repeat_rate--; - index++; - } + rs_get_lower_rate_down_column(lq_sta, &rate); - rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, - &rate_idx); + if (is_siso(&rate)) { + num_rates = RS_SECONDARY_SISO_NUM_RATES; + num_retries = RS_SECONDARY_SISO_RETRIES; + lq_cmd->mimo_delim = index; + } else if (is_legacy(&rate)) { + num_rates = RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = RS_LEGACY_RETRIES_PER_RATE; + } else { + WARN_ON_ONCE(1); + } + toggle_ant = true; - /* Indicate to uCode which entries might be MIMO. - * If initial rate was MIMO, this will finally end up - * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(tbl_type.lq_type)) - lq_cmd->mimo_delim = index; + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); - /* Get next rate */ - new_rate = rs_get_lower_rate(lq_sta, &tbl_type, rate_idx, - use_ht_possible); + rs_get_lower_rate_down_column(lq_sta, &rate); - /* How many times should we repeat the next rate? */ - if (is_legacy(tbl_type.lq_type)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (mvm && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &tbl_type)) - ant_toggle_cnt = 1; + num_rates = RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = RS_LEGACY_RETRIES_PER_RATE; - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = IWL_HT_NUMBER_TRY; - } + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); - /* Don't allow HT rates after next pass. - * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ - use_ht_possible = 0; +} - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate); +static void rs_fill_lq_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + u8 ant = initial_rate->ant; - /* Fill next table entry */ - lq_cmd->rs_table[index] = cpu_to_le32(new_rate); +#ifdef CONFIG_MAC80211_DEBUGFS + if (lq_sta->dbg_fixed_rate) { + rs_build_rates_table_from_fixed(mvm, lq_cmd, + lq_sta->band, + lq_sta->dbg_fixed_rate); + lq_cmd->reduced_tpc = 0; + ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> + RATE_MCS_ANT_POS; + } else +#endif + rs_build_rates_table(mvm, lq_sta, initial_rate); - index++; - repeat_rate--; - } + if (num_of_ant(ant) == 1) + lq_cmd->single_stream_ant_msk = ant; lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; @@ -2474,12 +2831,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, lq_cmd->agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - /* - * overwrite if needed, pass aggregation time limit - * to uCode in uSec - This is racy - but heh, at least it helps... - */ - if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) - lq_cmd->agg_time_limit = cpu_to_le16(1200); + if (sta) + lq_cmd->agg_time_limit = + cpu_to_le16(iwl_mvm_coex_agg_time_limit(mvm, sta)); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) @@ -2503,31 +2857,83 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, } #ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags) +int rs_pretty_print_rate(char *buf, const u32 rate) { - struct iwl_mvm *mvm; - u8 valid_tx_ant; - u8 ant_sel_tx; - mvm = lq_sta->drv; - valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - if (lq_sta->dbg_fixed_rate) { - ant_sel_tx = - ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) - >> RATE_MCS_ANT_POS); - if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { - *rate_n_flags = lq_sta->dbg_fixed_rate; - IWL_DEBUG_RATE(mvm, "Fixed rate ON\n"); - } else { - lq_sta->dbg_fixed_rate = 0; - IWL_ERR(mvm, - "Invalid antenna selection 0x%X, Valid is 0x%X\n", - ant_sel_tx, valid_tx_ant); - IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n"); - } + char *type, *bw; + u8 mcs = 0, nss = 0; + u8 ant = (rate & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS; + + if (!(rate & RATE_MCS_HT_MSK) && + !(rate & RATE_MCS_VHT_MSK)) { + int index = iwl_hwrate_to_plcp_idx(rate); + + return sprintf(buf, "Legacy | ANT: %s Rate: %s Mbps\n", + rs_pretty_ant(ant), + index == IWL_RATE_INVALID ? "BAD" : + iwl_rate_mcs[index].mbps); + } + + if (rate & RATE_MCS_VHT_MSK) { + type = "VHT"; + mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; + nss = ((rate & RATE_VHT_MCS_NSS_MSK) + >> RATE_VHT_MCS_NSS_POS) + 1; + } else if (rate & RATE_MCS_HT_MSK) { + type = "HT"; + mcs = rate & RATE_HT_MCS_INDEX_MSK; } else { - IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n"); + type = "Unknown"; /* shouldn't happen */ + } + + switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + bw = "20Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_40: + bw = "40Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_80: + bw = "80Mhz"; + break; + case RATE_MCS_CHAN_WIDTH_160: + bw = "160Mhz"; + break; + default: + bw = "BAD BW"; + } + + return sprintf(buf, "%s | ANT: %s BW: %s MCS: %d NSS: %d %s%s%s%s%s\n", + type, rs_pretty_ant(ant), bw, mcs, nss, + (rate & RATE_MCS_SGI_MSK) ? "SGI " : "NGI ", + (rate & RATE_MCS_HT_STBC_MSK) ? "STBC " : "", + (rate & RATE_MCS_LDPC_MSK) ? "LDPC " : "", + (rate & RATE_MCS_BF_MSK) ? "BF " : "", + (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); +} + +/** + * Program the device to use fixed rate for frame transmit + * This is for debugging/testing only + * once the device start use fixed rate, we need to reload the module + * to being back the normal operation. + */ +static void rs_program_fix_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta) +{ + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + struct rs_rate rate; + rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate, + lq_sta->band, &rate); + rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate); + iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false); } } @@ -2540,7 +2946,6 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, size_t buf_size; u32 parsed_rate; - mvm = lq_sta->drv; memset(buf, 0, sizeof(buf)); buf_size = min(count, sizeof(buf) - 1); @@ -2563,44 +2968,45 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, char *buff; int desc = 0; int i = 0; - int index = 0; ssize_t ret; struct iwl_lq_sta *lq_sta = file->private_data; struct iwl_mvm *mvm; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - + struct rs_rate *rate = &tbl->rate; mvm = lq_sta->drv; - buff = kmalloc(1024, GFP_KERNEL); + buff = kmalloc(2048, GFP_KERNEL); if (!buff) return -ENOMEM; desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); - desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%lX\n", lq_sta->total_failed, lq_sta->total_success, lq_sta->active_legacy_rate); desc += sprintf(buff+desc, "fixed rate 0x%X\n", lq_sta->dbg_fixed_rate); desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (iwl_fw_valid_tx_ant(mvm->fw) & ANT_A) ? "ANT_A," : "", - (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "", - (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : ""); + (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "", + (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "", + (mvm->fw->valid_tx_ant & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); - if (is_Ht(tbl->lq_type)) { + (is_legacy(rate)) ? "legacy" : + is_vht(rate) ? "VHT" : "HT"); + if (!is_legacy(rate)) { desc += sprintf(buff+desc, " %s", - (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); + (is_siso(rate)) ? "SISO" : "MIMO2"); desc += sprintf(buff+desc, " %s", - (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += sprintf(buff+desc, " %s %s %s\n", - (tbl->is_SGI) ? "SGI" : "", - (lq_sta->is_green) ? "GF enabled" : "", - (lq_sta->is_agg) ? "AGG on" : ""); + (is_ht20(rate)) ? "20MHz" : + (is_ht40(rate)) ? "40MHz" : + (is_ht80(rate)) ? "80Mhz" : "BAD BW"); + desc += sprintf(buff+desc, " %s %s\n", + (rate->sgi) ? "SGI" : "NGI", + (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); desc += sprintf(buff+desc, - "general: flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + "general: flags=0x%X mimo-d=%d s-ant=0x%x d-ant=0x%x\n", lq_sta->lq.flags, lq_sta->lq.mimo_delim, lq_sta->lq.single_stream_ant_msk, @@ -2612,6 +3018,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, lq_sta->lq.agg_disable_start_th, lq_sta->lq.agg_frame_cnt_limit); + desc += sprintf(buff+desc, "reduced tpc=%d\n", lq_sta->lq.reduced_tpc); desc += sprintf(buff+desc, "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", lq_sta->lq.initial_rate_index[0], @@ -2620,19 +3027,10 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, lq_sta->lq.initial_rate_index[3]); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - index = iwl_hwrate_to_plcp_idx( - le32_to_cpu(lq_sta->lq.rs_table[i])); - if (is_legacy(tbl->lq_type)) { - desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i]), - iwl_rate_mcs[index].mbps); - } else { - desc += sprintf(buff+desc, - " rate[%d] 0x%X %smbps (%s)\n", - i, le32_to_cpu(lq_sta->lq.rs_table[i]), - iwl_rate_mcs[index].mbps, - iwl_rate_mcs[index].mcs); - } + u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]); + + desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r); + desc += rs_pretty_print_rate(buff+desc, r); } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); @@ -2653,7 +3051,8 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, int desc = 0; int i, j; ssize_t ret; - + struct iwl_scale_tbl_info *tbl; + struct rs_rate *rate; struct iwl_lq_sta *lq_sta = file->private_data; buff = kmalloc(1024, GFP_KERNEL); @@ -2661,21 +3060,24 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, return -ENOMEM; for (i = 0; i < LQ_SIZE; i++) { + tbl = &(lq_sta->lq_info[i]); + rate = &tbl->rate; desc += sprintf(buff+desc, - "%s type=%d SGI=%d HT40=%d DUP=0 GF=%d\n" - "rate=0x%X\n", + "%s type=%d SGI=%d BW=%s DUP=0\n" + "index=%d\n", lq_sta->active_tbl == i ? "*" : "x", - lq_sta->lq_info[i].lq_type, - lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_ht40, - lq_sta->is_green, - lq_sta->lq_info[i].current_rate); + rate->type, + rate->sgi, + is_ht20(rate) ? "20Mhz" : + is_ht40(rate) ? "40Mhz" : + is_ht80(rate) ? "80Mhz" : "ERR", + rate->index); for (j = 0; j < IWL_RATE_COUNT; j++) { desc += sprintf(buff+desc, "counter=%d success=%d %%=%d\n", - lq_sta->lq_info[i].win[j].counter, - lq_sta->lq_info[i].win[j].success_counter, - lq_sta->lq_info[i].win[j].success_ratio); + tbl->win[j].counter, + tbl->win[j].success_counter, + tbl->win[j].success_ratio); } } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); @@ -2689,6 +3091,94 @@ static const struct file_operations rs_sta_dbgfs_stats_table_ops = { .llseek = default_llseek, }; +static ssize_t rs_sta_dbgfs_drv_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + static const char * const column_name[] = { + [RS_COLUMN_LEGACY_ANT_A] = "LEGACY_ANT_A", + [RS_COLUMN_LEGACY_ANT_B] = "LEGACY_ANT_B", + [RS_COLUMN_SISO_ANT_A] = "SISO_ANT_A", + [RS_COLUMN_SISO_ANT_B] = "SISO_ANT_B", + [RS_COLUMN_SISO_ANT_A_SGI] = "SISO_ANT_A_SGI", + [RS_COLUMN_SISO_ANT_B_SGI] = "SISO_ANT_B_SGI", + [RS_COLUMN_MIMO2] = "MIMO2", + [RS_COLUMN_MIMO2_SGI] = "MIMO2_SGI", + }; + + static const char * const rate_name[] = { + [IWL_RATE_1M_INDEX] = "1M", + [IWL_RATE_2M_INDEX] = "2M", + [IWL_RATE_5M_INDEX] = "5.5M", + [IWL_RATE_11M_INDEX] = "11M", + [IWL_RATE_6M_INDEX] = "6M|MCS0", + [IWL_RATE_9M_INDEX] = "9M", + [IWL_RATE_12M_INDEX] = "12M|MCS1", + [IWL_RATE_18M_INDEX] = "18M|MCS2", + [IWL_RATE_24M_INDEX] = "24M|MCS3", + [IWL_RATE_36M_INDEX] = "36M|MCS4", + [IWL_RATE_48M_INDEX] = "48M|MCS5", + [IWL_RATE_54M_INDEX] = "54M|MCS6", + [IWL_RATE_MCS_7_INDEX] = "MCS7", + [IWL_RATE_MCS_8_INDEX] = "MCS8", + [IWL_RATE_MCS_9_INDEX] = "MCS9", + }; + + char *buff, *pos, *endpos; + int col, rate; + ssize_t ret; + struct iwl_lq_sta *lq_sta = file->private_data; + struct rs_rate_stats *stats; + static const size_t bufsz = 1024; + + buff = kmalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + pos = buff; + endpos = pos + bufsz; + + pos += scnprintf(pos, endpos - pos, "COLUMN,"); + for (rate = 0; rate < IWL_RATE_COUNT; rate++) + pos += scnprintf(pos, endpos - pos, "%s,", rate_name[rate]); + pos += scnprintf(pos, endpos - pos, "\n"); + + for (col = 0; col < RS_COLUMN_COUNT; col++) { + pos += scnprintf(pos, endpos - pos, + "%s,", column_name[col]); + + for (rate = 0; rate < IWL_RATE_COUNT; rate++) { + stats = &(lq_sta->tx_stats[col][rate]); + pos += scnprintf(pos, endpos - pos, + "%llu/%llu,", + stats->success, + stats->total); + } + pos += scnprintf(pos, endpos - pos, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, pos - buff); + kfree(buff); + return ret; +} + +static ssize_t rs_sta_dbgfs_drv_tx_stats_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + memset(lq_sta->tx_stats, 0, sizeof(lq_sta->tx_stats)); + + return count; +} + +static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { + .read = rs_sta_dbgfs_drv_tx_stats_read, + .write = rs_sta_dbgfs_drv_tx_stats_write, + .open = simple_open, + .llseek = default_llseek, +}; + static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) { struct iwl_lq_sta *lq_sta = mvm_sta; @@ -2698,9 +3188,15 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) lq_sta->rs_sta_dbgfs_stats_table_file = debugfs_create_file("rate_stats_table", S_IRUSR, dir, lq_sta, &rs_sta_dbgfs_stats_table_ops); + lq_sta->rs_sta_dbgfs_drv_tx_stats_file = + debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops); lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, &lq_sta->tx_agg_tid_en); + lq_sta->rs_sta_dbgfs_reduced_txp_file = + debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, + &lq_sta->dbg_fixed_txp_reduction); } static void rs_remove_debugfs(void *mvm, void *mvm_sta) @@ -2708,7 +3204,9 @@ static void rs_remove_debugfs(void *mvm, void *mvm_sta) struct iwl_lq_sta *lq_sta = mvm_sta; debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file); debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file); } #endif @@ -2723,8 +3221,8 @@ static void rs_rate_init_stub(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta) { } -static struct rate_control_ops rs_mvm_ops = { - .module = NULL, + +static const struct rate_control_ops rs_mvm_ops = { .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, @@ -2733,6 +3231,7 @@ static struct rate_control_ops rs_mvm_ops = { .free = rs_free, .alloc_sta = rs_alloc_sta, .free_sta = rs_free_sta, + .rate_update = rs_rate_update, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = rs_add_debugfs, .remove_sta_debugfs = rs_remove_debugfs, @@ -2765,13 +3264,13 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, if (enable) { if (mvmsta->tx_protection == 0) - lq->flags |= LQ_FLAG_SET_STA_TLC_RTS_MSK; + lq->flags |= LQ_FLAG_USE_RTS_MSK; mvmsta->tx_protection++; } else { mvmsta->tx_protection--; if (mvmsta->tx_protection == 0) - lq->flags &= ~LQ_FLAG_SET_STA_TLC_RTS_MSK; + lq->flags &= ~LQ_FLAG_USE_RTS_MSK; } - return iwl_mvm_send_lq_cmd(mvm, lq, CMD_ASYNC, false); + return iwl_mvm_send_lq_cmd(mvm, lq, false); } diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 335cf168290..374a83d7db2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 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 @@ -35,9 +35,11 @@ #include "iwl-trans.h" struct iwl_rs_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_vht_siso; + u8 plcp_vht_mimo2; u8 prev_rs; /* previous rate used in rs algo */ u8 next_rs; /* next rate used in rs algo */ }; @@ -83,35 +85,52 @@ enum { #define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) -/* uCode API values for OFDM high-throughput (HT) bit rates */ +/* uCode API values for HT/VHT bit rates */ enum { - IWL_RATE_SISO_6M_PLCP = 0, - IWL_RATE_SISO_12M_PLCP = 1, - IWL_RATE_SISO_18M_PLCP = 2, - IWL_RATE_SISO_24M_PLCP = 3, - IWL_RATE_SISO_36M_PLCP = 4, - IWL_RATE_SISO_48M_PLCP = 5, - IWL_RATE_SISO_54M_PLCP = 6, - IWL_RATE_SISO_60M_PLCP = 7, - IWL_RATE_MIMO2_6M_PLCP = 0x8, - IWL_RATE_MIMO2_12M_PLCP = 0x9, - IWL_RATE_MIMO2_18M_PLCP = 0xa, - IWL_RATE_MIMO2_24M_PLCP = 0xb, - IWL_RATE_MIMO2_36M_PLCP = 0xc, - IWL_RATE_MIMO2_48M_PLCP = 0xd, - IWL_RATE_MIMO2_54M_PLCP = 0xe, - IWL_RATE_MIMO2_60M_PLCP = 0xf, - IWL_RATE_MIMO3_6M_PLCP = 0x10, - IWL_RATE_MIMO3_12M_PLCP = 0x11, - IWL_RATE_MIMO3_18M_PLCP = 0x12, - IWL_RATE_MIMO3_24M_PLCP = 0x13, - IWL_RATE_MIMO3_36M_PLCP = 0x14, - IWL_RATE_MIMO3_48M_PLCP = 0x15, - IWL_RATE_MIMO3_54M_PLCP = 0x16, - IWL_RATE_MIMO3_60M_PLCP = 0x17, - IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_HT_SISO_MCS_0_PLCP = 0, + IWL_RATE_HT_SISO_MCS_1_PLCP = 1, + IWL_RATE_HT_SISO_MCS_2_PLCP = 2, + IWL_RATE_HT_SISO_MCS_3_PLCP = 3, + IWL_RATE_HT_SISO_MCS_4_PLCP = 4, + IWL_RATE_HT_SISO_MCS_5_PLCP = 5, + IWL_RATE_HT_SISO_MCS_6_PLCP = 6, + IWL_RATE_HT_SISO_MCS_7_PLCP = 7, + IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8, + IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9, + IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA, + IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB, + IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC, + IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD, + IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE, + IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF, + IWL_RATE_VHT_SISO_MCS_0_PLCP = 0, + IWL_RATE_VHT_SISO_MCS_1_PLCP = 1, + IWL_RATE_VHT_SISO_MCS_2_PLCP = 2, + IWL_RATE_VHT_SISO_MCS_3_PLCP = 3, + IWL_RATE_VHT_SISO_MCS_4_PLCP = 4, + IWL_RATE_VHT_SISO_MCS_5_PLCP = 5, + IWL_RATE_VHT_SISO_MCS_6_PLCP = 6, + IWL_RATE_VHT_SISO_MCS_7_PLCP = 7, + IWL_RATE_VHT_SISO_MCS_8_PLCP = 8, + IWL_RATE_VHT_SISO_MCS_9_PLCP = 9, + IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10, + IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11, + IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12, + IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13, + IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14, + IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15, + IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16, + IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17, + IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18, + IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19, + IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, }; #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) @@ -136,30 +155,15 @@ enum { #define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ #define IWL_RATE_HIGH_TH 10880 /* 85% */ #define IWL_RATE_INCREASE_TH 6400 /* 50% */ -#define IWL_RATE_DECREASE_TH 1920 /* 15% */ +#define RS_SR_FORCE_DECREASE 1920 /* 15% */ +#define RS_SR_NO_DECREASE 10880 /* 85% */ -/* possible actions when in legacy mode */ -#define IWL_LEGACY_SWITCH_ANTENNA1 0 -#define IWL_LEGACY_SWITCH_ANTENNA2 1 -#define IWL_LEGACY_SWITCH_SISO 2 -#define IWL_LEGACY_SWITCH_MIMO2 3 - -/* possible actions when in siso mode */ -#define IWL_SISO_SWITCH_ANTENNA1 0 -#define IWL_SISO_SWITCH_ANTENNA2 1 -#define IWL_SISO_SWITCH_MIMO2 2 -#define IWL_SISO_SWITCH_GI 3 - -/* possible actions when in mimo mode */ -#define IWL_MIMO2_SWITCH_ANTENNA1 0 -#define IWL_MIMO2_SWITCH_ANTENNA2 1 -#define IWL_MIMO2_SWITCH_SISO_A 2 -#define IWL_MIMO2_SWITCH_SISO_B 3 -#define IWL_MIMO2_SWITCH_GI 4 - -#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI - -#define IWL_ACTION_LIMIT 3 /* # possible actions */ +#define TPC_SR_FORCE_INCREASE 9600 /* 75% */ +#define TPC_SR_NO_INCREASE 10880 /* 85% */ +#define TPC_TX_POWER_STEP 3 +#define TPC_MAX_REDUCTION 15 +#define TPC_NO_REDUCTION 0 +#define TPC_INVALID 0xff #define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ #define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) @@ -188,20 +192,54 @@ enum { enum iwl_table_type { LQ_NONE, - LQ_G, /* legacy types */ - LQ_A, - LQ_SISO, /* high-throughput types */ - LQ_MIMO2, + LQ_LEGACY_G, /* legacy types */ + LQ_LEGACY_A, + LQ_HT_SISO, /* HT types */ + LQ_HT_MIMO2, + LQ_VHT_SISO, /* VHT types */ + LQ_VHT_MIMO2, LQ_MAX, }; -#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) -#define is_siso(tbl) ((tbl) == LQ_SISO) -#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo(tbl) is_mimo2(tbl) -#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_A) -#define is_g_and(tbl) ((tbl) == LQ_G) +struct rs_rate { + int index; + enum iwl_table_type type; + u8 ant; + u32 bw; + bool sgi; +}; + + +#define is_type_legacy(type) (((type) == LQ_LEGACY_G) || \ + ((type) == LQ_LEGACY_A)) +#define is_type_ht_siso(type) ((type) == LQ_HT_SISO) +#define is_type_ht_mimo2(type) ((type) == LQ_HT_MIMO2) +#define is_type_vht_siso(type) ((type) == LQ_VHT_SISO) +#define is_type_vht_mimo2(type) ((type) == LQ_VHT_MIMO2) +#define is_type_siso(type) (is_type_ht_siso(type) || is_type_vht_siso(type)) +#define is_type_mimo2(type) (is_type_ht_mimo2(type) || is_type_vht_mimo2(type)) +#define is_type_mimo(type) (is_type_mimo2(type)) +#define is_type_ht(type) (is_type_ht_siso(type) || is_type_ht_mimo2(type)) +#define is_type_vht(type) (is_type_vht_siso(type) || is_type_vht_mimo2(type)) +#define is_type_a_band(type) ((type) == LQ_LEGACY_A) +#define is_type_g_band(type) ((type) == LQ_LEGACY_G) + +#define is_legacy(rate) is_type_legacy((rate)->type) +#define is_ht_siso(rate) is_type_ht_siso((rate)->type) +#define is_ht_mimo2(rate) is_type_ht_mimo2((rate)->type) +#define is_vht_siso(rate) is_type_vht_siso((rate)->type) +#define is_vht_mimo2(rate) is_type_vht_mimo2((rate)->type) +#define is_siso(rate) is_type_siso((rate)->type) +#define is_mimo2(rate) is_type_mimo2((rate)->type) +#define is_mimo(rate) is_type_mimo((rate)->type) +#define is_ht(rate) is_type_ht((rate)->type) +#define is_vht(rate) is_type_vht((rate)->type) +#define is_a_band(rate) is_type_a_band((rate)->type) +#define is_g_band(rate) is_type_g_band((rate)->type) + +#define is_ht20(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_20) +#define is_ht40(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_40) +#define is_ht80(rate) ((rate)->bw == RATE_MCS_CHAN_WIDTH_80) #define IWL_MAX_MCS_DISPLAY_SIZE 12 @@ -219,7 +257,30 @@ struct iwl_rate_scale_data { s32 success_ratio; /* per-cent * 128 */ s32 counter; /* number of frames attempted */ s32 average_tpt; /* success ratio * expected throughput */ - unsigned long stamp; +}; + +/* Possible Tx columns + * Tx Column = a combo of legacy/siso/mimo x antenna x SGI + */ +enum rs_column { + RS_COLUMN_LEGACY_ANT_A = 0, + RS_COLUMN_LEGACY_ANT_B, + 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, + RS_COLUMN_MIMO2_SGI, + + RS_COLUMN_LAST = RS_COLUMN_MIMO2_SGI, + RS_COLUMN_COUNT = RS_COLUMN_LAST + 1, + RS_COLUMN_INVALID, +}; + +/* Packet stats per rate */ +struct rs_rate_stats { + u64 success; + u64 total; }; /** @@ -229,15 +290,18 @@ struct iwl_rate_scale_data { * one for "active", and one for "search". */ struct iwl_scale_tbl_info { - enum iwl_table_type lq_type; - u8 ant_type; - u8 is_SGI; /* 1 = short guard interval */ - u8 is_ht40; /* 1 = 40 MHz channel width */ - u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ - u8 max_search; /* maximun number of tables we can search */ - s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ + struct rs_rate rate; + enum rs_column column; + const u16 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ + /* per txpower-reduction history */ + struct iwl_rate_scale_data tpc_win[TPC_MAX_REDUCTION + 1]; +}; + +enum { + RS_STATE_SEARCH_CYCLE_STARTED, + RS_STATE_SEARCH_CYCLE_ENDED, + RS_STATE_STAY_IN_COLUMN, }; /** @@ -247,8 +311,7 @@ struct iwl_scale_tbl_info { */ struct iwl_lq_sta { u8 active_tbl; /* index of active table, range 0-1 */ - u8 enable_counter; /* indicates HT mode */ - u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 rs_state; /* RS_STATE_* */ u8 search_better_tbl; /* 1: currently trying alternate mode */ s32 last_tpt; @@ -261,15 +324,25 @@ struct iwl_lq_sta { u32 total_success; /* total successful frames, any/all rates */ u64 flush_timer; /* time staying in mode before new search */ - u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; + u32 visited_columns; /* Bitmask marking which Tx columns were + * explored during a search cycle + */ + u64 last_tx; + bool is_vht; enum ieee80211_band band; + struct rs_rate_stats tx_stats[RS_COLUMN_COUNT][IWL_RATE_COUNT]; + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - u32 supp_rates; - u16 active_legacy_rate; - u16 active_siso_rate; - u16 active_mimo2_rate; + unsigned long active_legacy_rate; + unsigned long active_siso_rate; + unsigned long active_mimo2_rate; + + /* Highest rate per Tx mode */ + u8 max_legacy_rate_idx; + u8 max_siso_rate_idx; + u8 max_mimo2_rate_idx; + s8 max_rate_idx; /* Max rate set by user */ u8 missed_rate_counter; @@ -279,8 +352,11 @@ struct iwl_lq_sta { #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_scale_table_file; struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_drv_tx_stats_file; struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + struct dentry *rs_sta_dbgfs_reduced_txp_file; u32 dbg_fixed_rate; + u8 dbg_fixed_txp_reduction; #endif struct iwl_mvm *drv; @@ -290,33 +366,14 @@ struct iwl_lq_sta { u32 last_rate_n_flags; /* packets destined for this STA are aggregated */ u8 is_agg; - /* BT traffic this sta was last updated in */ - u8 last_bt_traffic; -}; -enum iwl_bt_coex_profile_traffic_load { - IWL_BT_COEX_TRAFFIC_LOAD_NONE = 0, - IWL_BT_COEX_TRAFFIC_LOAD_LOW = 1, - IWL_BT_COEX_TRAFFIC_LOAD_HIGH = 2, - IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS = 3, -/* - * There are no more even though below is a u8, the - * indication from the BT device only has two bits. - */ + /* tx power reduce for this sta */ + int tpc_reduce; }; - -static inline u8 num_of_ant(u8 mask) -{ - return !!((mask) & ANT_A) + - !!((mask) & ANT_B) + - !!((mask) & ANT_C); -} - /* Initialize station's rate scaling information after adding station */ -extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_band band); +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + enum ieee80211_band band, bool init); /** * iwl_rate_control_register - Register the rate control algorithm callbacks @@ -328,7 +385,7 @@ extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, * ieee80211_register_hw * */ -extern int iwl_mvm_rate_control_register(void); +int iwl_mvm_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks @@ -336,7 +393,7 @@ extern int iwl_mvm_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void iwl_mvm_rate_control_unregister(void); +void iwl_mvm_rate_control_unregister(void); struct iwl_mvm_sta; diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 2a8cb5a6053..cf7276967ac 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -60,7 +60,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include "iwl-trans.h" - #include "mvm.h" #include "fw-api.h" @@ -77,6 +76,15 @@ int iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, memcpy(&mvm->last_phy_info, pkt->data, sizeof(mvm->last_phy_info)); mvm->ampdu_ref++; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { + spin_lock(&mvm->drv_stats_lock); + mvm->drv_rx_stats.ampdu_count++; + spin_unlock(&mvm->drv_stats_lock); + } +#endif + return 0; } @@ -121,48 +129,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); - ieee80211_rx_ni(mvm->hw, skb); -} - -static void iwl_mvm_calc_rssi(struct iwl_mvm *mvm, - struct iwl_rx_phy_info *phy_info, - struct ieee80211_rx_status *rx_status) -{ - int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; - int rssi_all_band_a, rssi_all_band_b; - u32 agc_a, agc_b, max_agc; - u32 val; - - val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_AGC_IDX]); - agc_a = (val & IWL_OFDM_AGC_A_MSK) >> IWL_OFDM_AGC_A_POS; - agc_b = (val & IWL_OFDM_AGC_B_MSK) >> IWL_OFDM_AGC_B_POS; - max_agc = max_t(u32, agc_a, agc_b); - - val = le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_RSSI_AB_IDX]); - rssi_a = (val & IWL_OFDM_RSSI_INBAND_A_MSK) >> IWL_OFDM_RSSI_A_POS; - rssi_b = (val & IWL_OFDM_RSSI_INBAND_B_MSK) >> IWL_OFDM_RSSI_B_POS; - rssi_all_band_a = (val & IWL_OFDM_RSSI_ALLBAND_A_MSK) >> - IWL_OFDM_RSSI_ALLBAND_A_POS; - rssi_all_band_b = (val & IWL_OFDM_RSSI_ALLBAND_B_MSK) >> - IWL_OFDM_RSSI_ALLBAND_B_POS; - - /* - * dBm = rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. - */ - rssi_a_dbm = rssi_a - IWL_RSSI_OFFSET - agc_a; - rssi_b_dbm = rssi_b - IWL_RSSI_OFFSET - agc_b; - max_rssi_dbm = max_t(int, rssi_a_dbm, rssi_b_dbm); - - IWL_DEBUG_STATS(mvm, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", - rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); - - rx_status->signal = max_rssi_dbm; - rx_status->chains = (le16_to_cpu(phy_info->phy_flags) & - RX_RES_PHY_FLAGS_ANTENNA) - >> RX_RES_PHY_FLAGS_ANTENNA_POS; - rx_status->chain_signal[0] = rssi_a_dbm; - rx_status->chain_signal[1] = rssi_b_dbm; + ieee80211_rx(mvm->hw, skb); } /* @@ -251,6 +218,12 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, stats->flag |= RX_FLAG_DECRYPTED; return 0; + case RX_MPDU_RES_STATUS_SEC_EXT_ENC: + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) + return -1; + stats->flag |= RX_FLAG_DECRYPTED; + return 0; + default: IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); } @@ -300,10 +273,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } + /* + * Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); - return 0; + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; } /* This will be used in several places later */ @@ -324,10 +301,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, */ /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_RX_ENERGY_API) - iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status); - else - iwl_mvm_calc_rssi(mvm, phy_info, &rx_status); + iwl_mvm_get_signal_strength(mvm, phy_info, &rx_status); IWL_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal, (unsigned long long)rx_status.mactime); @@ -354,31 +328,45 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, rx_status.flag |= RX_FLAG_40MHZ; break; case RATE_MCS_CHAN_WIDTH_80: - rx_status.flag |= RX_FLAG_80MHZ; + rx_status.vht_flag |= RX_VHT_FLAG_80MHZ; break; case RATE_MCS_CHAN_WIDTH_160: - rx_status.flag |= RX_FLAG_160MHZ; + rx_status.vht_flag |= RX_VHT_FLAG_160MHZ; break; } if (rate_n_flags & RATE_MCS_SGI_MSK) rx_status.flag |= RX_FLAG_SHORT_GI; if (rate_n_flags & RATE_HT_MCS_GF_MSK) rx_status.flag |= RX_FLAG_HT_GF; + if (rate_n_flags & RATE_MCS_LDPC_MSK) + rx_status.flag |= RX_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK) { + u8 stbc = (rate_n_flags & RATE_MCS_HT_STBC_MSK) >> + RATE_MCS_STBC_POS; rx_status.flag |= RX_FLAG_HT; rx_status.rate_idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK; + rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT; } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + u8 stbc = (rate_n_flags & RATE_MCS_VHT_STBC_MSK) >> + RATE_MCS_STBC_POS; rx_status.vht_nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; rx_status.rate_idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; rx_status.flag |= RX_FLAG_VHT; + rx_status.flag |= stbc << RX_FLAG_STBC_SHIFT; + if (rate_n_flags & RATE_MCS_BF_MSK) + rx_status.vht_flag |= RX_VHT_FLAG_BF; } else { rx_status.rate_idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, rx_status.band); } +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags, + rx_status.flag & RX_FLAG_AMPDU_DETAILS); +#endif iwl_mvm_pass_packet_to_mac80211(mvm, hdr, len, ampdu_status, rxb, &rx_status); return 0; @@ -422,6 +410,27 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac, mvmvif->bf_data.ave_beacon_signal = sig; + /* BT Coex */ + if (mvmvif->bf_data.bt_coex_min_thold != + mvmvif->bf_data.bt_coex_max_thold) { + last_event = mvmvif->bf_data.last_bt_coex_event; + if (sig > mvmvif->bf_data.bt_coex_max_thold && + (last_event <= mvmvif->bf_data.bt_coex_min_thold || + last_event == 0)) { + mvmvif->bf_data.last_bt_coex_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", + sig); + iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); + } else if (sig < mvmvif->bf_data.bt_coex_min_thold && + (last_event >= mvmvif->bf_data.bt_coex_max_thold || + last_event == 0)) { + mvmvif->bf_data.last_bt_coex_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", + sig); + iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); + } + } + if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) return; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 621fb71f282..eac2b424f6a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -71,11 +71,25 @@ #define IWL_PLCP_QUIET_THRESH 1 #define IWL_ACTIVE_QUIET_TIME 10 +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) { u16 rx_chain; - u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw); + u8 rx_ant; + if (mvm->scan_rx_ant != ANT_NONE) + rx_ant = mvm->scan_rx_ant; + else + rx_ant = mvm->fw->valid_rx_ant; rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; @@ -83,22 +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) -{ - if (vif->bss_conf.assoc) - return cpu_to_le32(200 * 1024); - else - return 0; -} - -static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif) -{ - if (vif->bss_conf.assoc) - return cpu_to_le32(vif->bss_conf.beacon_int); - else - return 0; -} - static inline __le32 iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req) { @@ -115,7 +113,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, u32 tx_ant; mvm->scan_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw), + iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, mvm->scan_last_antenna_idx); tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; @@ -133,11 +131,12 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, * request. */ static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd, - struct cfg80211_scan_request *req) + struct cfg80211_scan_request *req, + int first) { int fw_idx, req_idx; - for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx > 0; + for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first; req_idx--, fw_idx++) { cmd->direct_scan[fw_idx].id = WLAN_EID_SSID; cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len; @@ -153,9 +152,9 @@ static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd, * just to notify that this scan is active and not passive. * In order to notify the FW of the number of SSIDs we wish to scan (including * the zero-length one), we need to set the corresponding bits in chan->type, - * one for each SSID, and set the active bit (first). The first SSID is already - * included in the probe template, so we need to set only req->n_ssids - 1 bits - * in addition to the first bit. + * one for each SSID, and set the active bit (first). If the first SSID is + * already included in the probe template, so we need to set only + * req->n_ssids - 1 bits in addition to the first bit. */ static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids) { @@ -170,22 +169,26 @@ 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) + struct cfg80211_scan_request *req, + 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); for (i = 0; i < cmd->channel_count; i++) { chan->channel = cpu_to_le16(req->channels[i]->hw_value); - chan->type = cpu_to_le32(BIT(req->n_ssids) - 1); - if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) + 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++; } @@ -252,6 +255,49 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta, return (u16)len; } +static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *global_bound = data; + + 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); + + if (!global_bound) + goto not_bound; + + params->suspend_time = 100; + params->max_out_time = 600; + + if (iwl_mvm_low_latency(mvm)) { + params->suspend_time = 250; + params->max_out_time = 250; + } + +not_bound: + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + 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, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) @@ -260,7 +306,6 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, .id = SCAN_REQUEST_CMD, .len = { 0, }, .data = { mvm->scan_cmd, }, - .flags = CMD_SYNC, .dataflags = { IWL_HCMD_DFL_NOCOPY, }, }; struct iwl_scan_cmd *cmd = mvm->scan_cmd; @@ -268,9 +313,15 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, 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); + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(mvm->scan_cmd == NULL)) + return -ENOMEM; IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n"); mvm->scan_status = IWL_MVM_SCAN_OS; @@ -282,8 +333,13 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, 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); - cmd->suspend_time = iwl_mvm_scan_suspend_time(vif); + + 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); @@ -302,16 +358,19 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, if (req->n_ssids > 0) { cmd->passive2active = cpu_to_le16(1); cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE; - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + if (basic_ssid) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; + } } else { cmd->passive2active = 0; cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE; } - iwl_mvm_scan_fill_ssids(cmd, req); + iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0); - cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL); + cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL | + TX_CMD_FLG_BT_DIS); cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id; cmd->tx_cmd.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); cmd->tx_cmd.rate_n_flags = @@ -326,7 +385,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); + 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) + @@ -368,12 +427,32 @@ 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); + + return 0; +} + +int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_sched_scan_results *notif = (void *)pkt->data; + + if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) { + IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); + ieee80211_sched_scan_results(mvm->hw); + } + return 0; } @@ -415,7 +494,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, @@ -423,27 +502,449 @@ 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 0; + } iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort, scan_abort_notif, ARRAY_SIZE(scan_abort_notif), iwl_mvm_scan_abort_notif, NULL); - ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, - CMD_SYNC | CMD_SEND_IN_RFKILL, 0, NULL); + ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, 0, 0, NULL); if (ret) { IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret); - /* mac80211's state will be cleaned in the fw_restart flow */ + /* mac80211's state will be cleaned in the nic_restart flow */ 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, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data; + + /* scan status must be locked for proper checking */ + lockdep_assert_held(&mvm->mutex); + + IWL_DEBUG_SCAN(mvm, + "Scheduled scan completed, status %s EBS status %s:%d\n", + scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? + "completed" : "aborted", scan_notif->ebs_status == + IWL_SCAN_EBS_SUCCESS ? "success" : "failed", + scan_notif->ebs_status); + + + /* 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); + } + + mvm->last_ebs_successful = !scan_notif->ebs_status; + + return 0; +} + +static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sched_scan_ies *ies, + enum ieee80211_band band, + struct iwl_tx_cmd *cmd, + u8 *data) +{ + u16 cmd_len; + + cmd->tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL); + cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); + cmd->sta_id = mvm->aux_sta.sta_id; + + cmd->rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, band, false); + + cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data, + vif->addr, + 1, NULL, 0, + ies->ie[band], ies->len[band], + SCAN_OFFLOAD_PROBE_REQ_SIZE); + cmd->len = cpu_to_le16(cmd_len); +} + +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_mvm_scan_params *params) +{ + scan->channel_count = req->n_channels; + scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); + 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 = 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) +{ + int i; + + for (i = 0; i < PROBE_OPTION_MAX; i++) { + if (!ssid_list[i].len) + break; + if (ssid_list[i].len == ssid_len && + !memcmp(ssid_list->ssid, ssid, ssid_len)) + return i; + } + return -1; +} + +static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, + struct iwl_scan_offload_cmd *scan, + u32 *ssid_bitmap) +{ + int i, j; + int index; + + /* + * copy SSIDs from match list. + * iwl_config_sched_scan_profiles() uses the order of these ssids to + * config match list. + */ + for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) { + /* skip empty SSID matchsets */ + if (!req->match_sets[i].ssid.ssid_len) + continue; + scan->direct_scan[i].id = WLAN_EID_SSID; + scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len; + memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid, + scan->direct_scan[i].len); + } + + /* add SSIDs from scan SSID list */ + *ssid_bitmap = 0; + for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) { + index = iwl_ssid_exist(req->ssids[j].ssid, + req->ssids[j].ssid_len, + scan->direct_scan); + if (index < 0) { + if (!req->ssids[j].ssid_len) + continue; + scan->direct_scan[i].id = WLAN_EID_SSID; + scan->direct_scan[i].len = req->ssids[j].ssid_len; + memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid, + scan->direct_scan[i].len); + *ssid_bitmap |= BIT(i + 1); + i++; + } else { + *ssid_bitmap |= BIT(index + 1); + } + } +} + +static void iwl_build_channel_cfg(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req, + struct iwl_scan_channel_cfg *channels, + enum ieee80211_band band, + int *head, + u32 ssid_bitmap, + struct iwl_mvm_scan_params *params) +{ + int i, index = 0; + + for (i = 0; i < req->n_channels; i++) { + struct ieee80211_channel *chan = req->channels[i]; + + if (chan->band != band) + continue; + + index = *head; + (*head)++; + + channels->channel_number[index] = cpu_to_le16(chan->hw_value); + 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; + + if (!(chan->flags & IEEE80211_CHAN_NO_IR)) + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE); + + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL | + IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); + + if (chan->flags & IEEE80211_CHAN_NO_HT40) + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW); + + /* scan for all SSIDs from req->ssids */ + channels->type[index] |= cpu_to_le32(ssid_bitmap); + } +} + +int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + 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; + u32 ssid_bitmap; + int cmd_len; + int ret; + + struct iwl_scan_offload_cfg *scan_cfg; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_CONFIG_CMD, + }; + struct iwl_mvm_scan_params params = {}; + + lockdep_assert_held(&mvm->mutex); + + cmd_len = sizeof(struct iwl_scan_offload_cfg) + + 2 * SCAN_OFFLOAD_PROBE_REQ_SIZE; + + scan_cfg = kzalloc(cmd_len, GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + 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); + /* build tx frames for supported bands */ + if (band_2ghz) { + iwl_scan_offload_build_tx_cmd(mvm, vif, ies, + IEEE80211_BAND_2GHZ, + &scan_cfg->scan_cmd.tx_cmd[0], + scan_cfg->data); + iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, + IEEE80211_BAND_2GHZ, &head, + ssid_bitmap, ¶ms); + } + if (band_5ghz) { + iwl_scan_offload_build_tx_cmd(mvm, vif, ies, + IEEE80211_BAND_5GHZ, + &scan_cfg->scan_cmd.tx_cmd[1], + scan_cfg->data + + SCAN_OFFLOAD_PROBE_REQ_SIZE); + iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, + IEEE80211_BAND_5GHZ, &head, + ssid_bitmap, ¶ms); + } + + cmd.data[0] = scan_cfg; + cmd.len[0] = cmd_len; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + + IWL_DEBUG_SCAN(mvm, "Sending scheduled scan config\n"); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(scan_cfg); + return ret; +} + +int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_scan_offload_profile *profile; + struct iwl_scan_offload_profile_cfg *profile_cfg; + struct iwl_scan_offload_blacklist *blacklist; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + .len[1] = sizeof(*profile_cfg), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .dataflags[1] = IWL_HCMD_DFL_NOCOPY, + }; + int blacklist_len; + int i; + int ret; + + if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) + return -EIO; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) + blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; + else + blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; + + blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); + if (!blacklist) + return -ENOMEM; + + profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); + if (!profile_cfg) { + ret = -ENOMEM; + goto free_blacklist; + } + + cmd.data[0] = blacklist; + cmd.len[0] = sizeof(*blacklist) * blacklist_len; + cmd.data[1] = profile_cfg; + + /* No blacklist configuration */ + + profile_cfg->num_profiles = req->n_match_sets; + profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; + profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; + profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; + if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) + profile_cfg->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; + + for (i = 0; i < req->n_match_sets; i++) { + profile = &profile_cfg->profiles[i]; + profile->ssid_index = i; + /* Support any cipher and auth algorithm */ + profile->unicast_cipher = 0xff; + profile->auth_alg = 0xff; + profile->network_type = IWL_NETWORK_TYPE_ANY; + profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; + profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; + } + + IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(profile_cfg); +free_blacklist: + kfree(blacklist); + + return ret; +} + +int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_scan_offload_req scan_req = { + .watchdog = IWL_SCHED_SCAN_WATCHDOG, + + .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS, + .schedule_line[0].delay = req->interval / 1000, + .schedule_line[0].full_scan_mul = 1, + + .schedule_line[1].iterations = 0xff, + .schedule_line[1].delay = req->interval / 1000, + .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER, + }; + + if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { + IWL_DEBUG_SCAN(mvm, + "Sending scheduled scan with filtering, filter len %d\n", + req->n_match_sets); + } else { + IWL_DEBUG_SCAN(mvm, + "Sending Scheduled scan without filtering\n"); + scan_req.flags |= cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_PASS_ALL); + } + + if (mvm->last_ebs_successful && + mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) + scan_req.flags |= + cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_EBS_ACCURATE_MODE); + + return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, 0, + sizeof(scan_req), &scan_req); +} + +static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_ABORT_CMD, + }; + u32 status; + + /* Exit instantly with error when device is not ready + * to receive scan abort command or it does not perform + * scheduled scan currently */ + if (mvm->scan_status != IWL_MVM_SCAN_SCHED) + return -EIO; + + ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); + if (ret) + return ret; + + if (status != CAN_ABORT_STATUS) { + /* + * The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before the + * microcode has notified us that a scan is completed. + */ + IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); + ret = -ENOENT; + } + + return ret; +} + +int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify) +{ + 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 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) { + IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret); + 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; + + if (notify) + ieee80211_sched_scan_stopped(mvm->hw); + + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c new file mode 100644 index 00000000000..7edfd15efc9 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/sf.c @@ -0,0 +1,289 @@ +/****************************************************************************** + * + * 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) 2013 - 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) 2013 - 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 "mvm.h" + +/* For counting bound interfaces */ +struct iwl_mvm_active_iface_iterator_data { + struct ieee80211_vif *ignore_vif; + u8 sta_vif_ap_sta_id; + enum iwl_sf_state sta_vif_state; + int num_active_macs; +}; + +/* + * Count bound interfaces which are not p2p, besides data->ignore_vif. + * data->station_vif will point to one bound vif of type station, if exists. + */ +static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_active_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif == data->ignore_vif || !mvmvif->phy_ctxt || + vif->type == NL80211_IFTYPE_P2P_DEVICE) + return; + + data->num_active_macs++; + + if (vif->type == NL80211_IFTYPE_STATION) { + data->sta_vif_ap_sta_id = mvmvif->ap_sta_id; + if (vif->bss_conf.assoc) + data->sta_vif_state = SF_FULL_ON; + else + data->sta_vif_state = SF_INIT_OFF; + } +} + +/* + * Aging and idle timeouts for the different possible scenarios + * in SF_FULL_ON state. + */ +static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { + { + cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER), + cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER), + cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_MCAST_AGING_TIMER), + cpu_to_le32(SF_MCAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_BA_AGING_TIMER), + cpu_to_le32(SF_BA_IDLE_TIMER) + }, + { + cpu_to_le32(SF_TX_RE_AGING_TIMER), + cpu_to_le32(SF_TX_RE_IDLE_TIMER) + }, +}; + +static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd, + struct ieee80211_sta *sta) +{ + int i, j, watermark; + + sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN); + + /* + * If we are in association flow - check antenna configuration + * capabilities of the AP station, and choose the watermark accordingly. + */ + if (sta) { + if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) { + switch (sta->rx_nss) { + case 1: + watermark = SF_W_MARK_SISO; + break; + case 2: + watermark = SF_W_MARK_MIMO2; + break; + default: + watermark = SF_W_MARK_MIMO3; + break; + } + } else { + watermark = SF_W_MARK_LEGACY; + } + /* default watermark value for unassociated mode. */ + } else { + watermark = SF_W_MARK_MIMO2; + } + sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark); + + for (i = 0; i < SF_NUM_SCENARIO; i++) { + for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) { + sf_cmd->long_delay_timeouts[i][j] = + cpu_to_le32(SF_LONG_DELAY_AGING_TIMER); + } + } + BUILD_BUG_ON(sizeof(sf_full_timeout) != + sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES); + + memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, + sizeof(sf_full_timeout)); +} + +static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, + enum iwl_sf_state new_state) +{ + struct iwl_sf_cfg_cmd sf_cmd = { + .state = new_state, + }; + struct ieee80211_sta *sta; + int ret = 0; + + /* + * If an associated AP sta changed its antenna configuration, the state + * will remain FULL_ON but SF parameters need to be reconsidered. + */ + if (new_state != SF_FULL_ON && mvm->sf_state == new_state) + return 0; + + switch (new_state) { + case SF_UNINIT: + break; + case SF_FULL_ON: + if (sta_id == IWL_MVM_STATION_COUNT) { + IWL_ERR(mvm, + "No station: Cannot switch SF to FULL_ON\n"); + return -EINVAL; + } + rcu_read_lock(); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, "Invalid station id\n"); + rcu_read_unlock(); + return -EINVAL; + } + iwl_mvm_fill_sf_command(&sf_cmd, sta); + rcu_read_unlock(); + break; + case SF_INIT_OFF: + iwl_mvm_fill_sf_command(&sf_cmd, NULL); + break; + default: + WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n", + new_state); + return -EINVAL; + } + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC, + sizeof(sf_cmd), &sf_cmd); + if (!ret) + mvm->sf_state = new_state; + + return ret; +} + +/* + * Update Smart fifo: + * Count bound interfaces that are not to be removed, ignoring p2p devices, + * and set new state accordingly. + */ +int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, + bool remove_vif) +{ + enum iwl_sf_state new_state; + u8 sta_id = IWL_MVM_STATION_COUNT; + struct iwl_mvm_vif *mvmvif = NULL; + struct iwl_mvm_active_iface_iterator_data data = { + .ignore_vif = changed_vif, + .sta_vif_state = SF_UNINIT, + .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, + }; + + /* + * Ignore the call if we are in HW Restart flow, or if the handled + * vif is a p2p device. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || + (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE)) + return 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bound_iface_iterator, + &data); + + /* If changed_vif exists and is not to be removed, add to the count */ + if (changed_vif && !remove_vif) + data.num_active_macs++; + + switch (data.num_active_macs) { + case 0: + /* If there are no active macs - change state to SF_INIT_OFF */ + new_state = SF_INIT_OFF; + break; + case 1: + if (remove_vif) { + /* The one active mac left is of type station + * and we filled the relevant data during iteration + */ + new_state = data.sta_vif_state; + sta_id = data.sta_vif_ap_sta_id; + } else { + if (WARN_ON(!changed_vif)) + return -EINVAL; + if (changed_vif->type != NL80211_IFTYPE_STATION) { + new_state = SF_UNINIT; + } else if (changed_vif->bss_conf.assoc && + changed_vif->bss_conf.dtim_period) { + mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); + sta_id = mvmvif->ap_sta_id; + new_state = SF_FULL_ON; + } else { + new_state = SF_INIT_OFF; + } + } + break; + default: + /* If there are multiple active macs - change to SF_UNINIT */ + new_state = SF_UNINIT; + } + return iwl_mvm_sf_config(mvm, sta_id, new_state); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 44add291531..1fb01ea2e70 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,19 +66,30 @@ #include "sta.h" #include "rs.h" -static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) +static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, + enum nl80211_iftype iftype) { int sta_id; + u32 reserved_ids = 0; + BUILD_BUG_ON(IWL_MVM_STATION_COUNT > 32); WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)); lockdep_assert_held(&mvm->mutex); + /* d0i3/d3 assumes the AP's sta_id (of sta vif) is 0. reserve it. */ + if (iftype != NL80211_IFTYPE_STATION) + reserved_ids = BIT(0); + /* Don't take rcu_read_lock() since we are protected by mvm->mutex */ - for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) + for (sta_id = 0; sta_id < IWL_MVM_STATION_COUNT; sta_id++) { + if (BIT(sta_id) & reserved_ids) + continue; + if (!rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], lockdep_is_held(&mvm->mutex))) return sta_id; + } return IWL_MVM_STATION_COUNT; } @@ -204,7 +215,8 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - sta_id = iwl_mvm_find_free_sta_id(mvm); + sta_id = iwl_mvm_find_free_sta_id(mvm, + ieee80211_vif_type_p2p(vif)); else sta_id = mvm_sta->sta_id; @@ -229,8 +241,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - /* for HW restart - need to reset the seq_number etc... */ - memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); + /* for HW restart - reset everything but the sequence number */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = mvm_sta->tid_data[i].seq_number; + memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); + mvm_sta->tid_data[i].seq_number = seq; + } ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); if (ret) @@ -311,7 +327,7 @@ static int iwl_mvm_rm_sta_common(struct iwl_mvm *mvm, u8 sta_id) return -EINVAL; } - ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, CMD_SYNC, + ret = iwl_mvm_send_cmd_pdu(mvm, REMOVE_STA, 0, sizeof(rm_sta_cmd), &rm_sta_cmd); if (ret) { IWL_ERR(mvm, "Failed to remove station. Id=%d\n", sta_id); @@ -341,8 +357,15 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk) rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], lockdep_is_held(&mvm->mutex)); - /* This station is in use */ - if (!IS_ERR(sta)) + /* + * This station is in use or RCU-removed; the latter happens in + * managed mode, where mac80211 removes the station before we + * can remove it from firmware (we can only do that after the + * MAC is marked unassociated), and possibly while the deauth + * frame to disconnect from the AP is still queued. Then, the + * station pointer is -ENOENT when the last skb is reclaimed. + */ + if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT) continue; if (PTR_ERR(sta) == -EINVAL) { @@ -368,7 +391,7 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk) sta_id); continue; } - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL); + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); clear_bit(sta_id, mvm->sta_drained); } @@ -390,20 +413,16 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, /* flush its queues here since we are freeing mvm_sta */ ret = iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, true); - /* - * Put a non-NULL since the fw station isn't removed. - * It will be removed after the MAC will be set as - * unassoc. - */ - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], - ERR_PTR(-EINVAL)); - /* if we are associated - we can't remove the AP STA now */ if (vif->bss_conf.assoc) return ret; /* unassoc - go ahead - remove the AP STA now */ mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; + + /* clear d0i3_ap_sta_id if no longer relevant */ + if (mvm->d0i3_ap_sta_id == mvm_sta->sta_id) + mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; } /* @@ -423,7 +442,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, } else { spin_unlock_bh(&mvm_sta->lock); ret = iwl_mvm_rm_sta_common(mvm, mvm_sta->sta_id); - rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[mvm_sta->sta_id], NULL); } return ret; @@ -437,15 +456,15 @@ int iwl_mvm_rm_sta_id(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta_id], NULL); + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta_id], NULL); return ret; } int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, - u32 qmask) + u32 qmask, enum nl80211_iftype iftype) { if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { - sta->sta_id = iwl_mvm_find_free_sta_id(mvm); + sta->sta_id = iwl_mvm_find_free_sta_id(mvm, iftype); if (WARN_ON_ONCE(sta->sta_id == IWL_MVM_STATION_COUNT)) return -ENOSPC; } @@ -459,7 +478,7 @@ int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta) { - rcu_assign_pointer(mvm->fw_id_to_mac_id[sta->sta_id], NULL); + RCU_INIT_POINTER(mvm->fw_id_to_mac_id[sta->sta_id], NULL); memset(sta, 0, sizeof(struct iwl_mvm_int_sta)); sta->sta_id = IWL_MVM_STATION_COUNT; } @@ -475,7 +494,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd)); + memset(&cmd, 0, sizeof(cmd)); cmd.sta_id = sta->sta_id; cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, color)); @@ -510,7 +529,8 @@ int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); /* Add the aux station, but without any queues */ - ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0); + ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, 0, + NL80211_IFTYPE_UNSPECIFIED); if (ret) return ret; @@ -534,10 +554,14 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *bsta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const u8 *baddr = _baddr; lockdep_assert_held(&mvm->mutex); + if (vif->type == NL80211_IFTYPE_ADHOC) + baddr = vif->bss_conf.bssid; + if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT)) return -ENOSPC; @@ -578,7 +602,8 @@ int iwl_mvm_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); - ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask); + ret = iwl_mvm_allocate_int_sta(mvm, bsta, qmask, + ieee80211_vif_type_p2p(vif)); if (ret) return ret; @@ -714,7 +739,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, return ret; } -static const u8 tid_to_ac[] = { +const u8 tid_to_mac80211_ac[] = { IEEE80211_AC_BE, IEEE80211_AC_BK, IEEE80211_AC_BK, @@ -725,10 +750,21 @@ static const u8 tid_to_ac[] = { IEEE80211_AC_VO, }; +static const u8 tid_to_ucode_ac[] = { + AC_BE, + AC_BK, + AC_BK, + AC_BE, + AC_VI, + AC_VI, + AC_VO, + AC_VO, +}; + int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; int txq_id; @@ -743,21 +779,29 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); - for (txq_id = IWL_MVM_FIRST_AGG_QUEUE; - txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++) + for (txq_id = mvm->first_agg_queue; + txq_id <= mvm->last_agg_queue; txq_id++) if (mvm->queue_to_mac80211[txq_id] == IWL_INVALID_MAC80211_QUEUE) break; - if (txq_id > IWL_MVM_LAST_AGG_QUEUE) { + if (txq_id > mvm->last_agg_queue) { IWL_ERR(mvm, "Failed to allocate agg queue\n"); 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_ac[tid]]; + 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; @@ -783,7 +827,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u8 buf_size) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; int queue, fifo, ret; u16 ssn; @@ -797,7 +841,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, tid_data->ssn = 0xffff; spin_unlock_bh(&mvmsta->lock); - fifo = iwl_mvm_ac_to_tx_fifo[tid_to_ac[tid]]; + fifo = iwl_mvm_ac_to_tx_fifo[tid_to_mac80211_ac[tid]]; ret = iwl_mvm_sta_tx_agg(mvm, sta, tid, queue, true); if (ret) @@ -820,26 +864,13 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", sta->addr, tid); - if (mvm->cfg->ht_params->use_rts_for_aggregation) { - /* - * switch to RTS/CTS if it is the prefer protection - * method for HT traffic - * this function also sends the LQ command - */ - return iwl_mvm_tx_protection(mvm, mvmsta, true); - /* - * TODO: remove the TLC_RTS flag when we tear down the last - * AGG session (agg_tids_count in DVM) - */ - } - - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, CMD_ASYNC, false); + return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); } int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; u16 txq_id; int err; @@ -911,7 +942,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; u16 txq_id; enum iwl_mvm_agg_state old_state; @@ -986,8 +1017,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, u8 sta_id, u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags) { + struct iwl_mvm_add_sta_key_cmd cmd = {}; __le16 key_flags; - struct iwl_mvm_add_sta_cmd cmd = {}; int ret, status; u16 keyidx; int i; @@ -1000,37 +1031,34 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_TKIP: key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); - cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; + cmd.tkip_rx_tsc_byte2 = tkip_iv32; for (i = 0; i < 5; i++) - cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(cmd.key.key, keyconf->key, keyconf->keylen); + cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); + memcpy(cmd.key, keyconf->key, keyconf->keylen); break; case WLAN_CIPHER_SUITE_CCMP: key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); - memcpy(cmd.key.key, keyconf->key, keyconf->keylen); + memcpy(cmd.key, keyconf->key, keyconf->keylen); break; default: - WARN_ON(1); - return -EINVAL; + key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); + memcpy(cmd.key, keyconf->key, keyconf->keylen); } if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.key.key_offset = keyconf->hw_key_idx; - cmd.key.key_flags = key_flags; - cmd.add_modify = STA_MODE_MODIFY; - cmd.modify_mask = STA_MODIFY_KEY; + cmd.key_offset = keyconf->hw_key_idx; + cmd.key_flags = key_flags; cmd.sta_id = sta_id; status = ADD_STA_SUCCESS; - if (cmd_flags == CMD_SYNC) - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + if (cmd_flags & CMD_ASYNC) + ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, CMD_ASYNC, + sizeof(cmd), &cmd); else - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), + &cmd, &status); switch (status) { case ADD_STA_SUCCESS: @@ -1083,7 +1111,7 @@ static int iwl_mvm_send_sta_igtk(struct iwl_mvm *mvm, remove_key ? "removing" : "installing", igtk_cmd.sta_id); - return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, CMD_SYNC, + return iwl_mvm_send_cmd_pdu(mvm, MGMT_MCAST_KEY, 0, sizeof(igtk_cmd), &igtk_cmd); } @@ -1170,15 +1198,15 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id, - seq.tkip.iv32, p1k, CMD_SYNC); + seq.tkip.iv32, p1k, 0); break; case WLAN_CIPHER_SUITE_CCMP: ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, sta_id, - 0, NULL, CMD_SYNC); + 0, NULL, 0); break; default: - IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher); - ret = -EINVAL; + ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, + sta_id, 0, NULL, 0); } if (ret) @@ -1197,7 +1225,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, struct ieee80211_key_conf *keyconf) { struct iwl_mvm_sta *mvm_sta; - struct iwl_mvm_add_sta_cmd cmd = {}; + struct iwl_mvm_add_sta_key_cmd cmd = {}; __le16 key_flags; int ret, status; u8 sta_id; @@ -1252,16 +1280,12 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.key.key_flags = key_flags; - cmd.key.key_offset = keyconf->hw_key_idx; + cmd.key_flags = key_flags; + cmd.key_offset = keyconf->hw_key_idx; cmd.sta_id = sta_id; - cmd.modify_mask = STA_MODIFY_KEY; - cmd.add_modify = STA_MODE_MODIFY; - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, sizeof(cmd), &cmd, &status); switch (status) { @@ -1308,7 +1332,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, @@ -1325,28 +1349,102 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum ieee80211_frame_release_type reason, - u16 cnt) + u16 cnt, u16 tids, bool more_data, + bool agg) { - u16 sleep_state_flags = - (reason == IEEE80211_FRAME_RELEASE_UAPSD) ? - STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL; - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, .sleep_tx_count = cpu_to_le16(cnt), .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color), - /* - * Same modify mask for sleep_tx_count and sleep_state_flags so - * we must set the sleep_state_flags too. - */ - .sleep_state_flags = cpu_to_le16(sleep_state_flags), }; - int ret; + int tid, ret; + unsigned long _tids = tids; + + /* convert TIDs to ACs - we don't support TSPEC so that's OK + * Note that this field is reserved and unused by firmware not + * supporting GO uAPSD, so it's safe to always do this. + */ + for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) + cmd.awake_acs |= BIT(tid_to_ucode_ac[tid]); + + /* If we're releasing frames from aggregation queues then check if the + * all queues combined that we're releasing frames from have + * - more frames than the service period, in which case more_data + * needs to be set + * - fewer than 'cnt' frames, in which case we need to adjust the + * firmware command (but do that unconditionally) + */ + if (agg) { + int remaining = cnt; + + spin_lock_bh(&mvmsta->lock); + for_each_set_bit(tid, &_tids, IWL_MAX_TID_COUNT) { + struct iwl_mvm_tid_data *tid_data; + u16 n_queued; + + tid_data = &mvmsta->tid_data[tid]; + if (WARN(tid_data->state != IWL_AGG_ON && + tid_data->state != IWL_EMPTYING_HW_QUEUE_DELBA, + "TID %d state is %d\n", + tid, tid_data->state)) { + spin_unlock_bh(&mvmsta->lock); + ieee80211_sta_eosp(sta); + return; + } + + n_queued = iwl_mvm_tid_queued(tid_data); + if (n_queued > remaining) { + more_data = true; + remaining = 0; + break; + } + remaining -= n_queued; + } + spin_unlock_bh(&mvmsta->lock); + + cmd.sleep_tx_count = cpu_to_le16(cnt - remaining); + if (WARN_ON(cnt - remaining == 0)) { + ieee80211_sta_eosp(sta); + return; + } + } + + /* Note: this is ignored by firmware not supporting GO uAPSD */ + if (more_data) + cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_MOREDATA); + + if (reason == IEEE80211_FRAME_RELEASE_PSPOLL) { + mvmsta->next_status_eosp = true; + cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_PS_POLL); + } else { + cmd.sleep_state_flags |= cpu_to_le16(STA_SLEEP_STATE_UAPSD); + } - /* TODO: somehow the fw doesn't seem to take PS_POLL into account */ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } + +int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mvm_eosp_notification *notif = (void *)pkt->data; + struct ieee80211_sta *sta; + u32 sta_id = le32_to_cpu(notif->sta_id); + + if (WARN_ON_ONCE(sta_id >= IWL_MVM_STATION_COUNT)) + return 0; + + rcu_read_lock(); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (!IS_ERR_OR_NULL(sta)) + ieee80211_sta_eosp(sta); + rcu_read_unlock(); + + return 0; +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 94b265eb32b..d98e8a2142b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -195,24 +195,33 @@ struct iwl_mvm; /** * DOC: AP mode - PS * - * When a station is asleep, the fw will set it as "asleep". All the - * non-aggregation frames to that station will be dropped by the fw - * (%TX_STATUS_FAIL_DEST_PS failure code). + * When a station is asleep, the fw will set it as "asleep". All frames on + * shared queues (i.e. non-aggregation queues) to that station will be dropped + * by the fw (%TX_STATUS_FAIL_DEST_PS failure code). + * * AMPDUs are in a separate queue that is stopped by the fw. We just need to - * let mac80211 know how many frames we have in these queues so that it can + * let mac80211 know when there are frames in these queues so that it can * properly handle trigger frames. - * When the a trigger frame is received, mac80211 tells the driver to send - * frames from the AMPDU queues or AC queue depending on which queue are - * delivery-enabled and what TID has frames to transmit (Note that mac80211 has - * all the knowledege since all the non-agg frames are buffered / filtered, and - * the driver tells mac80211 about agg frames). The driver needs to tell the fw - * to let frames out even if the station is asleep. This is done by - * %iwl_mvm_sta_modify_sleep_tx_count. - * When we receive a frame from that station with PM bit unset, the - * driver needs to let the fw know that this station isn't alseep any more. - * This is done by %iwl_mvm_sta_modify_ps_wake. - * - * TODO - EOSP handling + * + * When a trigger frame is received, mac80211 tells the driver to send frames + * from the AMPDU queues or sends frames to non-aggregation queues itself, + * depending on which ACs are delivery-enabled and what TID has frames to + * transmit. Note that mac80211 has all the knowledege since all the non-agg + * frames are buffered / filtered, and the driver tells mac80211 about agg + * frames). The driver needs to tell the fw to let frames out even if the + * station is asleep. This is done by %iwl_mvm_sta_modify_sleep_tx_count. + * + * When we receive a frame from that station with PM bit unset, the driver + * needs to let the fw know that this station isn't asleep any more. This is + * done by %iwl_mvm_sta_modify_ps_wake in response to mac80211 signalling the + * station's wakeup. + * + * For a GO, the Service Period might be cut short due to an absence period + * of the GO. In this (and all other cases) the firmware notifies us with the + * EOSP_NOTIFICATION, and we notify mac80211 of that. Further frames that we + * already sent to the device will be rejected again. + * + * See also "AP support for powersaving clients" in mac80211.h. */ /** @@ -244,6 +253,8 @@ enum iwl_mvm_agg_state { * This is basically (last acked packet++). * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). + * @reduced_tpc: Reduced tx power. Holds the data between the + * Tx response (TX_CMD), and the block ack notification (COMPRESSED_BA). * @state: state of the BA agreement establishment / tear down. * @txq_id: Tx queue used by the BA session * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or @@ -256,11 +267,18 @@ struct iwl_mvm_tid_data { u16 next_reclaimed; /* The rest is Tx AGG related */ u32 rate_n_flags; + u8 reduced_tpc; enum iwl_mvm_agg_state state; u16 txq_id; u16 ssn; }; +static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) +{ + return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number), + tid_data->next_reclaimed); +} + /** * struct iwl_mvm_sta - representation of a station in the driver * @sta_id: the index of the station in the fw (will be replaced by id_n_color) @@ -270,6 +288,8 @@ struct iwl_mvm_tid_data { * tid. * @max_agg_bufsize: the maximal size of the AGG buffer for this station * @bt_reduced_txpower: is reduced tx power enabled for this station + * @next_status_eosp: the next reclaimed packet is a PS-Poll response and + * we need to signal the EOSP * @lock: lock to protect the whole struct. Since %tid_data is access from Tx * and from Tx response flow, it needs a spinlock. * @tid_data: per tid data. Look at %iwl_mvm_tid_data. @@ -288,20 +308,23 @@ struct iwl_mvm_sta { u16 tid_disable_agg; u8 max_agg_bufsize; bool bt_reduced_txpower; + bool next_status_eosp; spinlock_t lock; struct iwl_mvm_tid_data tid_data[IWL_MAX_TID_COUNT]; struct iwl_lq_sta lq_sta; struct ieee80211_vif *vif; -#ifdef CONFIG_PM_SLEEP - u16 last_seq_ctl; -#endif - /* Temporary, until the new TLC will control the Tx protection */ s8 tx_protection; bool tt_tx_protection; }; +static inline struct iwl_mvm_sta * +iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) +{ + return (void *)sta->drv_priv; +} + /** * struct iwl_mvm_int_sta - representation of an internal station (auxiliary or * broadcast) @@ -343,6 +366,10 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); +int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + /* AMPDU */ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u16 ssn, bool start); @@ -357,7 +384,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm); int iwl_mvm_allocate_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta, - u32 qmask); + u32 qmask, enum nl80211_iftype iftype); void iwl_mvm_dealloc_int_sta(struct iwl_mvm *mvm, struct iwl_mvm_int_sta *sta); int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -373,7 +400,8 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, struct ieee80211_sta *sta, enum ieee80211_frame_release_type reason, - u16 cnt); + u16 cnt, u16 tids, bool more_data, + bool agg); int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool drain); diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h new file mode 100644 index 00000000000..0241665925f --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/testmode.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * + * 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) 2013 - 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) 2013 - 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 __IWL_MVM_TESTMODE_H__ +#define __IWL_MVM_TESTMODE_H__ + +/** + * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA + * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute) + * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32) + * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32) + * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32) + */ +enum iwl_mvm_testmode_attrs { + IWL_MVM_TM_ATTR_UNSPEC, + IWL_MVM_TM_ATTR_CMD, + IWL_MVM_TM_ATTR_NOA_DURATION, + IWL_MVM_TM_ATTR_BEACON_FILTER_STATE, + + /* keep last */ + NUM_IWL_MVM_TM_ATTRS, + IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1, +}; + +/** + * enum iwl_mvm_testmode_commands - MVM testmode commands + * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing + * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on + */ +enum iwl_mvm_testmode_commands { + IWL_MVM_TM_CMD_SET_NOA, + IWL_MVM_TM_CMD_SET_BEACON_FILTER, +}; + +#endif /* __IWL_MVM_TESTMODE_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 76a3c177e10..80100f6cc12 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -126,6 +126,7 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm) * in iwl_mvm_te_handle_notif). */ clear_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); + iwl_mvm_unref(mvm, IWL_MVM_REF_ROC); /* * Of course, our status bit is just as racy as mac80211, so in @@ -176,8 +177,11 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, * P2P Device discoveribility, while there are other higher priority * events in the system). */ - if (WARN_ONCE(!le32_to_cpu(notif->status), - "Failed to schedule time event\n")) { + if (!le32_to_cpu(notif->status)) { + bool start = le32_to_cpu(notif->action) & + TE_V2_NOTIF_HOST_EVENT_START; + IWL_WARN(mvm, "Time Event %s notification failure\n", + start ? "start" : "end"); if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) { iwl_mvm_te_clear_data(mvm, te_data); return; @@ -207,6 +211,7 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm, if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) { set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status); + iwl_mvm_ref(mvm, IWL_MVM_REF_ROC); ieee80211_ready_on_channel(mvm->hw); } } else { @@ -246,12 +251,12 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, container_of(notif_wait, struct iwl_mvm, notif_wait); struct iwl_mvm_time_event_data *te_data = data; struct iwl_time_event_resp *resp; - int resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + int resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON(pkt->hdr.cmd != TIME_EVENT_CMD)) return true; - if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { IWL_ERR(mvm, "Invalid TIME_EVENT_CMD response\n"); return true; } @@ -268,67 +273,10 @@ static bool iwl_mvm_time_event_response(struct iwl_notif_wait_data *notif_wait, return true; } -/* used to convert from time event API v2 to v1 */ -#define TE_V2_DEP_POLICY_MSK (TE_V2_DEP_OTHER | TE_V2_DEP_TSF |\ - TE_V2_EVENT_SOCIOPATHIC) -static inline u16 te_v2_get_notify(__le16 policy) -{ - return le16_to_cpu(policy) & TE_V2_NOTIF_MSK; -} - -static inline u16 te_v2_get_dep_policy(__le16 policy) -{ - return (le16_to_cpu(policy) & TE_V2_DEP_POLICY_MSK) >> - TE_V2_PLACEMENT_POS; -} - -static inline u16 te_v2_get_absence(__le16 policy) -{ - return (le16_to_cpu(policy) & TE_V2_ABSENCE) >> TE_V2_ABSENCE_POS; -} - -static void iwl_mvm_te_v2_to_v1(const struct iwl_time_event_cmd_v2 *cmd_v2, - struct iwl_time_event_cmd_v1 *cmd_v1) -{ - cmd_v1->id_and_color = cmd_v2->id_and_color; - cmd_v1->action = cmd_v2->action; - cmd_v1->id = cmd_v2->id; - cmd_v1->apply_time = cmd_v2->apply_time; - cmd_v1->max_delay = cmd_v2->max_delay; - cmd_v1->depends_on = cmd_v2->depends_on; - cmd_v1->interval = cmd_v2->interval; - cmd_v1->duration = cmd_v2->duration; - if (cmd_v2->repeat == TE_V2_REPEAT_ENDLESS) - cmd_v1->repeat = cpu_to_le32(TE_V1_REPEAT_ENDLESS); - else - cmd_v1->repeat = cpu_to_le32(cmd_v2->repeat); - cmd_v1->max_frags = cpu_to_le32(cmd_v2->max_frags); - cmd_v1->interval_reciprocal = 0; /* unused */ - - cmd_v1->dep_policy = cpu_to_le32(te_v2_get_dep_policy(cmd_v2->policy)); - cmd_v1->is_present = cpu_to_le32(!te_v2_get_absence(cmd_v2->policy)); - cmd_v1->notify = cpu_to_le32(te_v2_get_notify(cmd_v2->policy)); -} - -static int iwl_mvm_send_time_event_cmd(struct iwl_mvm *mvm, - const struct iwl_time_event_cmd_v2 *cmd) -{ - struct iwl_time_event_cmd_v1 cmd_v1; - - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2) - return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, - sizeof(*cmd), cmd); - - iwl_mvm_te_v2_to_v1(cmd, &cmd_v1); - return iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC, - sizeof(cmd_v1), &cmd_v1); -} - - static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_time_event_data *te_data, - struct iwl_time_event_cmd_v2 *te_cmd) + struct iwl_time_event_cmd *te_cmd) { static const u8 time_event_response[] = { TIME_EVENT_CMD }; struct iwl_notification_wait wait_time_event; @@ -364,7 +312,8 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, ARRAY_SIZE(time_event_response), iwl_mvm_time_event_response, te_data); - ret = iwl_mvm_send_time_event_cmd(mvm, te_cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, + sizeof(*te_cmd), te_cmd); if (ret) { IWL_ERR(mvm, "Couldn't send TIME_EVENT_CMD: %d\n", ret); iwl_remove_notification(&mvm->notif_wait, &wait_time_event); @@ -387,11 +336,12 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, void iwl_mvm_protect_session(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 duration, u32 min_duration) + u32 duration, u32 min_duration, + u32 max_delay) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd_v2 time_cmd = {}; + struct iwl_time_event_cmd time_cmd = {}; lockdep_assert_held(&mvm->mutex); @@ -426,13 +376,14 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG)); time_cmd.max_frags = TE_V2_FRAG_NONE; - time_cmd.max_delay = cpu_to_le32(500); + time_cmd.max_delay = cpu_to_le32(max_delay); /* TODO: why do we need to interval = bi if it is not periodic? */ time_cmd.interval = cpu_to_le32(1); time_cmd.duration = cpu_to_le32(duration); time_cmd.repeat = 1; time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_NOTIF_HOST_EVENT_END); + TE_V2_NOTIF_HOST_EVENT_END | + T2_V2_START_IMMEDIATELY); iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); } @@ -446,7 +397,7 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif, struct iwl_mvm_time_event_data *te_data) { - struct iwl_time_event_cmd_v2 time_cmd = {}; + struct iwl_time_event_cmd time_cmd = {}; u32 id, uid; int ret; @@ -483,7 +434,8 @@ void iwl_mvm_remove_time_event(struct iwl_mvm *mvm, cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); IWL_DEBUG_TE(mvm, "Removing TE 0x%x\n", le32_to_cpu(time_cmd.id)); - ret = iwl_mvm_send_time_event_cmd(mvm, &time_cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, 0, + sizeof(time_cmd), &time_cmd); if (WARN_ON(ret)) return; } @@ -503,7 +455,7 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; - struct iwl_time_event_cmd_v2 time_cmd = {}; + struct iwl_time_event_cmd time_cmd = {}; lockdep_assert_held(&mvm->mutex); if (te_data->running) { @@ -547,7 +499,8 @@ int iwl_mvm_start_p2p_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif, time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); time_cmd.repeat = 1; time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START | - TE_V2_NOTIF_HOST_EVENT_END); + TE_V2_NOTIF_HOST_EVENT_END | + T2_V2_START_IMMEDIATELY); return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd); } diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h index f86c51065ed..4a61c8c0237 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -123,6 +123,7 @@ * @duration: the duration of the session in TU. * @min_duration: will start a new session if the current session will end * in less than min_duration. + * @max_delay: maximum delay before starting the time event (in TU) * * This function can be used to start a session protection which means that the * fw will stay on the channel for %duration_ms milliseconds. This function @@ -133,7 +134,8 @@ */ void iwl_mvm_protect_session(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 duration, u32 min_duration); + u32 duration, u32 min_duration, + u32 max_delay); /** * iwl_mvm_stop_session_protection - cancel the session protection. diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 1f3282dff51..86856151278 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -340,7 +340,7 @@ static void check_exit_ctkill(struct work_struct *work) iwl_trans_start_hw(mvm->trans); temp = check_nic_temperature(mvm); - iwl_trans_stop_hw(mvm->trans, false); + iwl_trans_stop_device(mvm->trans); if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) { IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n"); @@ -388,7 +388,7 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) lockdep_is_held(&mvm->mutex)); if (IS_ERR_OR_NULL(sta)) continue; - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); if (enable == mvmsta->tt_tx_protection) continue; err = iwl_mvm_tx_protection(mvm, mvmsta, enable); @@ -403,15 +403,16 @@ static void iwl_mvm_tt_tx_protection(struct iwl_mvm *mvm, bool enable) } } -static void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) +void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff) { struct iwl_host_cmd cmd = { .id = REPLY_THERMAL_MNG_BACKOFF, .len = { sizeof(u32), }, .data = { &backoff, }, - .flags = CMD_SYNC, }; + backoff = max(backoff, mvm->thermal_throttle.min_backoff); + if (iwl_mvm_send_cmd(mvm, &cmd) == 0) { IWL_DEBUG_TEMP(mvm, "Set Thermal Tx backoff to: %u\n", backoff); @@ -466,13 +467,14 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm) } if (params->support_tx_backoff) { - tx_backoff = 0; + tx_backoff = tt->min_backoff; for (i = 0; i < TT_TX_BACKOFF_SIZE; i++) { if (temperature < params->tx_backoff[i].temperature) break; - tx_backoff = params->tx_backoff[i].backoff; + tx_backoff = max(tt->min_backoff, + params->tx_backoff[i].backoff); } - if (tx_backoff != 0) + if (tx_backoff != tt->min_backoff) throttle_enable = true; if (tt->tx_backoff != tx_backoff) iwl_mvm_tt_tx_backoff(mvm, tx_backoff); @@ -482,7 +484,8 @@ void iwl_mvm_tt_handler(struct iwl_mvm *mvm) IWL_WARN(mvm, "Due to high temperature thermal throttling initiated\n"); tt->throttle = true; - } else if (tt->throttle && !tt->dynamic_smps && tt->tx_backoff == 0 && + } else if (tt->throttle && !tt->dynamic_smps && + tt->tx_backoff == tt->min_backoff && temperature <= params->tx_protection_exit) { IWL_WARN(mvm, "Temperature is back to normal thermal throttling stopped\n"); @@ -534,7 +537,7 @@ static const struct iwl_tt_params iwl7000_high_temp_tt_params = { .support_tx_backoff = true, }; -void iwl_mvm_tt_initialize(struct iwl_mvm *mvm) +void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) { struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; @@ -546,6 +549,7 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm) tt->params = &iwl7000_tt_params; tt->throttle = false; + tt->min_backoff = min_backoff; INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); } diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index e05440d9031..3846a6c41eb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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); @@ -122,15 +121,12 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, * it */ WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU); - } else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { + } else if (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { tx_cmd->pm_frame_timeout = cpu_to_le16(2); } else { 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; @@ -207,7 +203,7 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_fw_valid_tx_ant(mvm->fw), + iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, mvm->mgmt_last_antenna_idx); rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS; @@ -253,8 +249,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); break; default: - IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher); - break; + tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; } } @@ -276,6 +271,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, return NULL; memset(dev_cmd, 0, sizeof(*dev_cmd)); + dev_cmd->hdr.cmd = TX_CMD; tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; if (info->control.hw_key) @@ -361,7 +357,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, u8 txq_id = info->hw_queue; bool is_data_qos = false, is_ampdu = false; - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); fc = hdr->frame_control; if (WARN_ON_ONCE(!mvmsta)) @@ -377,6 +373,13 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; /* From now on, we cannot access info->control */ + /* + * we handle that entirely ourselves -- for uAPSD the firmware + * will always send a notification, and for PS-Poll responses + * we'll notify mac80211 when getting frame status + */ + info->flags &= ~IEEE80211_TX_STATUS_EOSP; + spin_lock(&mvmsta->lock); if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { @@ -390,7 +393,6 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, seq_number &= IEEE80211_SCTL_SEQ; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; is_data_qos = true; is_ampdu = info->flags & IEEE80211_TX_CTL_AMPDU; } @@ -407,17 +409,17 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, } IWL_DEBUG_TX(mvm, "TX to [%d|%d] Q:%d - seq: 0x%x\n", mvmsta->sta_id, - tid, txq_id, seq_number); + tid, txq_id, IEEE80211_SEQ_TO_SN(seq_number)); if (iwl_trans_tx(mvm->trans, skb, dev_cmd, txq_id)) goto drop_unlock_sta; if (is_data_qos && !ieee80211_has_morefrags(fc)) - mvmsta->tid_data[tid].seq_number = seq_number; + mvmsta->tid_data[tid].seq_number = seq_number + 0x10; spin_unlock(&mvmsta->lock); - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE) + if (txq_id < mvm->first_agg_queue) atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); return 0; @@ -432,12 +434,23 @@ drop: static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct ieee80211_sta *sta, u8 tid) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; lockdep_assert_held(&mvmsta->lock); + if ((tid_data->state == IWL_AGG_ON || + tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && + iwl_mvm_tid_queued(tid_data) == 0) { + /* + * Now that this aggregation queue is empty tell mac80211 so it + * knows we no longer have frames buffered for the station on + * this TID (for the TIM bitmap calculation.) + */ + ieee80211_sta_set_buffered(sta, tid, false); + } + if (tid_data->ssn != tid_data->next_reclaimed) return; @@ -511,16 +524,10 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status) } #endif /* CONFIG_IWLWIFI_DEBUG */ -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags, - struct ieee80211_tx_info *info) +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + struct ieee80211_tx_rate *r) { - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if (rate_n_flags & RATE_HT_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { @@ -549,10 +556,23 @@ static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags, r->flags |= IEEE80211_TX_RC_VHT_MCS; } else { r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - info->band); + band); } } +/** + * translate ucode response to mac80211 tx status control values + */ +static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -602,11 +622,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, } info->status.rates[0].count = tx_resp->failure_frame + 1; - iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate), - info); + iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), + info); /* Single frame failure in an AMPDU queue => send BAR */ - if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE && + if (txq_id >= mvm->first_agg_queue && !(info->flags & IEEE80211_TX_STAT_ACK)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; @@ -616,10 +636,14 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, seq_ctl = le16_to_cpu(hdr->seq_ctrl); } - ieee80211_tx_status_ni(mvm->hw, skb); + BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); + info->status.status_driver_data[0] = + (void *)(uintptr_t)tx_resp->reduced_tpc; + + ieee80211_tx_status(mvm->hw, skb); } - if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) { + if (txq_id >= mvm->first_agg_queue) { /* If this is an aggregation queue, we use the ssn since: * ssn = wifi seq_num % 256. * The seq_ctl is the sequence control of the packet to which @@ -653,9 +677,15 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, rcu_read_lock(); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + /* + * sta can't be NULL otherwise it'd mean that the sta has been freed in + * the firmware while we still have packets for it in the Tx queues. + */ + if (WARN_ON_ONCE(!sta)) + goto out; - if (!IS_ERR_OR_NULL(sta)) { - mvmsta = (void *)sta->drv_priv; + if (!IS_ERR(sta)) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); if (tid != IWL_TID_NON_QOS) { struct iwl_mvm_tid_data *tid_data = @@ -669,11 +699,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, spin_unlock_bh(&mvmsta->lock); } -#ifdef CONFIG_PM_SLEEP - mvmsta->last_seq_ctl = seq_ctl; -#endif + if (mvmsta->next_status_eosp) { + mvmsta->next_status_eosp = false; + ieee80211_sta_eosp(sta); + } } else { - sta = NULL; mvmsta = NULL; } @@ -681,42 +711,38 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, * If the txq is not an AMPDU queue, there is no chance we freed * several skbs. Check that out... */ - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) && - atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) { - if (mvmsta) { - /* - * If there are no pending frames for this STA, notify - * mac80211 that this station can go to sleep in its - * STA table. - */ - if (mvmsta->vif->type == NL80211_IFTYPE_AP) - ieee80211_sta_block_awake(mvm->hw, sta, false); - /* - * We might very well have taken mvmsta pointer while - * the station was being removed. The remove flow might - * have seen a pending_frame (because we didn't take - * the lock) even if now the queues are drained. So make - * really sure now that this the station is not being - * removed. If it is, run the drain worker to remove it. - */ - spin_lock_bh(&mvmsta->lock); - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (IS_ERR_OR_NULL(sta)) { - /* - * Station disappeared in the meantime: - * so we are draining. - */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } - spin_unlock_bh(&mvmsta->lock); - } else if (!mvmsta) { - /* Tx response without STA, so we are draining */ - set_bit(sta_id, mvm->sta_drained); - schedule_work(&mvm->sta_drained_wk); - } + if (txq_id >= mvm->first_agg_queue) + goto out; + + /* We can't free more than one frame at once on a shared queue */ + WARN_ON(skb_freed > 1); + + /* If we have still frames from this STA nothing to do here */ + if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) + goto out; + + if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) { + /* + * If there are no pending frames for this STA, notify + * mac80211 that this station can go to sleep in its + * STA table. + * If mvmsta is not NULL, sta is valid. + */ + ieee80211_sta_block_awake(mvm->hw, sta, false); } + if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) { + /* + * We are draining and this was the last packet - pre_rcu_remove + * has been called already. We might be after the + * synchronize_net already. + * Don't rely on iwl_mvm_rm_sta to see the empty Tx queues. + */ + set_bit(sta_id, mvm->sta_drained); + schedule_work(&mvm->sta_drained_wk); + } + +out: rcu_read_unlock(); } @@ -777,7 +803,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, u16 sequence = le16_to_cpu(pkt->hdr.sequence); struct ieee80211_sta *sta; - if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE)) + if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue)) return; if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) @@ -790,9 +816,10 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); if (!WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) { - struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmsta->tid_data[tid].rate_n_flags = le32_to_cpu(tx_resp->initial_rate); + mvmsta->tid_data[tid].reduced_tpc = tx_resp->reduced_tpc; } rcu_read_unlock(); @@ -819,16 +846,12 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_mvm_ba_notif *ba_notif = (void *)pkt->data; struct sk_buff_head reclaimed_skbs; struct iwl_mvm_tid_data *tid_data; - struct ieee80211_tx_info *info; struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; - struct ieee80211_hdr *hdr; struct sk_buff *skb; int sta_id, tid, freed; - /* "flow" corresponds to Tx queue */ u16 scd_flow = le16_to_cpu(ba_notif->scd_flow); - /* "ssn" is start of block-ack Tx window, corresponds to index * (in Tx queue's circular buffer) of first TFD/frame in window */ u16 ba_resp_scd_ssn = le16_to_cpu(ba_notif->scd_ssn); @@ -846,7 +869,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } - mvmsta = (void *)sta->drv_priv; + mvmsta = iwl_mvm_sta_from_mac80211(sta); tid_data = &mvmsta->tid_data[tid]; if (WARN_ONCE(tid_data->txq_id != scd_flow, "Q %d, tid %d, flow %d", @@ -885,27 +908,33 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, freed = 0; skb_queue_walk(&reclaimed_skbs, skb) { - hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); if (ieee80211_is_data_qos(hdr->frame_control)) freed++; else WARN_ON_ONCE(1); - info = IEEE80211_SKB_CB(skb); iwl_trans_free_tx_cmd(mvm->trans, info->driver_data[1]); + memset(&info->status, 0, sizeof(info->status)); + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + info->flags |= IEEE80211_TX_STAT_ACK; + if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ - info = IEEE80211_SKB_CB(skb); - memset(&info->status, 0, sizeof(info->status)); - info->flags |= IEEE80211_TX_STAT_ACK; info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_notif->txed_2_done; info->status.ampdu_len = ba_notif->txed; - iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags, - info); + iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags, + info); + info->status.status_driver_data[0] = + (void *)(uintptr_t)tid_data->reduced_tpc; } } @@ -915,7 +944,7 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, while (!skb_queue_empty(&reclaimed_skbs)) { skb = __skb_dequeue(&reclaimed_skbs); - ieee80211_tx_status_ni(mvm->hw, skb); + ieee80211_tx_status(mvm->hw, skb); } return 0; @@ -929,7 +958,7 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, bool sync) .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), }; - u32 flags = sync ? CMD_SYNC : CMD_ASYNC; + u32 flags = sync ? 0 : CMD_ASYNC; ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, sizeof(flush_cmd), &flush_cmd); diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index a9c35749143..aa9fc77e841 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -64,6 +64,7 @@ #include "iwl-debug.h" #include "iwl-io.h" +#include "iwl-prph.h" #include "mvm.h" #include "fw-api-rs.h" @@ -143,7 +144,7 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, "cmd flags %x", cmd->flags)) return -EINVAL; - cmd->flags |= CMD_SYNC | CMD_WANT_SKB; + cmd->flags |= CMD_WANT_SKB; ret = iwl_trans_send_cmd(mvm->trans, cmd); if (ret == -ERFKILL) { @@ -168,8 +169,8 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, goto out_free_resp; } - resp_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (WARN_ON_ONCE(resp_len != sizeof(pkt->hdr) + sizeof(*resp))) { + resp_len = iwl_rx_packet_payload_len(pkt); + if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { ret = -EIO; goto out_free_resp; } @@ -289,8 +290,8 @@ u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx) return last_idx; } -static struct { - char *name; +static const struct { + const char *name; u8 num; } advanced_lookup[] = { { "NMI_INTERRUPT_WDG", 0x34 }, @@ -376,9 +377,67 @@ struct iwl_error_event_table { u32 flow_handler; /* FH read/write pointers, RX credit */ } __packed; +/* + * UMAC error struct - relevant starting from family 8000 chip. + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwl_umac_error_event_table { + u32 valid; /* (nonzero) valid, (0) log is empty */ + u32 error_id; /* type of error */ + u32 pc; /* program counter */ + u32 blink1; /* branch link */ + u32 blink2; /* branch link */ + u32 ilink1; /* interrupt link */ + u32 ilink2; /* interrupt link */ + u32 data1; /* error-specific data */ + u32 data2; /* error-specific data */ + u32 line; /* source code line of error */ + u32 umac_ver; /* umac version */ +} __packed; + #define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_ELEM_SIZE (7 * sizeof(u32)) +static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) +{ + struct iwl_trans *trans = mvm->trans; + struct iwl_umac_error_event_table table; + u32 base; + + base = mvm->umac_error_event_table; + + if (base < 0x800000 || base >= 0x80C000) { + IWL_ERR(mvm, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, + (mvm->cur_ucode == IWL_UCODE_INIT) + ? "Init" : "RT"); + return; + } + + iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + IWL_ERR(trans, "Start IWL Error Log Dump:\n"); + IWL_ERR(trans, "Status: 0x%08lX, count: %d\n", + mvm->status, table.valid); + } + + IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, + desc_lookup(table.error_id)); + IWL_ERR(mvm, "0x%08X | umac uPc\n", table.pc); + IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); + IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); + IWL_ERR(mvm, "0x%08X | umac interruptlink1\n", table.ilink1); + IWL_ERR(mvm, "0x%08X | umac interruptlink2\n", table.ilink2); + IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); + IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); + IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_ver); +} + void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) { struct iwl_trans *trans = mvm->trans; @@ -394,7 +453,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) base = mvm->fw->inst_errlog_ptr; } - if (base < 0x800000 || base >= 0x80C000) { + if (base < 0x800000) { IWL_ERR(mvm, "Not valid error log pointer 0x%08X for %s uCode\n", base, @@ -411,6 +470,10 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) mvm->status, table.valid); } + /* Do not change this output - scripts rely on it */ + + IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version); + trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low, table.data1, table.data2, table.data3, table.blink1, table.blink2, table.ilink1, @@ -451,30 +514,75 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp); IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler); + + if (mvm->support_umac_log) + iwl_mvm_dump_umac_error_log(mvm); } -void iwl_mvm_dump_sram(struct iwl_mvm *mvm) +#ifdef CONFIG_IWLWIFI_DEBUGFS +void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm) { const struct fw_img *img; - int ofs, len = 0; - u8 *buf; + u32 ofs, sram_len; + void *sram; - if (!mvm->ucode_loaded) + if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump) 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; + + sram = kzalloc(sram_len, GFP_ATOMIC); + if (!sram) + return; + + iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len); + mvm->fw_error_sram = sram; + mvm->fw_error_sram_len = sram_len; +} + +void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm) +{ + int i, reg_val; + unsigned long flags; + + if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump) + return; + + /* reading buffer size */ + reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); + mvm->fw_error_rxf_len = + (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS; + + /* the register holds the value divided by 128 */ + mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7; + + if (!mvm->fw_error_rxf_len) + return; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) + mvm->fw_error_rxf = kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC); + if (!mvm->fw_error_rxf) { + mvm->fw_error_rxf_len = 0; return; + } - iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len); - iwl_print_hex_error(mvm->trans, buf, len); + if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) { + kfree(mvm->fw_error_rxf); + mvm->fw_error_rxf = NULL; + mvm->fw_error_rxf_len = 0; + return; + } - kfree(buf); + for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) { + iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR, + i * sizeof(u32)); + mvm->fw_error_rxf[i] = + iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR); + } + iwl_trans_release_nic_access(mvm->trans, &flags); } +#endif /** * iwl_mvm_send_lq_cmd() - Send link quality command @@ -486,22 +594,18 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm) * this case to clear the state indicating that station creation is in * progress. */ -int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, - u8 flags, bool init) +int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init) { struct iwl_host_cmd cmd = { .id = LQ_CMD, .len = { sizeof(struct iwl_lq_cmd), }, - .flags = flags, + .flags = init ? 0 : CMD_ASYNC, .data = { lq, }, }; if (WARN_ON(lq->sta_id == IWL_MVM_STATION_COUNT)) return -EINVAL; - if (WARN_ON(init && (cmd.flags & CMD_ASYNC))) - return -EINVAL; - return iwl_mvm_send_cmd(mvm, &cmd); } @@ -518,10 +622,20 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_smps_mode smps_request) { struct iwl_mvm_vif *mvmvif; - enum ieee80211_smps_mode smps_mode = IEEE80211_SMPS_AUTOMATIC; + enum ieee80211_smps_mode smps_mode; int i; lockdep_assert_held(&mvm->mutex); + + /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ + if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + return; + + if (vif->type == NL80211_IFTYPE_AP) + smps_mode = IEEE80211_SMPS_OFF; + else + smps_mode = IEEE80211_SMPS_AUTOMATIC; + mvmvif = iwl_mvm_vif_from_mac80211(vif); mvmvif->smps_requests[req_type] = smps_request; for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { @@ -535,3 +649,96 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ieee80211_request_smps(vif, smps_mode); } + +static void iwl_mvm_diversity_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + bool *result = _data; + int i; + + for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { + if (mvmvif->smps_requests[i] == IEEE80211_SMPS_STATIC || + mvmvif->smps_requests[i] == IEEE80211_SMPS_DYNAMIC) + *result = false; + } +} + +bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) +{ + bool result = true; + + lockdep_assert_held(&mvm->mutex); + + if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + return false; + + if (!mvm->cfg->rx_with_siso_diversity) + return false; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_diversity_iter, &result); + + return result; +} + +int iwl_mvm_update_low_latency(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int res; + + lockdep_assert_held(&mvm->mutex); + + if (mvmvif->low_latency == value) + return 0; + + mvmvif->low_latency = value; + + res = iwl_mvm_update_quotas(mvm, NULL); + if (res) + return res; + + iwl_mvm_bt_coex_vif_change(mvm); + + return iwl_mvm_power_update_mac(mvm); +} + +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; +} + +static void iwl_mvm_idle_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *idle = _data; + + if (!vif->bss_conf.idle) + *idle = false; +} + +bool iwl_mvm_is_idle(struct iwl_mvm *mvm) +{ + bool idle = true; + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_idle_iter, &idle); + + return idle; +} diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 26108a1a29f..98950e45c7b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -66,6 +66,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/pci-aspm.h> +#include <linux/acpi.h> #include "iwl-trans.h" #include "iwl-drv.h" @@ -268,7 +269,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { #endif /* CONFIG_IWLDVM */ #if IS_ENABLED(CONFIG_IWLMVM) -/* 7000 Series */ +/* 7260 Series */ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, @@ -297,6 +298,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)}, {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, @@ -350,12 +354,131 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)}, + +/* 7265 Series */ + {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, + +/* 8000 Series */ + {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} }; MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); +#ifdef CONFIG_ACPI +#define SPL_METHOD "SPLC" +#define SPL_DOMAINTYPE_MODULE BIT(0) +#define SPL_DOMAINTYPE_WIFI BIT(1) +#define SPL_DOMAINTYPE_WIGIG BIT(2) +#define SPL_DOMAINTYPE_RFEM BIT(3) + +static u64 splx_get_pwr_limit(struct iwl_trans *trans, union acpi_object *splx) +{ + union acpi_object *limits, *domain_type, *power_limit; + + if (splx->type != ACPI_TYPE_PACKAGE || + splx->package.count != 2 || + splx->package.elements[0].type != ACPI_TYPE_INTEGER || + splx->package.elements[0].integer.value != 0) { + IWL_ERR(trans, "Unsupported splx structure\n"); + return 0; + } + + limits = &splx->package.elements[1]; + if (limits->type != ACPI_TYPE_PACKAGE || + limits->package.count < 2 || + limits->package.elements[0].type != ACPI_TYPE_INTEGER || + limits->package.elements[1].type != ACPI_TYPE_INTEGER) { + IWL_ERR(trans, "Invalid limits element\n"); + return 0; + } + + domain_type = &limits->package.elements[0]; + power_limit = &limits->package.elements[1]; + if (!(domain_type->integer.value & SPL_DOMAINTYPE_WIFI)) { + IWL_DEBUG_INFO(trans, "WiFi power is not limited\n"); + return 0; + } + + return power_limit->integer.value; +} + +static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) +{ + acpi_handle pxsx_handle; + acpi_handle handle; + struct acpi_buffer splx = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + + pxsx_handle = ACPI_HANDLE(&pdev->dev); + if (!pxsx_handle) { + IWL_DEBUG_INFO(trans, + "Could not retrieve root port ACPI handle\n"); + return; + } + + /* Get the method's handle */ + status = acpi_get_handle(pxsx_handle, (acpi_string)SPL_METHOD, &handle); + if (ACPI_FAILURE(status)) { + IWL_DEBUG_INFO(trans, "SPL method not found\n"); + return; + } + + /* Call SPLC with no arguments */ + status = acpi_evaluate_object(handle, NULL, NULL, &splx); + if (ACPI_FAILURE(status)) { + IWL_ERR(trans, "SPLC invocation failed (0x%x)\n", status); + return; + } + + trans->dflt_pwr_limit = splx_get_pwr_limit(trans, splx.pointer); + IWL_DEBUG_INFO(trans, "Default power limit set to %lld\n", + trans->dflt_pwr_limit); + kfree(splx.pointer); +} + +#else /* CONFIG_ACPI */ +static void set_dflt_pwr_limit(struct iwl_trans *trans, struct pci_dev *pdev) {} +#endif + /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 @@ -380,6 +503,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_free_trans; } + set_dflt_pwr_limit(iwl_trans, pdev); + /* register transport layer debugfs here */ ret = iwl_trans_dbgfs_register(iwl_trans, iwl_trans->dbgfs_dir); if (ret) @@ -391,7 +516,6 @@ out_free_drv: iwl_drv_stop(trans_pcie->drv); out_free_trans: iwl_trans_pcie_free(iwl_trans); - pci_set_drvdata(pdev, NULL); return ret; } @@ -402,8 +526,6 @@ static void iwl_pci_remove(struct pci_dev *pdev) iwl_drv_stop(trans_pcie->drv); iwl_trans_pcie_free(trans); - - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM_SLEEP @@ -441,7 +563,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 fa22639b63c..6c22b23a284 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -102,7 +102,7 @@ struct iwl_rxq { u32 write_actual; struct list_head rx_free; struct list_head rx_used; - int need_update; + bool need_update; struct iwl_rb_status *rb_stts; dma_addr_t rb_stts_dma; spinlock_t lock; @@ -117,21 +117,19 @@ struct iwl_dma_ptr { /** * iwl_queue_inc_wrap - increment queue index, wrap back to beginning * @index -- current index - * @n_bd -- total number of entries in queue (must be power of 2) */ -static inline int iwl_queue_inc_wrap(int index, int n_bd) +static inline int iwl_queue_inc_wrap(int index) { - return ++index & (n_bd - 1); + return ++index & (TFD_QUEUE_SIZE_MAX - 1); } /** * iwl_queue_dec_wrap - decrement queue index, wrap back to end * @index -- current index - * @n_bd -- total number of entries in queue (must be power of 2) */ -static inline int iwl_queue_dec_wrap(int index, int n_bd) +static inline int iwl_queue_dec_wrap(int index) { - return --index & (n_bd - 1); + return --index & (TFD_QUEUE_SIZE_MAX - 1); } struct iwl_cmd_meta { @@ -145,13 +143,13 @@ struct iwl_cmd_meta { * * Contains common data for Rx and Tx queues. * - * Note the difference between n_bd and n_window: the hardware - * always assumes 256 descriptors, so n_bd is always 256 (unless + * Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware + * always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless * there might be HW changes in the future). For the normal TX * queues, n_window, which is the size of the software queue data * is also 256; however, for the command queue, n_window is only * 32 since we don't need so many commands pending. Since the HW - * still uses 256 BDs for DMA though, n_bd stays 256. As a result, + * still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256. As a result, * the software buffers (in the variables @meta, @txb in struct * iwl_txq) only have 32 entries, while the HW buffers (@tfds in * the same struct) have 256. @@ -162,7 +160,6 @@ struct iwl_cmd_meta { * data is a window overlayed over the HW queue. */ struct iwl_queue { - int n_bd; /* number of BDs in this queue */ int write_ptr; /* 1-st empty entry (index) host_w*/ int read_ptr; /* last used entry (index) host_r*/ /* use for monitoring and recovering the stuck queue */ @@ -231,7 +228,7 @@ struct iwl_txq { spinlock_t lock; struct timer_list stuck_timer; struct iwl_trans_pcie *trans_pcie; - u8 need_update; + bool need_update; u8 active; bool ampdu; }; @@ -256,13 +253,13 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @hw_base: pci hardware address support * @ucode_write_complete: indicates that the ucode has been copied. * @ucode_write_waitq: wait queue for uCode load - * @status - transport specific status flags * @cmd_queue - command queue number * @rx_buf_size_8k: 8 kB RX buffer size * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @rx_page_order: page order for receive buffer size * @wd_timeout: queue watchdog timeout (jiffies) * @reg_lock: protect hw register access + * @cmd_in_flight: true when we have a host command in flight */ struct iwl_trans_pcie { struct iwl_rxq rxq; @@ -270,11 +267,13 @@ struct iwl_trans_pcie { struct iwl_trans *trans; struct iwl_drv *drv; + struct net_device napi_dev; + struct napi_struct napi; + /* INT ICT Table */ __le32 *ict_tbl; dma_addr_t ict_tbl_dma; int ict_index; - u32 inta; bool use_ict; struct isr_statistics isr_stats; @@ -296,7 +295,6 @@ struct iwl_trans_pcie { wait_queue_head_t ucode_write_waitq; wait_queue_head_t wait_command_queue; - unsigned long status; u8 cmd_queue; u8 cmd_fifo; u8 n_no_reclaim_cmds; @@ -306,31 +304,14 @@ struct iwl_trans_pcie { bool bc_table_dword; u32 rx_page_order; - const char **command_names; + const char *const *command_names; /* queue watchdog */ unsigned long wd_timeout; /*protect hw register */ spinlock_t reg_lock; -}; - -/** - * enum iwl_pcie_status: status of the PCIe transport - * @STATUS_HCMD_ACTIVE: a SYNC command is being processed - * @STATUS_DEVICE_ENABLED: APM is enabled - * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) - * @STATUS_INT_ENABLED: interrupts are enabled - * @STATUS_RFKILL: the HW RFkill switch is in KILL position - * @STATUS_FW_ERROR: the fw is in error state - */ -enum iwl_pcie_status { - STATUS_HCMD_ACTIVE, - STATUS_DEVICE_ENABLED, - STATUS_TPOWER_PMI, - STATUS_INT_ENABLED, - STATUS_RFKILL, - STATUS_FW_ERROR, + bool cmd_in_flight; }; #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ @@ -363,7 +344,7 @@ void iwl_pcie_rx_free(struct iwl_trans *trans); /***************************************************** * ICT - interrupt handling ******************************************************/ -irqreturn_t iwl_pcie_isr_ict(int irq, void *data); +irqreturn_t iwl_pcie_isr(int irq, void *data); int iwl_pcie_alloc_ict(struct iwl_trans *trans); void iwl_pcie_free_ict(struct iwl_trans *trans); void iwl_pcie_reset_ict(struct iwl_trans *trans); @@ -381,7 +362,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int txq_id); -void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq); +void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); void iwl_pcie_hcmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, int handler_status); @@ -389,6 +370,13 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); +static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + return le16_to_cpu(tb->hi_n_len) >> 4; +} + /***************************************************** * Error handling ******************************************************/ @@ -399,8 +387,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans); ******************************************************/ static inline void iwl_disable_interrupts(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); + clear_bit(STATUS_INT_ENABLED, &trans->status); /* disable interrupts from uCode/NIC to host */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); @@ -417,14 +404,18 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &trans_pcie->status); + set_bit(STATUS_INT_ENABLED, &trans->status); + trans_pcie->inta_mask = CSR_INI_SET_MASK; iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); } static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n"); - iwl_write32(trans, CSR_INT_MASK, CSR_INT_BIT_RF_KILL); + trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL; + iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); } static inline void iwl_wake_queue(struct iwl_trans *trans, @@ -477,4 +468,33 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); } +static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, + u32 reg, u32 mask, u32 value) +{ + u32 v; + +#ifdef CONFIG_IWLWIFI_DEBUG + WARN_ON_ONCE(value & ~mask); +#endif + + v = iwl_read32(trans, reg); + v &= ~mask; + v |= value; + iwl_write32(trans, reg, v); +} + +static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans, + u32 reg, u32 mask) +{ + __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0); +} + +static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, + u32 reg, u32 mask) +{ + __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 3f237b42eb3..a2698e5e062 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -145,55 +145,52 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans) /* * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue */ -static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, - struct iwl_rxq *rxq) +static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans) { - unsigned long flags; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; u32 reg; - spin_lock_irqsave(&rxq->lock, flags); + lockdep_assert_held(&rxq->lock); - if (rxq->need_update == 0) - goto exit_unlock; + /* + * explicitly wake up the NIC if: + * 1. shadow registers aren't enabled + * 2. there is a chance that the NIC is asleep + */ + if (!trans->cfg->base_params->shadow_reg_enable && + test_bit(STATUS_TPOWER_PMI, &trans->status)) { + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, "Rx queue requesting wakeup, GP1 = 0x%x\n", + reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + rxq->need_update = true; + return; + } + } - if (trans->cfg->base_params->shadow_reg_enable) { - /* shadow register enabled */ - /* Device expects a multiple of 8 */ - rxq->write_actual = (rxq->write & ~0x7); - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); - } else { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - /* If power-saving is in use, make sure device is awake */ - if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, - "Rx queue requesting wakeup," - " GP1 = 0x%x\n", reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - goto exit_unlock; - } + rxq->write_actual = round_down(rxq->write, 8); + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); +} - rxq->write_actual = (rxq->write & ~0x7); - iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, - rxq->write_actual); +static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; - /* Else device is assumed to be awake */ - } else { - /* Device expects a multiple of 8 */ - rxq->write_actual = (rxq->write & ~0x7); - iwl_write_direct32(trans, FH_RSCSR_CHNL0_WPTR, - rxq->write_actual); - } - } - rxq->need_update = 0; + spin_lock(&rxq->lock); + + if (!rxq->need_update) + goto exit_unlock; + + iwl_pcie_rxq_inc_wr_ptr(trans); + rxq->need_update = false; exit_unlock: - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); } /* @@ -212,7 +209,6 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rx_mem_buffer *rxb; - unsigned long flags; /* * If the device isn't enabled - not need to try to add buffers... @@ -222,10 +218,10 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) * stopped, we cannot access the HW (in particular not prph). * So don't try to restock if the APM has been already stopped. */ - if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) return; - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) { /* The overwritten rxb must be a used one */ rxb = rxq->queue[rxq->write]; @@ -242,7 +238,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; } - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); /* If the pre-allocated buffer pool is dropping low, schedule to * refill it */ if (rxq->free_count <= RX_LOW_WATERMARK) @@ -251,10 +247,9 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) /* If we've added more space for the firmware to place data, tell it. * Increment device's write pointer in multiples of 8. */ if (rxq->write_actual != (rxq->write & ~0x7)) { - spin_lock_irqsave(&rxq->lock, flags); - rxq->need_update = 1; - spin_unlock_irqrestore(&rxq->lock, flags); - iwl_pcie_rxq_inc_wr_ptr(trans, rxq); + spin_lock(&rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans); + spin_unlock(&rxq->lock); } } @@ -273,16 +268,15 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rx_mem_buffer *rxb; struct page *page; - unsigned long flags; gfp_t gfp_mask = priority; while (1) { - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); return; } - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); if (rxq->free_count > RX_LOW_WATERMARK) gfp_mask |= __GFP_NOWARN; @@ -311,17 +305,17 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) return; } - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); if (list_empty(&rxq->rx_used)) { - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); __free_pages(page, trans_pcie->rx_page_order); return; } rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer, list); list_del(&rxb->list); - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); BUG_ON(rxb->page); rxb->page = page; @@ -332,9 +326,9 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) DMA_FROM_DEVICE); if (dma_mapping_error(trans->dev, rxb->page_dma)) { rxb->page = NULL; - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); list_add(&rxb->list, &rxq->rx_used); - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); __free_pages(page, trans_pcie->rx_page_order); return; } @@ -343,12 +337,12 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) /* and also 256 byte aligned! */ BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); list_add_tail(&rxb->list, &rxq->rx_free); rxq->free_count++; - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); } } @@ -379,21 +373,9 @@ static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans) * Also restock the Rx queue via iwl_pcie_rxq_restock. * This is called as a scheduled work item (except for during initialization) */ -static void iwl_pcie_rx_replenish(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - - iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_pcie_rxq_restock(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); -} - -static void iwl_pcie_rx_replenish_now(struct iwl_trans *trans) +static void iwl_pcie_rx_replenish(struct iwl_trans *trans, gfp_t gfp) { - iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); + iwl_pcie_rxq_alloc_rbs(trans, gfp); iwl_pcie_rxq_restock(trans); } @@ -403,7 +385,7 @@ static void iwl_pcie_rx_replenish_work(struct work_struct *data) struct iwl_trans_pcie *trans_pcie = container_of(data, struct iwl_trans_pcie, rx_replenish); - iwl_pcie_rx_replenish(trans_pcie->trans); + iwl_pcie_rx_replenish(trans_pcie->trans, GFP_KERNEL); } static int iwl_pcie_rx_alloc(struct iwl_trans *trans) @@ -489,6 +471,10 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) /* Set interrupt coalescing timer to default (2048 usecs) */ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); + + /* W/A for interrupt coalescing bug in 7260 and 3160 */ + if (trans->cfg->host_interrupt_operation_mode) + iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); } static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) @@ -510,7 +496,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *rxq = &trans_pcie->rxq; int i, err; - unsigned long flags; if (!rxq->bd) { err = iwl_pcie_rx_alloc(trans); @@ -518,7 +503,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) return err; } - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); INIT_WORK(&trans_pcie->rx_replenish, iwl_pcie_rx_replenish_work); @@ -534,16 +519,15 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) rxq->read = rxq->write = 0; rxq->write_actual = 0; memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); - iwl_pcie_rx_replenish(trans); + iwl_pcie_rx_replenish(trans, GFP_KERNEL); iwl_pcie_rx_hw_init(trans, rxq); - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - rxq->need_update = 1; - iwl_pcie_rxq_inc_wr_ptr(trans, rxq); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_lock(&rxq->lock); + iwl_pcie_rxq_inc_wr_ptr(trans); + spin_unlock(&rxq->lock); return 0; } @@ -552,7 +536,6 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *rxq = &trans_pcie->rxq; - unsigned long flags; /*if rxq->bd is NULL, it means that nothing has been allocated, * exit now */ @@ -563,9 +546,9 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) cancel_work_sync(&trans_pcie->rx_replenish); - spin_lock_irqsave(&rxq->lock, flags); + spin_lock(&rxq->lock); iwl_pcie_rxq_free_rbs(trans); - spin_unlock_irqrestore(&rxq->lock, flags); + spin_unlock(&rxq->lock); dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, rxq->bd, rxq->bd_dma); @@ -588,7 +571,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; - unsigned long flags; bool page_stolen = false; int max_len = PAGE_SIZE << trans_pcie->rx_page_order; u32 offset = 0; @@ -621,7 +603,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd), pkt->hdr.cmd); - len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + len = iwl_rx_packet_len(pkt); len += sizeof(u32); /* account for status word */ trace_iwlwifi_dev_rx(trans->dev, trans, pkt, len); trace_iwlwifi_dev_rx_data(trans->dev, trans, pkt, len); @@ -690,7 +672,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, /* Reuse the page if possible. For notification packets and * SKBs that fail to Rx correctly, add them back into the * rx_free list for reuse later. */ - spin_lock_irqsave(&rxq->lock, flags); if (rxb->page != NULL) { rxb->page_dma = dma_map_page(trans->dev, rxb->page, 0, @@ -711,7 +692,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, } } else list_add_tail(&rxb->list, &rxq->rx_used); - spin_unlock_irqrestore(&rxq->lock, flags); } /* @@ -726,6 +706,8 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans) u32 count = 8; int total_empty; +restart: + spin_lock(&rxq->lock); /* uCode's read index (stored in shared DRAM) indicates the last Rx * buffer that the driver may process (last buffer filled by ucode). */ r = le16_to_cpu(ACCESS_ONCE(rxq->rb_stts->closed_rb_num)) & 0x0FFF; @@ -760,18 +742,25 @@ static void iwl_pcie_rx_handle(struct iwl_trans *trans) count++; if (count >= 8) { rxq->read = i; - iwl_pcie_rx_replenish_now(trans); + spin_unlock(&rxq->lock); + iwl_pcie_rx_replenish(trans, GFP_ATOMIC); count = 0; + goto restart; } } } /* Backtrack one entry */ rxq->read = i; + spin_unlock(&rxq->lock); + if (fill_rx) - iwl_pcie_rx_replenish_now(trans); + iwl_pcie_rx_replenish(trans, GFP_ATOMIC); else iwl_pcie_rxq_restock(trans); + + if (trans_pcie->napi.poll) + napi_gro_flush(&trans_pcie->napi, false); } /* @@ -787,7 +776,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & APMG_PS_CTRL_VAL_RESET_REQ))) { - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); iwl_op_mode_wimax_active(trans->op_mode); wake_up(&trans_pcie->wait_command_queue); return; @@ -796,13 +785,94 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) iwl_pcie_dump_csr(trans); iwl_dump_fh(trans, NULL); - set_bit(STATUS_FW_ERROR, &trans_pcie->status); - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - wake_up(&trans_pcie->wait_command_queue); - local_bh_disable(); - iwl_op_mode_nic_error(trans->op_mode); + /* The STATUS_FW_ERROR bit is set in this function. This must happen + * before we wake up the command caller, to ensure a proper cleanup. */ + iwl_trans_fw_error(trans); local_bh_enable(); + + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + wake_up(&trans_pcie->wait_command_queue); +} + +static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans) +{ + u32 inta; + + lockdep_assert_held(&IWL_TRANS_GET_PCIE_TRANS(trans)->irq_lock); + + trace_iwlwifi_dev_irq(trans->dev); + + /* Discover which interrupts are active/pending */ + inta = iwl_read32(trans, CSR_INT); + + /* the thread will service interrupts and re-enable them */ + return inta; +} + +/* a device (PCI-E) page is 4096 bytes long */ +#define ICT_SHIFT 12 +#define ICT_SIZE (1 << ICT_SHIFT) +#define ICT_COUNT (ICT_SIZE / sizeof(u32)) + +/* interrupt handler using ict table, with this interrupt driver will + * stop using INTA register to get device's interrupt, reading this register + * is expensive, device will write interrupts in ICT dram table, increment + * index then will fire interrupt to driver, driver will OR all ICT table + * entries from current index up to table entry with 0 value. the result is + * the interrupt we need to service, driver will set the entries back to 0 and + * set index. + */ +static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 inta; + u32 val = 0; + u32 read; + + trace_iwlwifi_dev_irq(trans->dev); + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); + if (!read) + return 0; + + /* + * Collect all entries up to the first 0, starting from ict_index; + * note we already read at ict_index. + */ + do { + val |= read; + IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", + trans_pcie->ict_index, read); + trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; + trans_pcie->ict_index = + ((trans_pcie->ict_index + 1) & (ICT_COUNT - 1)); + + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, + read); + } while (read); + + /* We should not get this value, just ignore it. */ + if (val == 0xffffffff) + val = 0; + + /* + * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit + * (bit 15 before shifting it to 31) to clear when using interrupt + * coalescing. fortunately, bits 18 and 19 stay set when this happens + * so we use them to decide on the real state of the Rx bit. + * In order words, bit 15 is set if bit 18 or bit 19 are set. + */ + if (val & 0xC0000) + val |= 0x8000; + + inta = (0xff & val) | ((0xff00 & val) << 16); + return inta; } irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) @@ -812,12 +882,60 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) struct isr_statistics *isr_stats = &trans_pcie->isr_stats; u32 inta = 0; u32 handled = 0; - unsigned long flags; - u32 i; lock_map_acquire(&trans->sync_cmd_lockdep_map); - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); + + /* dram interrupt table not set yet, + * use legacy interrupt. + */ + if (likely(trans_pcie->use_ict)) + inta = iwl_pcie_int_cause_ict(trans); + else + inta = iwl_pcie_int_cause_non_ict(trans); + + if (iwl_have_debug_level(IWL_DL_ISR)) { + IWL_DEBUG_ISR(trans, + "ISR inta 0x%08x, enabled 0x%08x(sw), enabled(hw) 0x%08x, fh 0x%08x\n", + inta, trans_pcie->inta_mask, + iwl_read32(trans, CSR_INT_MASK), + iwl_read32(trans, CSR_FH_INT_STATUS)); + if (inta & (~trans_pcie->inta_mask)) + IWL_DEBUG_ISR(trans, + "We got a masked interrupt (0x%08x)\n", + inta & (~trans_pcie->inta_mask)); + } + + inta &= trans_pcie->inta_mask; + + /* + * Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. + */ + if (unlikely(!inta)) { + IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); + /* + * Re-enable interrupts here since we don't + * have anything to service + */ + if (test_bit(STATUS_INT_ENABLED, &trans->status)) + iwl_enable_interrupts(trans); + spin_unlock(&trans_pcie->irq_lock); + lock_map_release(&trans->sync_cmd_lockdep_map); + return IRQ_NONE; + } + + if (unlikely(inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { + /* + * Hardware disappeared. It might have + * already raised an interrupt. + */ + IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); + spin_unlock(&trans_pcie->irq_lock); + goto out; + } /* Ack/clear/reset pending uCode interrupts. * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, @@ -830,19 +948,13 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) * hardware bugs here by ACKing all the possible interrupts so that * interrupt coalescing can still be achieved. */ - iwl_write32(trans, CSR_INT, - trans_pcie->inta | ~trans_pcie->inta_mask); - - inta = trans_pcie->inta; + iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask); if (iwl_have_debug_level(IWL_DL_ISR)) IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n", inta, iwl_read32(trans, CSR_INT_MASK)); - /* saved interrupt in inta variable now we can reset trans_pcie->inta */ - trans_pcie->inta = 0; - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); /* Now service all interrupt bits discovered above. */ if (inta & CSR_INT_BIT_HW_ERR) { @@ -887,16 +999,16 @@ 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_pcie->status); - if (test_and_clear_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status)) + set_bit(STATUS_RFKILL, &trans->status); + if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status)) IWL_DEBUG_RF_KILL(trans, "Rfkill while SYNC HCMD in flight\n"); wake_up(&trans_pcie->wait_command_queue); } else { - clear_bit(STATUS_RFKILL, &trans_pcie->status); + clear_bit(STATUS_RFKILL, &trans->status); } handled |= CSR_INT_BIT_RF_KILL; @@ -921,9 +1033,8 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) /* uCode wakes up after power-down sleep */ if (inta & CSR_INT_BIT_WAKEUP) { IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); - iwl_pcie_rxq_inc_wr_ptr(trans, &trans_pcie->rxq); - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) - iwl_pcie_txq_inc_wr_ptr(trans, &trans_pcie->txq[i]); + iwl_pcie_rxq_check_wrptr(trans); + iwl_pcie_txq_check_wrptrs(trans); isr_stats->wakeup++; @@ -961,8 +1072,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) iwl_write8(trans, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_DIS); - iwl_pcie_rx_handle(trans); - /* * Enable periodic interrupt in 8 msec only if we received * real RX interrupt (instead of just periodic int), to catch @@ -975,6 +1084,10 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) CSR_INT_PERIODIC_ENA); isr_stats->rx++; + + local_bh_disable(); + iwl_pcie_rx_handle(trans); + local_bh_enable(); } /* This "Tx" DMA channel is used only for loading uCode */ @@ -1000,7 +1113,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) /* Re-enable all interrupts */ /* only Re-enable if disabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status)) + if (test_bit(STATUS_INT_ENABLED, &trans->status)) iwl_enable_interrupts(trans); /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) @@ -1017,11 +1130,6 @@ out: * ******************************************************************************/ -/* a device (PCI-E) page is 4096 bytes long */ -#define ICT_SHIFT 12 -#define ICT_SIZE (1 << ICT_SHIFT) -#define ICT_COUNT (ICT_SIZE / sizeof(u32)) - /* Free dram table */ void iwl_pcie_free_ict(struct iwl_trans *trans) { @@ -1046,7 +1154,7 @@ int iwl_pcie_alloc_ict(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); trans_pcie->ict_tbl = - dma_alloc_coherent(trans->dev, ICT_SIZE, + dma_zalloc_coherent(trans->dev, ICT_SIZE, &trans_pcie->ict_tbl_dma, GFP_KERNEL); if (!trans_pcie->ict_tbl) @@ -1058,17 +1166,10 @@ int iwl_pcie_alloc_ict(struct iwl_trans *trans) return -EINVAL; } - IWL_DEBUG_ISR(trans, "ict dma addr %Lx\n", - (unsigned long long)trans_pcie->ict_tbl_dma); - - IWL_DEBUG_ISR(trans, "ict vir addr %p\n", trans_pcie->ict_tbl); - - /* reset table and index to all 0 */ - memset(trans_pcie->ict_tbl, 0, ICT_SIZE); - trans_pcie->ict_index = 0; + IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n", + (unsigned long long)trans_pcie->ict_tbl_dma, + trans_pcie->ict_tbl); - /* add periodic RX interrupt */ - trans_pcie->inta_mask |= CSR_INT_BIT_RX_PERIODIC; return 0; } @@ -1079,12 +1180,11 @@ void iwl_pcie_reset_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 val; - unsigned long flags; if (!trans_pcie->ict_tbl) return; - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); memset(trans_pcie->ict_tbl, 0, ICT_SIZE); @@ -1101,124 +1201,26 @@ void iwl_pcie_reset_ict(struct iwl_trans *trans) trans_pcie->ict_index = 0; iwl_write32(trans, CSR_INT, trans_pcie->inta_mask); iwl_enable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); } /* Device is going down disable ict interrupt usage */ void iwl_pcie_disable_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); trans_pcie->use_ict = false; - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); } -/* legacy (non-ICT) ISR. Assumes that trans_pcie->irq_lock is held */ -static irqreturn_t iwl_pcie_isr(int irq, void *data) +irqreturn_t iwl_pcie_isr(int irq, void *data) { struct iwl_trans *trans = data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 inta, inta_mask; - irqreturn_t ret = IRQ_NONE; - - lockdep_assert_held(&trans_pcie->irq_lock); - - trace_iwlwifi_dev_irq(trans->dev); - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the irq thread will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. */ - inta_mask = iwl_read32(trans, CSR_INT_MASK); - iwl_write32(trans, CSR_INT_MASK, 0x00000000); - - /* Discover which interrupts are active/pending */ - inta = iwl_read32(trans, CSR_INT); - - if (inta & (~inta_mask)) { - IWL_DEBUG_ISR(trans, - "We got a masked interrupt (0x%08x)...Ack and ignore\n", - inta & (~inta_mask)); - iwl_write32(trans, CSR_INT, inta & (~inta_mask)); - inta &= inta_mask; - } - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - if (!inta) { - IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); - goto none; - } - - if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { - /* Hardware disappeared. It might have already raised - * an interrupt */ - IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta); - return IRQ_HANDLED; - } - - if (iwl_have_debug_level(IWL_DL_ISR)) - IWL_DEBUG_ISR(trans, - "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", - inta, inta_mask, - iwl_read32(trans, CSR_FH_INT_STATUS)); - - trans_pcie->inta |= inta; - /* the thread will service interrupts and re-enable them */ - if (likely(inta)) - return IRQ_WAKE_THREAD; - - ret = IRQ_HANDLED; - -none: - /* re-enable interrupts here since we don't have anything to service. */ - /* only Re-enable if disabled by irq and no schedules tasklet. */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - - return ret; -} - -/* interrupt handler using ict table, with this interrupt driver will - * stop using INTA register to get device's interrupt, reading this register - * is expensive, device will write interrupts in ICT dram table, increment - * index then will fire interrupt to driver, driver will OR all ICT table - * entries from current index up to table entry with 0 value. the result is - * the interrupt we need to service, driver will set the entries back to 0 and - * set index. - */ -irqreturn_t iwl_pcie_isr_ict(int irq, void *data) -{ - struct iwl_trans *trans = data; - struct iwl_trans_pcie *trans_pcie; - u32 inta; - u32 val = 0; - u32 read; - unsigned long flags; - irqreturn_t ret = IRQ_NONE; if (!trans) return IRQ_NONE; - trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* dram interrupt table not set yet, - * use legacy interrupt. - */ - if (unlikely(!trans_pcie->use_ict)) { - ret = iwl_pcie_isr(irq, data); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return ret; - } - - trace_iwlwifi_dev_irq(trans->dev); - /* Disable (but don't clear!) interrupts here to avoid * back-to-back ISRs and sporadic interrupts from our NIC. * If we have something to service, the tasklet will re-enable ints. @@ -1226,73 +1228,5 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); - trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read); - if (!read) { - IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); - goto none; - } - - /* - * Collect all entries up to the first 0, starting from ict_index; - * note we already read at ict_index. - */ - do { - val |= read; - IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", - trans_pcie->ict_index, read); - trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; - trans_pcie->ict_index = - iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); - - read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); - trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, - read); - } while (read); - - /* We should not get this value, just ignore it. */ - if (val == 0xffffffff) - val = 0; - - /* - * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit - * (bit 15 before shifting it to 31) to clear when using interrupt - * coalescing. fortunately, bits 18 and 19 stay set when this happens - * so we use them to decide on the real state of the Rx bit. - * In order words, bit 15 is set if bit 18 or bit 19 are set. - */ - if (val & 0xC0000) - val |= 0x8000; - - inta = (0xff & val) | ((0xff00 & val) << 16); - IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled(sw) 0x%08x ict 0x%08x\n", - inta, trans_pcie->inta_mask, val); - if (iwl_have_debug_level(IWL_DL_ISR)) - IWL_DEBUG_ISR(trans, "enabled(hw) 0x%08x\n", - iwl_read32(trans, CSR_INT_MASK)); - - inta &= trans_pcie->inta_mask; - trans_pcie->inta |= inta; - - /* iwl_pcie_tasklet() will service interrupts and re-enable them */ - if (likely(inta)) { - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return IRQ_WAKE_THREAD; - } - - ret = IRQ_HANDLED; - - none: - /* re-enable interrupts here since we don't have anything to service. - * only Re-enable if disabled by irq. - */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && - !trans_pcie->inta) - iwl_enable_interrupts(trans); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - return ret; + return IRQ_WAKE_THREAD; } diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index c3f904d422b..788085bc65d 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 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 @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,33 +73,21 @@ #include "iwl-csr.h" #include "iwl-prph.h" #include "iwl-agn-hw.h" +#include "iwl-fw-error-dump.h" #include "internal.h" -static void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, - u32 reg, u32 mask, u32 value) +static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) { - u32 v; - -#ifdef CONFIG_IWLWIFI_DEBUG - WARN_ON_ONCE(value & ~mask); -#endif - - v = iwl_read32(trans, reg); - v &= ~mask; - v |= value; - iwl_write32(trans, reg, v); + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (2 << 28))); + return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); } -static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans, - u32 reg, u32 mask) +static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) { - __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0); -} - -static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans, - u32 reg, u32 mask) -{ - __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask); + 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) @@ -150,7 +138,6 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans) */ static int iwl_pcie_apm_init(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); @@ -160,8 +147,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) */ /* Disable L0S exit timer (platform NMI Work/Around) */ - iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); /* * Disable L0s without affecting L1; @@ -206,26 +194,165 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) goto out; } + if (trans->cfg->host_interrupt_operation_mode) { + /* + * This is a bit of an abuse - This is needed for 7260 / 3160 + * only check host_interrupt_operation_mode even if this is + * not related to host_interrupt_operation_mode. + * + * Enable the oscillator to count wake up time for L1 exit. This + * consumes slightly more power (100uA) - but allows to be sure + * that we wake up from L1 on time. + * + * This looks weird: read twice the same register, discard the + * value, set a bit, and yet again, read that same register + * just to discard the value. But that's the way the hardware + * seems to like it. + */ + iwl_read_prph(trans, OSC_CLK); + iwl_read_prph(trans, OSC_CLK); + iwl_set_bits_prph(trans, OSC_CLK, OSC_CLK_FORCE_CONTROL); + iwl_read_prph(trans, OSC_CLK); + iwl_read_prph(trans, OSC_CLK); + } + /* * Enable DMA clock and wait for it to stabilize. * - * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits - * do not disable clocks. This preserves any hardware bits already - * set by default in "CLK_CTRL_REG" after reset. + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" + * bits do not disable clocks. This preserves any hardware + * bits already set by default in "CLK_CTRL_REG" after reset. */ - iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); - udelay(20); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + iwl_write_prph(trans, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); - /* Disable L1-Active */ - iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + /* Disable L1-Active */ + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + /* Clear the interrupt in APMG if the NIC is in RFKILL */ + iwl_write_prph(trans, APMG_RTC_INT_STT_REG, + APMG_RTC_INT_STT_RFKILL); + } + + set_bit(STATUS_DEVICE_ENABLED, &trans->status); 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; @@ -246,14 +373,18 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) static void iwl_pcie_apm_stop(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); - clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); /* 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); @@ -270,18 +401,15 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans) static int iwl_pcie_nic_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; /* nic_init */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); iwl_pcie_apm_init(trans); - /* Set interrupt coalescing calibration timer to default (512 usecs) */ - iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); + spin_unlock(&trans_pcie->irq_lock); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - iwl_pcie_set_pwr(trans, false); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_pcie_set_pwr(trans, false); iwl_op_mode_nic_config(trans->op_mode); @@ -326,6 +454,7 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) { int ret; int t = 0; + int iter; IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); @@ -334,18 +463,23 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) if (ret >= 0) return 0; - /* If HW is not ready, prepare the conditions to check again */ - iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); + for (iter = 0; iter < 10; iter++) { + /* If HW is not ready, prepare the conditions to check again */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE); - do { - ret = iwl_pcie_set_hw_ready(trans); - if (ret >= 0) - return 0; + do { + ret = iwl_pcie_set_hw_ready(trans); + if (ret >= 0) + return 0; - usleep_range(200, 1000); - t += 200; - } while (t < 150000); + usleep_range(200, 1000); + t += 200; + } while (t < 150000); + msleep(25); + } + + IWL_DEBUG_INFO(trans, "got NIC after %d iterations\n", iter); return ret; } @@ -443,22 +577,181 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, return ret; } -static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, - const struct fw_img *image) +static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans, + const struct fw_img *image, + int cpu, + int *first_ucode_section) { + int shift_param; int i, ret = 0; + u32 last_read_idx = 0; - for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { - if (!image->sec[i].data) + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } + + for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { + last_read_idx = i; + + if (!image->sec[i].data || + image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) { + IWL_DEBUG_FW(trans, + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; + } + + if (i == (*first_ucode_section) + 1) + /* set CPU to started */ + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + LMPM_CPU_HDRS_LOADING_COMPLETED + << shift_param); + + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } + /* image loading complete */ + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param); + + *first_ucode_section = last_read_idx; + + return 0; +} + +static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, + const struct fw_img *image, + int cpu, + int *first_ucode_section) +{ + int shift_param; + int i, ret = 0; + u32 last_read_idx = 0; + + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } + + for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { + last_read_idx = i; + + if (!image->sec[i].data || + image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) { + IWL_DEBUG_FW(trans, + "Break since Data not valid or Empty section, sec = %d\n", + i); break; + } ret = iwl_pcie_load_section(trans, i, &image->sec[i]); if (ret) return ret; } - /* Remove all resets to allow NIC to operate */ - iwl_write32(trans, CSR_RESET, 0); + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + (LMPM_CPU_UCODE_LOADING_COMPLETED | + LMPM_CPU_HDRS_LOADING_COMPLETED | + LMPM_CPU_UCODE_LOADING_STARTED) << + shift_param); + + *first_ucode_section = last_read_idx; + + return 0; +} + +static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, + const struct fw_img *image) +{ + int ret = 0; + int first_ucode_section; + + IWL_DEBUG_FW(trans, + "working with %s image\n", + image->is_secure ? "Secured" : "Non Secured"); + IWL_DEBUG_FW(trans, + "working with %s CPU\n", + image->is_dual_cpus ? "Dual" : "Single"); + + /* configure the ucode to be ready to get the secured image */ + if (image->is_secure) { + /* set secure boot inspector addresses */ + iwl_write_prph(trans, + LMPM_SECURE_INSPECTOR_CODE_ADDR, + LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE); + + iwl_write_prph(trans, + LMPM_SECURE_INSPECTOR_DATA_ADDR, + LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE); + + /* set CPU1 header address */ + iwl_write_prph(trans, + LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR, + LMPM_SECURE_CPU1_HDR_MEM_SPACE); + + /* load to FW the binary Secured sections of CPU1 */ + ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1, + &first_ucode_section); + if (ret) + return ret; + + } else { + /* load to FW the binary Non secured sections of CPU1 */ + ret = iwl_pcie_load_cpu_sections(trans, image, 1, + &first_ucode_section); + if (ret) + return ret; + } + + if (image->is_dual_cpus) { + /* set CPU2 header address */ + iwl_write_prph(trans, + LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, + LMPM_SECURE_CPU2_HDR_MEM_SPACE); + + /* load to FW the binary sections of CPU2 */ + if (image->is_secure) + ret = iwl_pcie_load_cpu_secured_sections( + trans, image, 2, + &first_ucode_section); + else + ret = iwl_pcie_load_cpu_sections(trans, image, 2, + &first_ucode_section); + if (ret) + return ret; + } + + /* release CPU reset */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); + else + iwl_write32(trans, CSR_RESET, 0); + + if (image->is_secure) { + /* wait for image verification to complete */ + ret = iwl_poll_prph_bit(trans, + LMPM_SECURE_BOOT_CPU1_STATUS_ADDR, + LMPM_SECURE_BOOT_STATUS_SUCCESS, + LMPM_SECURE_BOOT_STATUS_SUCCESS, + LMPM_SECURE_TIME_OUT); + + if (ret < 0) { + IWL_ERR(trans, "Time out on secure boot process\n"); + return ret; + } + } return 0; } @@ -466,7 +759,6 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; bool hw_rfkill; @@ -476,17 +768,15 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, return -EIO; } - clear_bit(STATUS_FW_ERROR, &trans_pcie->status); - iwl_enable_rfkill_int(trans); /* If platform's RF_KILL switch is NOT set to KILL */ hw_rfkill = iwl_is_rfkill_set(trans); if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans_pcie->status); + set_bit(STATUS_RFKILL, &trans->status); else - clear_bit(STATUS_RFKILL, &trans_pcie->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + clear_bit(STATUS_RFKILL, &trans->status); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); if (hw_rfkill && !run_in_rfkill) return -ERFKILL; @@ -524,12 +814,14 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; + bool hw_rfkill, was_hw_rfkill; + + was_hw_rfkill = iwl_is_rfkill_set(trans); /* tell the device to stop sending interrupts */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); /* device going down, Stop using ICT table */ iwl_pcie_disable_ict(trans); @@ -541,7 +833,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) * restart. So don't process again if the device is * already dead. */ - if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { + if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) { iwl_pcie_tx_stop(trans); iwl_pcie_rx_stop(trans); @@ -561,21 +853,51 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) /* Upon stop, the APM issues an interrupt if HW RF kill is set. * Clean again the interrupt here */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - iwl_enable_rfkill_int(trans); + spin_unlock(&trans_pcie->irq_lock); /* stop and reset the on-board processor */ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); /* clear all status bits */ - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); - clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); - clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); - clear_bit(STATUS_RFKILL, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + clear_bit(STATUS_INT_ENABLED, &trans->status); + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); + clear_bit(STATUS_TPOWER_PMI, &trans->status); + clear_bit(STATUS_RFKILL, &trans->status); + + /* + * Even if we stop the HW, we still want the RF kill + * interrupt + */ + iwl_enable_rfkill_int(trans); + + /* + * Check again since the RF kill state may have changed while + * all the interrupts were disabled, in this case we couldn't + * receive the RF kill interrupt and update the state in the + * op_mode. + * Don't call the op_mode if the rkfill state hasn't changed. + * This allows the op_mode to call stop_device from the rfkill + * notification without endless recursion. Under very rare + * circumstances, we might have a small recursion if the rfkill + * state changed exactly now while we were called from stop_device. + * This is very unlikely but can happen and is supported. + */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + if (hw_rfkill != was_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) @@ -660,7 +982,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill; int err; @@ -671,7 +992,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) } /* Reset the entire device */ - iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); usleep_range(10, 15); @@ -682,53 +1003,30 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) hw_rfkill = iwl_is_rfkill_set(trans); if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans_pcie->status); + set_bit(STATUS_RFKILL, &trans->status); else - clear_bit(STATUS_RFKILL, &trans_pcie->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + clear_bit(STATUS_RFKILL, &trans->status); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); return 0; } -static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, - bool op_mode_leaving) +static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; - unsigned long flags; - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + /* disable interrupts - don't enable HW RF kill interrupt */ + spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); iwl_pcie_apm_stop(trans); - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); iwl_pcie_disable_ict(trans); - - if (!op_mode_leaving) { - /* - * Even if we stop the HW, we still want the RF kill - * interrupt - */ - iwl_enable_rfkill_int(trans); - - /* - * Check again since the RF kill state may have changed while - * all the interrupts were disabled, in this case we couldn't - * receive the RF kill interrupt and update the state in the - * op_mode. - */ - hw_rfkill = iwl_is_rfkill_set(trans); - if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans_pcie->status); - else - clear_bit(STATUS_RFKILL, &trans_pcie->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - } } static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) @@ -761,6 +1059,12 @@ static void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); } +static int iwl_pcie_dummy_napi_poll(struct napi_struct *napi, int budget) +{ + WARN_ON(1); + return 0; +} + static void iwl_trans_pcie_configure(struct iwl_trans *trans, const struct iwl_trans_config *trans_cfg) { @@ -787,6 +1091,18 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, trans_pcie->command_names = trans_cfg->command_names; trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; + + /* Initialize NAPI here - it should be before registering to mac80211 + * in the opmode but after the HW struct is allocated. + * As this function may be called again in some corner cases don't + * do anything if NAPI was already initialized. + */ + if (!trans_pcie->napi.poll && trans->op_mode->ops->napi_add) { + init_dummy_netdev(&trans_pcie->napi_dev); + iwl_op_mode_napi_add(trans->op_mode, &trans_pcie->napi, + &trans_pcie->napi_dev, + iwl_pcie_dummy_napi_poll, 64); + } } void iwl_trans_pcie_free(struct iwl_trans *trans) @@ -807,17 +1123,18 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) pci_disable_device(trans_pcie->pci_dev); kmem_cache_destroy(trans->dev_cmd_pool); + if (trans_pcie->napi.poll) + netif_napi_del(&trans_pcie->napi); + kfree(trans); } static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (state) - set_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + set_bit(STATUS_TPOWER_PMI, &trans->status); else - clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + clear_bit(STATUS_TPOWER_PMI, &trans->status); } static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, @@ -828,6 +1145,9 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, spin_lock_irqsave(&trans_pcie->reg_lock, *flags); + if (trans_pcie->cmd_in_flight) + goto out; + /* this bit wakes up the NIC */ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); @@ -867,6 +1187,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, } } +out: /* * Fool sparse by faking we release the lock - sparse will * track nic_access anyway. @@ -888,6 +1209,9 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, */ __acquire(&trans_pcie->reg_lock); + if (trans_pcie->cmd_in_flight) + goto out; + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* @@ -897,6 +1221,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans, * scheduled on different CPUs (after we drop reg_lock). */ mmiowb(); +out: spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags); } @@ -939,7 +1264,7 @@ static int iwl_trans_pcie_write_mem(struct iwl_trans *trans, u32 addr, #define IWL_FLUSH_WAIT_MS 2000 -static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) +static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans, u32 txq_bm) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq; @@ -952,13 +1277,31 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) /* waiting for all the tx frames complete might take a while */ for (cnt = 0; cnt < trans->cfg->base_params->num_of_queues; cnt++) { + u8 wr_ptr; + if (cnt == trans_pcie->cmd_queue) continue; + if (!test_bit(cnt, trans_pcie->queue_used)) + continue; + if (!(BIT(cnt) & txq_bm)) + continue; + + IWL_DEBUG_TX_QUEUES(trans, "Emptying queue %d...\n", cnt); txq = &trans_pcie->txq[cnt]; q = &txq->q; - while (q->read_ptr != q->write_ptr && !time_after(jiffies, - now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) + wr_ptr = ACCESS_ONCE(q->write_ptr); + + while (q->read_ptr != ACCESS_ONCE(q->write_ptr) && + !time_after(jiffies, + now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS))) { + u8 write_ptr = ACCESS_ONCE(q->write_ptr); + + if (WARN_ONCE(wr_ptr != write_ptr, + "WR pointer moved while flushing %d -> %d\n", + wr_ptr, write_ptr)) + return -ETIMEDOUT; msleep(1); + } if (q->read_ptr != q->write_ptr) { IWL_ERR(trans, @@ -966,6 +1309,7 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) ret = -ETIMEDOUT; break; } + IWL_DEBUG_TX_QUEUES(trans, "Queue %d is now empty.\n", cnt); } if (!ret) @@ -1000,8 +1344,8 @@ static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) IWL_ERR(trans, "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", cnt, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, - SCD_QUEUE_RDPTR(cnt)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_RDPTR(cnt)) & + (TFD_QUEUE_SIZE_MAX - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(cnt))); } @@ -1045,6 +1389,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"; @@ -1077,6 +1422,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 }; @@ -1294,16 +1640,15 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, { struct iwl_trans *trans = file->private_data; char *buf = NULL; - int pos = 0; - ssize_t ret = -EFAULT; - - ret = pos = iwl_dump_fh(trans, &buf); - if (buf) { - ret = simple_read_from_buffer(user_buf, - count, ppos, buf, pos); - kfree(buf); - } + ssize_t ret; + ret = iwl_dump_fh(trans, &buf); + if (ret < 0) + return ret; + if (!buf) + return -EINVAL; + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); return ret; } @@ -1331,6 +1676,61 @@ err: IWL_ERR(trans, "failed to create the trans debugfs entry\n"); return -ENOMEM; } + +static u32 iwl_trans_pcie_get_cmdlen(struct iwl_tfd *tfd) +{ + u32 cmdlen = 0; + int i; + + for (i = 0; i < IWL_NUM_OF_TBS; i++) + cmdlen += iwl_pcie_tfd_tb_get_len(tfd, i); + + return cmdlen; +} + +static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans, + void *buf, u32 buflen) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_fw_error_dump_data *data; + struct iwl_txq *cmdq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_fw_error_dump_txcmd *txcmd; + u32 len; + int i, ptr; + + if (!buf) + return sizeof(*data) + + cmdq->q.n_window * (sizeof(*txcmd) + + TFD_MAX_PAYLOAD_SIZE); + + len = 0; + data = buf; + data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXCMD); + txcmd = (void *)data->data; + spin_lock_bh(&cmdq->lock); + ptr = cmdq->q.write_ptr; + for (i = 0; i < cmdq->q.n_window; i++) { + u8 idx = get_cmd_index(&cmdq->q, ptr); + u32 caplen, cmdlen; + + cmdlen = iwl_trans_pcie_get_cmdlen(&cmdq->tfds[ptr]); + caplen = min_t(u32, TFD_MAX_PAYLOAD_SIZE, cmdlen); + + if (cmdlen) { + len += sizeof(*txcmd) + caplen; + txcmd->cmdlen = cpu_to_le32(cmdlen); + txcmd->caplen = cpu_to_le32(caplen); + memcpy(txcmd->data, cmdq->entries[idx].cmd, caplen); + txcmd = (void *)((u8 *)txcmd->data + caplen); + } + + ptr = iwl_queue_dec_wrap(ptr); + } + spin_unlock_bh(&cmdq->lock); + + data->len = cpu_to_le32(len); + return sizeof(*data) + len; +} #else static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, struct dentry *dir) @@ -1341,7 +1741,7 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, static const struct iwl_trans_ops trans_ops_pcie = { .start_hw = iwl_trans_pcie_start_hw, - .stop_hw = iwl_trans_pcie_stop_hw, + .op_mode_leave = iwl_trans_pcie_op_mode_leave, .fw_alive = iwl_trans_pcie_fw_alive, .start_fw = iwl_trans_pcie_start_fw, .stop_device = iwl_trans_pcie_stop_device, @@ -1373,6 +1773,10 @@ static const struct iwl_trans_ops trans_ops_pcie = { .grab_nic_access = iwl_trans_pcie_grab_nic_access, .release_nic_access = iwl_trans_pcie_release_nic_access, .set_bits_mask = iwl_trans_pcie_set_bits_mask, + +#ifdef CONFIG_IWLWIFI_DEBUGFS + .dump_data = iwl_trans_pcie_dump_data, +#endif }; struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, @@ -1450,6 +1854,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * PCI Tx retries from interfering with C3 CPU state */ pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + trans->dev = &pdev->dev; + trans_pcie->pci_dev = pdev; + iwl_disable_interrupts(trans); + err = pci_enable_msi(pdev); if (err) { dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", err); @@ -1461,8 +1869,6 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } } - trans->dev = &pdev->dev; - trans_pcie->pci_dev = pdev; trans->hw_rev = iwl_read32(trans, CSR_HW_REV); trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), @@ -1488,12 +1894,10 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_pci_disable_msi; } - trans_pcie->inta_mask = CSR_INI_SET_MASK; - if (iwl_pcie_alloc_ict(trans)) goto out_free_cmd_pool; - err = request_threaded_irq(pdev->irq, iwl_pcie_isr_ict, + err = request_threaded_irq(pdev->irq, iwl_pcie_isr, iwl_pcie_irq_handler, IRQF_SHARED, DRV_NAME, trans); if (err) { @@ -1501,6 +1905,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_free_ict; } + trans_pcie->inta_mask = CSR_INI_SET_MASK; + return trans; out_free_ict: diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 1424335163b..038940afbdc 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -70,20 +70,20 @@ static int iwl_queue_space(const struct iwl_queue *q) /* * To avoid ambiguity between empty and completely full queues, there - * should always be less than q->n_bd elements in the queue. - * If q->n_window is smaller than q->n_bd, there is no need to reserve - * any queue entries for this purpose. + * should always be less than TFD_QUEUE_SIZE_MAX elements in the queue. + * If q->n_window is smaller than TFD_QUEUE_SIZE_MAX, there is no need + * to reserve any queue entries for this purpose. */ - if (q->n_window < q->n_bd) + if (q->n_window < TFD_QUEUE_SIZE_MAX) max = q->n_window; else - max = q->n_bd - 1; + max = TFD_QUEUE_SIZE_MAX - 1; /* - * q->n_bd is a power of 2, so the following is equivalent to modulo by - * q->n_bd and is well defined for negative dividends. + * TFD_QUEUE_SIZE_MAX is a power of 2, so the following is equivalent to + * modulo by TFD_QUEUE_SIZE_MAX and is well defined. */ - used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1); + used = (q->write_ptr - q->read_ptr) & (TFD_QUEUE_SIZE_MAX - 1); if (WARN_ON(used > max)) return 0; @@ -94,17 +94,11 @@ static int iwl_queue_space(const struct iwl_queue *q) /* * iwl_queue_init - Initialize queue's high/low-water and read/write indexes */ -static int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) +static int iwl_queue_init(struct iwl_queue *q, int slots_num, u32 id) { - q->n_bd = count; q->n_window = slots_num; q->id = id; - /* count must be power-of-two size, otherwise iwl_queue_inc_wrap - * and iwl_queue_dec_wrap are broken. */ - if (WARN_ON(!is_power_of_2(count))) - return -EINVAL; - /* slots_num must be power-of-two size, otherwise * get_cmd_index is broken. */ if (WARN_ON(!is_power_of_2(slots_num))) @@ -197,17 +191,17 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) IWL_ERR(trans, "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", i, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, - SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_RDPTR(i)) & + (TFD_QUEUE_SIZE_MAX - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); } for (i = q->read_ptr; i != q->write_ptr; - i = iwl_queue_inc_wrap(i, q->n_bd)) + i = iwl_queue_inc_wrap(i)) IWL_ERR(trans, "scratch %d = 0x%08x\n", i, le32_to_cpu(txq->scratchbufs[i].scratch)); - iwl_op_mode_nic_error(trans->op_mode); + iwl_force_nmi(trans); } /* @@ -287,53 +281,64 @@ static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, /* * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware */ -void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) +static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, + struct iwl_txq *txq) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 reg = 0; int txq_id = txq->q.id; - if (txq->need_update == 0) - return; + lockdep_assert_held(&txq->lock); - if (trans->cfg->base_params->shadow_reg_enable) { - /* shadow register enabled */ - iwl_write32(trans, HBUS_TARG_WRPTR, - txq->q.write_ptr | (txq_id << 8)); - } else { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - /* if we're trying to save power */ - if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { - /* wake up nic if it's powered down ... - * uCode will wake up, and interrupt us again, so next - * time we'll skip this part. */ - reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); - - if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { - IWL_DEBUG_INFO(trans, - "Tx queue %d requesting wakeup," - " GP1 = 0x%x\n", txq_id, reg); - iwl_set_bit(trans, CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - return; - } + /* + * explicitly wake up the NIC if: + * 1. shadow registers aren't enabled + * 2. NIC is woken up for CMD regardless of shadow outside this function + * 3. there is a chance that the NIC is asleep + */ + if (!trans->cfg->base_params->shadow_reg_enable && + txq_id != trans_pcie->cmd_queue && + test_bit(STATUS_TPOWER_PMI, &trans->status)) { + /* + * wake up nic if it's powered down ... + * uCode will wake up, and interrupt us again, so next + * time we'll skip this part. + */ + reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(trans, "Tx queue %d requesting wakeup, GP1 = 0x%x\n", + txq_id, reg); + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + txq->need_update = true; + return; + } + } - IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, - txq->q.write_ptr); + /* + * if not in power-save mode, uCode will never sleep when we're + * trying to tx (during RFKILL, we're not trying to tx). + */ + IWL_DEBUG_TX(trans, "Q:%d WR: 0x%x\n", txq_id, txq->q.write_ptr); + iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); +} - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - txq->q.write_ptr | (txq_id << 8)); +void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int i; - /* - * else not in power-save mode, - * uCode will never sleep when we're - * trying to tx (during RFKILL, we're not trying to tx). - */ - } else - iwl_write32(trans, HBUS_TARG_WRPTR, - txq->q.write_ptr | (txq_id << 8)); + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + struct iwl_txq *txq = &trans_pcie->txq[i]; + + spin_lock_bh(&txq->lock); + if (trans_pcie->txq[i].need_update) { + iwl_pcie_txq_inc_wr_ptr(trans, txq); + trans_pcie->txq[i].need_update = false; + } + spin_unlock_bh(&txq->lock); } - txq->need_update = 0; } static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) @@ -348,13 +353,6 @@ static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) return addr; } -static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) -{ - struct iwl_tfd_tb *tb = &tfd->tbs[idx]; - - return le16_to_cpu(tb->hi_n_len) >> 4; -} - static inline void iwl_pcie_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, dma_addr_t addr, u16 len) { @@ -414,13 +412,17 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) { struct iwl_tfd *tfd_tmp = txq->tfds; - /* rd_ptr is bounded by n_bd and idx is bounded by n_window */ + /* rd_ptr is bounded by TFD_QUEUE_SIZE_MAX and + * idx is bounded by n_window + */ int rd_ptr = txq->q.read_ptr; int idx = get_cmd_index(&txq->q, rd_ptr); lockdep_assert_held(&txq->lock); - /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ + /* We have only q->n_window txq->entries, but we use + * TFD_QUEUE_SIZE_MAX tfds + */ iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr]); /* free SKB */ @@ -441,7 +443,7 @@ static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq) } static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, - dma_addr_t addr, u16 len, u8 reset) + dma_addr_t addr, u16 len, bool reset) { struct iwl_queue *q; struct iwl_tfd *tfd, *tfd_tmp; @@ -547,15 +549,14 @@ static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, { int ret; - txq->need_update = 0; + txq->need_update = false; /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); /* Initialize queue's high/low-water marks, and head/tail indexes */ - ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, - txq_id); + ret = iwl_queue_init(&txq->q, slots_num, txq_id); if (ret) return ret; @@ -580,15 +581,12 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) struct iwl_txq *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; - if (!q->n_bd) - return; - spin_lock_bh(&txq->lock); while (q->write_ptr != q->read_ptr) { IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, q->read_ptr); iwl_pcie_txq_free_tfd(trans, txq); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr); } txq->active = false; spin_unlock_bh(&txq->lock); @@ -625,10 +623,12 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) } /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) { - dma_free_coherent(dev, sizeof(struct iwl_tfd) * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); + if (txq->tfds) { + dma_free_coherent(dev, + sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX, + txq->tfds, txq->q.dma_addr); txq->q.dma_addr = 0; + txq->tfds = NULL; dma_free_coherent(dev, sizeof(*txq->scratchbufs) * txq->q.n_window, @@ -685,7 +685,8 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) /* The chain extension of the SCD doesn't work well. This feature is * enabled by default by the HW, so we need to disable it manually. */ - iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); + if (trans->cfg->base_params->scd_chain_ext_wa) + iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, trans_pcie->cmd_fifo); @@ -705,8 +706,9 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Enable L1-Active */ - iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) @@ -739,10 +741,9 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ch, txq_id, ret; - unsigned long flags; /* Turn off all Tx DMA fifos */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); iwl_pcie_txq_set_sched(trans, 0); @@ -759,13 +760,19 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans) iwl_read_direct32(trans, FH_TSSR_TX_STATUS_REG)); } - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); - if (!trans_pcie->txq) { - IWL_WARN(trans, - "Stopping tx queues that aren't allocated...\n"); + /* + * This function can be called before the op_mode disabled the + * queues. This happens when we have an rfkill interrupt. + * Since we stop Tx altogether - mark the queues as stopped. + */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + /* This can happen: start_hw, stop_device */ + if (!trans_pcie->txq) return 0; - } /* Unmap DMA from host system and free skb's */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; @@ -867,7 +874,6 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; int txq_id, slots_num; - unsigned long flags; bool alloc = false; if (!trans_pcie->txq) { @@ -877,7 +883,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) alloc = true; } - spin_lock_irqsave(&trans_pcie->irq_lock, flags); + spin_lock(&trans_pcie->irq_lock); /* Turn off all Tx DMA fifos */ iwl_write_prph(trans, SCD_TXFACT, 0); @@ -886,7 +892,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, trans_pcie->kw.dma >> 4); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + spin_unlock(&trans_pcie->irq_lock); /* Alloc and init all Tx queues, including the command queue (#4/#9) */ for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; @@ -931,8 +937,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = &trans_pcie->txq[txq_id]; - /* n_bd is usually 256 => n_bd - 1 = 0xff */ - int tfd_num = ssn & (txq->q.n_bd - 1); + int tfd_num = ssn & (TFD_QUEUE_SIZE_MAX - 1); struct iwl_queue *q = &txq->q; int last_to_free; @@ -956,12 +961,12 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ - last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd); + last_to_free = iwl_queue_dec_wrap(tfd_num); if (!iwl_queue_used(q, last_to_free)) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, last_to_free, q->n_bd, + __func__, txq_id, last_to_free, TFD_QUEUE_SIZE_MAX, q->write_ptr, q->read_ptr); goto out; } @@ -971,7 +976,7 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, for (; q->read_ptr != tfd_num; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) continue; @@ -1005,28 +1010,40 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_txq *txq = &trans_pcie->txq[txq_id]; struct iwl_queue *q = &txq->q; + unsigned long flags; int nfreed = 0; lockdep_assert_held(&txq->lock); - if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) { + if ((idx >= TFD_QUEUE_SIZE_MAX) || (!iwl_queue_used(q, idx))) { IWL_ERR(trans, "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, idx, q->n_bd, + __func__, txq_id, idx, TFD_QUEUE_SIZE_MAX, q->write_ptr, q->read_ptr); return; } - for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + for (idx = iwl_queue_inc_wrap(idx); q->read_ptr != idx; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr)) { if (nfreed++ > 0) { IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx, q->write_ptr, q->read_ptr); - iwl_op_mode_nic_error(trans->op_mode); + iwl_force_nmi(trans); } } + if (trans->cfg->base_params->apmg_wake_up_wa && + q->read_ptr == q->write_ptr) { + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + WARN_ON(!trans_pcie->cmd_in_flight); + trans_pcie->cmd_in_flight = false; + __iwl_trans_pcie_clear_bit(trans, + CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + } + iwl_pcie_txq_progress(trans_pcie, txq); } @@ -1143,8 +1160,15 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) SCD_TX_STTS_QUEUE_OFFSET(txq_id); static const u32 zero_val[4] = {}; + /* + * Upon HW Rfkill - we stop the device, and then stop the queues + * in the op_mode. Just for the sake of the simplicity of the op_mode, + * allow the op_mode to call txq_disable after it already called + * stop_device. + */ if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) { - WARN_ONCE(1, "queue %d not used", txq_id); + WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status), + "queue %d not used", txq_id); return; } @@ -1178,12 +1202,13 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; + unsigned long flags; void *dup_buf = NULL; dma_addr_t phys_addr; int idx; u16 copy_size, cmd_size, scratch_size; bool had_nocopy = false; - int i; + int i, ret; u32 cmd_pos; const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD]; u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; @@ -1290,28 +1315,39 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, cmd_pos = offsetof(struct iwl_device_cmd, payload); copy_size = sizeof(out_cmd->hdr); for (i = 0; i < IWL_MAX_CMD_TBS_PER_TFD; i++) { - int copy = 0; + int copy; if (!cmd->len[i]) continue; - /* need at least IWL_HCMD_SCRATCHBUF_SIZE copied */ - if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { - copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; - - if (copy > cmd->len[i]) - copy = cmd->len[i]; - } - /* copy everything if not nocopy/dup */ if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | - IWL_HCMD_DFL_DUP))) + IWL_HCMD_DFL_DUP))) { copy = cmd->len[i]; - if (copy) { memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); cmd_pos += copy; copy_size += copy; + continue; + } + + /* + * Otherwise we need at least IWL_HCMD_SCRATCHBUF_SIZE copied + * in total (for the scratchbuf handling), but copy up to what + * we can fit into the payload for debug dump purposes. + */ + copy = min_t(int, TFD_MAX_PAYLOAD_SIZE - cmd_pos, cmd->len[i]); + + memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], copy); + cmd_pos += copy; + + /* However, treat copy_size the proper way, we need it below */ + if (copy_size < IWL_HCMD_SCRATCHBUF_SIZE) { + copy = IWL_HCMD_SCRATCHBUF_SIZE - copy_size; + + if (copy > cmd->len[i]) + copy = cmd->len[i]; + copy_size += copy; } } @@ -1326,7 +1362,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, memcpy(&txq->scratchbufs[q->write_ptr], &out_cmd->hdr, scratch_size); iwl_pcie_txq_build_tfd(trans, txq, iwl_pcie_get_scratchbuf_dma(txq, q->write_ptr), - scratch_size, 1); + scratch_size, true); /* map first command fragment, if any remains */ if (copy_size > scratch_size) { @@ -1342,7 +1378,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } iwl_pcie_txq_build_tfd(trans, txq, phys_addr, - copy_size - scratch_size, 0); + copy_size - scratch_size, false); } /* map the remaining (adjusted) nocopy/dup fragments */ @@ -1365,7 +1401,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, goto out; } - iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], 0); + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmdlen[i], false); } out_meta->flags = cmd->flags; @@ -1373,18 +1409,46 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, kfree(txq->entries[idx].free_buf); txq->entries[idx].free_buf = dup_buf; - txq->need_update = 1; - trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr); /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + spin_lock_irqsave(&trans_pcie->reg_lock, flags); + + /* + * wake up the NIC to make sure that the firmware will see the host + * command - we will let the NIC sleep once all the host commands + * returned. This needs to be done only on NICs that have + * apmg_wake_up_wa set. + */ + if (trans->cfg->base_params->apmg_wake_up_wa && + !trans_pcie->cmd_in_flight) { + trans_pcie->cmd_in_flight = true; + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, + (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), + 15000); + if (ret < 0) { + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + trans_pcie->cmd_in_flight = false; + idx = -EIO; + goto out; + } + } + /* Increment and update queue's write index */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); iwl_pcie_txq_inc_wr_ptr(trans, txq); + spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); + out: spin_unlock_bh(&txq->lock); free_dup_buf: @@ -1449,12 +1513,12 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, iwl_pcie_cmdq_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { - if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { + if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) { IWL_WARN(trans, "HCMD_ACTIVE already clear for command %s\n", get_cmd_string(trans_pcie, cmd->hdr.cmd)); } - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", get_cmd_string(trans_pcie, cmd->hdr.cmd)); wake_up(&trans_pcie->wait_command_queue); @@ -1465,7 +1529,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, spin_unlock_bh(&txq->lock); } -#define HOST_COMPLETE_TIMEOUT (2 * HZ) +#define HOST_COMPLETE_TIMEOUT (2 * HZ) static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) @@ -1497,12 +1561,11 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", get_cmd_string(trans_pcie, cmd->id)); - if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status))) { - IWL_ERR(trans, "Command %s: a command is already active!\n", - get_cmd_string(trans_pcie, cmd->id)); + if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), + "Command %s: a command is already active!\n", + get_cmd_string(trans_pcie, cmd->id))) return -EIO; - } IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", get_cmd_string(trans_pcie, cmd->id)); @@ -1510,7 +1573,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); if (cmd_idx < 0) { ret = cmd_idx; - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", get_cmd_string(trans_pcie, cmd->id), ret); @@ -1518,34 +1581,32 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, } ret = wait_event_timeout(trans_pcie->wait_command_queue, - !test_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status), + !test_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), HOST_COMPLETE_TIMEOUT); if (!ret) { - if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { - struct iwl_txq *txq = - &trans_pcie->txq[trans_pcie->cmd_queue]; - struct iwl_queue *q = &txq->q; + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_queue *q = &txq->q; - IWL_ERR(trans, - "Error sending %s: time out after %dms.\n", - get_cmd_string(trans_pcie, cmd->id), - jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + IWL_ERR(trans, "Error sending %s: time out after %dms.\n", + get_cmd_string(trans_pcie, cmd->id), + jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - IWL_ERR(trans, - "Current CMD queue read_ptr %d write_ptr %d\n", - q->read_ptr, q->write_ptr); - - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - IWL_DEBUG_INFO(trans, - "Clearing HCMD_ACTIVE for command %s\n", - get_cmd_string(trans_pcie, cmd->id)); - ret = -ETIMEDOUT; - goto cancel; - } + IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", + q->read_ptr, q->write_ptr); + + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + ret = -ETIMEDOUT; + + iwl_force_nmi(trans); + iwl_trans_fw_error(trans); + + goto cancel; } - if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { + if (test_bit(STATUS_FW_ERROR, &trans->status)) { IWL_ERR(trans, "FW error in SYNC CMD %s\n", get_cmd_string(trans_pcie, cmd->id)); dump_stack(); @@ -1554,7 +1615,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, } if (!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans_pcie->status)) { + test_bit(STATUS_RFKILL, &trans->status)) { IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); ret = -ERFKILL; goto cancel; @@ -1591,13 +1652,8 @@ cancel: int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) - return -EIO; - if (!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans_pcie->status)) { + test_bit(STATUS_RFKILL, &trans->status)) { IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", cmd->id); return -ERFKILL; @@ -1622,7 +1678,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, dma_addr_t tb0_phys, tb1_phys, scratch_phys; void *tb1_addr; u16 len, tb1_len, tb2_len; - u8 wait_write_ptr = 0; + bool wait_write_ptr; __le16 fc = hdr->frame_control; u8 hdr_len = ieee80211_hdrlen(fc); u16 wifi_seq; @@ -1651,7 +1707,6 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, txq->entries[q->write_ptr].skb = skb; txq->entries[q->write_ptr].cmd = dev_cmd; - dev_cmd->hdr.cmd = REPLY_TX; dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | INDEX_TO_SEQ(q->write_ptr))); @@ -1684,7 +1739,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, memcpy(&txq->scratchbufs[q->write_ptr], &dev_cmd->hdr, IWL_HCMD_SCRATCHBUF_SIZE); iwl_pcie_txq_build_tfd(trans, txq, tb0_phys, - IWL_HCMD_SCRATCHBUF_SIZE, 1); + IWL_HCMD_SCRATCHBUF_SIZE, true); /* there must be data left over for TB1 or this code must be changed */ BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_HCMD_SCRATCHBUF_SIZE); @@ -1694,7 +1749,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, tb1_phys = dma_map_single(trans->dev, tb1_addr, tb1_len, DMA_TO_DEVICE); if (unlikely(dma_mapping_error(trans->dev, tb1_phys))) goto out_err; - iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, 0); + iwl_pcie_txq_build_tfd(trans, txq, tb1_phys, tb1_len, false); /* * Set up TFD's third entry to point directly to remainder @@ -1710,7 +1765,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, &txq->tfds[q->write_ptr]); goto out_err; } - iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, 0); + iwl_pcie_txq_build_tfd(trans, txq, tb2_phys, tb2_len, false); } /* Set up entry for this TFD in Tx byte-count array */ @@ -1724,12 +1779,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, trace_iwlwifi_dev_tx_data(trans->dev, skb, skb->data + hdr_len, tb2_len); - if (!ieee80211_has_morefrags(fc)) { - txq->need_update = 1; - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } + wait_write_ptr = ieee80211_has_morefrags(fc); /* start timer if queue currently empty */ if (txq->need_update && q->read_ptr == q->write_ptr && @@ -1737,22 +1787,19 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); /* Tell device the write index *just past* this latest filled TFD */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - iwl_pcie_txq_inc_wr_ptr(trans, txq); + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); + if (!wait_write_ptr) + iwl_pcie_txq_inc_wr_ptr(trans, txq); /* * At this point the frame is "transmitted" successfully - * and we will get a TX status notification eventually, - * regardless of the value of ret. "ret" only indicates - * whether or not we should update the write pointer. + * and we will get a TX status notification eventually. */ if (iwl_queue_space(q) < q->high_mark) { - if (wait_write_ptr) { - txq->need_update = 1; + if (wait_write_ptr) iwl_pcie_txq_inc_wr_ptr(trans, txq); - } else { + else iwl_stop_queue(trans, txq); - } } spin_unlock(&txq->lock); return 0; diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README index 91f2ca90c70..1a554a685e9 100644 --- a/drivers/net/wireless/libertas/README +++ b/drivers/net/wireless/libertas/README @@ -8,9 +8,8 @@ Ltd. under the terms of the GNU General Public License Version 2, June 1991 (the "License"). You may use, redistribute and/or modify this File in accordance with the terms and conditions of the License, a copy of which - is available along with the File in the license.txt file or by writing to - the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + is available along with the File in the license.txt file or on the worldwide + web at http://www.gnu.org/licenses/gpl.txt. THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 116f4aba08d..47a998d8f99 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -621,7 +621,7 @@ static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy, id = *pos++; elen = *pos++; left -= 2; - if (elen > left || elen == 0) { + if (elen > left) { lbs_deb_scan("scan response: invalid IE fmt\n"); goto done; } @@ -1006,9 +1006,8 @@ struct cmd_key_material { } __packed; static int lbs_set_key_material(struct lbs_private *priv, - int key_type, - int key_info, - u8 *key, u16 key_len) + int key_type, int key_info, + const u8 *key, u16 key_len) { struct cmd_key_material cmd; int ret; @@ -1268,14 +1267,9 @@ static struct cfg80211_scan_request * _new_connect_scan_req(struct wiphy *wiphy, struct cfg80211_connect_params *sme) { struct cfg80211_scan_request *creq = NULL; - int i, n_channels = 0; + int i, n_channels = ieee80211_get_num_supported_channels(wiphy); enum ieee80211_band band; - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (wiphy->bands[band]) - n_channels += wiphy->bands[band]->n_channels; - } - creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) + n_channels * sizeof(void *), GFP_ATOMIC); @@ -1615,7 +1609,7 @@ static int lbs_cfg_del_key(struct wiphy *wiphy, struct net_device *netdev, */ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct lbs_private *priv = wiphy_priv(wiphy); s8 signal, noise; @@ -1771,7 +1765,8 @@ static void lbs_join_post(struct lbs_private *priv, memcpy(priv->wdev->ssid, params->ssid, params->ssid_len); priv->wdev->ssid_len = params->ssid_len; - cfg80211_ibss_joined(priv->dev, bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->dev, bssid, params->chandef.chan, + GFP_KERNEL); /* TODO: consider doing this at MACREG_INT_CODE_LINK_SENSED time */ priv->connect_status = LBS_CONNECTED; diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 178b222b3ce..65f18f1e869 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -248,7 +248,7 @@ int lbs_process_event(struct lbs_private *priv, u32 event) /* handle unexpected PS SLEEP event */ if (priv->psstate == PS_STATE_FULL_POWER) { lbs_deb_cmd( - "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n"); + "EVENT: in FULL POWER mode, ignoring PS_SLEEP\n"); break; } priv->psstate = PS_STATE_PRE_SLEEP; diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 668dd27616a..cc6a0a586f0 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -913,7 +913,10 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, char *p2; struct debug_data *d = f->private_data; - pdata = kmalloc(cnt, GFP_KERNEL); + if (cnt == 0) + return 0; + + pdata = kmalloc(cnt + 1, GFP_KERNEL); if (pdata == NULL) return 0; @@ -922,6 +925,7 @@ static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf, kfree(pdata); return 0; } + pdata[cnt] = '\0'; p0 = pdata; for (i = 0; i < num_of_items; i++) { diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h index ab966f08024..407784aca62 100644 --- a/drivers/net/wireless/libertas/defs.h +++ b/drivers/net/wireless/libertas/defs.h @@ -90,7 +90,8 @@ do { if ((lbs_debug & (grp)) == (grp)) \ #define lbs_deb_cfg80211(fmt, args...) LBS_DEB_LL(LBS_DEB_CFG80211, " cfg80211", fmt, ##args) #ifdef DEBUG -static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len) +static inline void lbs_deb_hex(unsigned int grp, const char *prompt, + const u8 *buf, int len) { int i = 0; diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c index c0f9e7e862f..51b92b5df11 100644 --- a/drivers/net/wireless/libertas/firmware.c +++ b/drivers/net/wireless/libertas/firmware.c @@ -53,6 +53,11 @@ static void main_firmware_cb(const struct firmware *firmware, void *context) /* Firmware found! */ lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); + if (priv->helper_fw) { + release_firmware (priv->helper_fw); + priv->helper_fw = NULL; + } + release_firmware (firmware); } static void helper_firmware_cb(const struct firmware *firmware, void *context) diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index c94dd680267..f499efc6abc 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -754,14 +754,14 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret, if (ret == 0 && (card->model != MODEL_8305)) ret = if_cs_prog_real(card, mainfw); if (ret) - goto out; + return; /* Now actually get the IRQ */ ret = request_irq(card->p_dev->irq, if_cs_interrupt, IRQF_SHARED, DRV_NAME, card); if (ret) { pr_err("error in request_irq\n"); - goto out; + return; } /* @@ -777,10 +777,6 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret, pr_err("could not activate card\n"); free_irq(card->p_dev->irq, card); } - -out: - release_firmware(helper); - release_firmware(mainfw); } @@ -906,6 +902,7 @@ static int if_cs_probe(struct pcmcia_device *p_dev) if (card->model == MODEL_UNKNOWN) { pr_err("unsupported manf_id 0x%04x / card_id 0x%04x\n", p_dev->manf_id, p_dev->card_id); + ret = -ENODEV; goto out2; } diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 45578335e42..33ceda296c9 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -498,7 +498,7 @@ static int if_sdio_prog_helper(struct if_sdio_card *card, */ mdelay(2); - chunk_size = min(size, (size_t)60); + chunk_size = min_t(size_t, size, 60); *((__le32*)chunk_buffer) = cpu_to_le32(chunk_size); memcpy(chunk_buffer + 4, firmware, chunk_size); @@ -639,7 +639,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card, req_size = size; while (req_size) { - chunk_size = min(req_size, (size_t)512); + chunk_size = min_t(size_t, req_size, 512); memcpy(chunk_buffer, firmware, chunk_size); /* @@ -708,20 +708,16 @@ static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, ret = if_sdio_prog_helper(card, helper); if (ret) - goto out; + return; lbs_deb_sdio("Helper firmware loaded\n"); ret = if_sdio_prog_real(card, mainfw); if (ret) - goto out; + return; lbs_deb_sdio("Firmware loaded\n"); if_sdio_finish_power_on(card); - -out: - release_firmware(helper); - release_firmware(mainfw); } static int if_sdio_prog_firmware(struct if_sdio_card *card) @@ -853,7 +849,7 @@ static void if_sdio_finish_power_on(struct if_sdio_card *card) card->started = true; /* Tell PM core that we don't need the card to be * powered now */ - pm_runtime_put_noidle(&func->dev); + pm_runtime_put(&func->dev); } } @@ -911,8 +907,8 @@ static int if_sdio_power_on(struct if_sdio_card *card) sdio_release_host(func); ret = if_sdio_prog_firmware(card); if (ret) { - sdio_disable_func(func); - return ret; + sdio_claim_host(func); + goto disable; } return 0; diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 4bb6574f407..f11728a866f 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -93,7 +93,6 @@ static void free_if_spi_card(struct if_spi_card *card) list_del(&packet->list); kfree(packet); } - spi_set_drvdata(card->spi, NULL); kfree(card); } @@ -1094,11 +1093,7 @@ static int if_spi_init_card(struct if_spi_card *card) goto out; out: - release_firmware(helper); - release_firmware(mainfw); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; } @@ -1128,7 +1123,7 @@ static int if_spi_probe(struct spi_device *spi) { struct if_spi_card *card; struct lbs_private *priv = NULL; - struct libertas_spi_platform_data *pdata = spi->dev.platform_data; + struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); int err = 0; lbs_deb_enter(LBS_DEB_SPI); diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 27980778d99..dff08a2896a 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -844,7 +844,7 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret, cardp->fw = fw; if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { ret = -EINVAL; - goto release_fw; + goto done; } /* Cancel any pending usb business */ @@ -861,7 +861,7 @@ restart: if (if_usb_submit_rx_urb_fwload(cardp) < 0) { lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); ret = -EIO; - goto release_fw; + goto done; } cardp->bootcmdresp = 0; @@ -883,14 +883,14 @@ restart: usb_kill_urb(cardp->tx_urb); if (if_usb_submit_rx_urb(cardp) < 0) ret = -EIO; - goto release_fw; + goto done; } else if (cardp->bootcmdresp <= 0) { if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } ret = -EIO; - goto release_fw; + goto done; } i = 0; @@ -921,14 +921,14 @@ restart: pr_info("FW download failure, time = %d ms\n", i * 100); ret = -EIO; - goto release_fw; + goto done; } cardp->priv->fw_ready = 1; if_usb_submit_rx_urb(cardp); if (lbs_start_card(priv)) - goto release_fw; + goto done; if_usb_setup_firmware(priv); @@ -939,11 +939,8 @@ restart: if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) priv->ehs_remove_supported = false; - release_fw: - release_firmware(cardp->fw); - cardp->fw = NULL; - done: + cardp->fw = NULL; lbs_deb_leave(LBS_DEB_USB); } diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index c7366b07b56..e446fed7b34 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -71,8 +71,10 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) skb->ip_summed = CHECKSUM_NONE; - if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) - return process_rxed_802_11_packet(priv, skb); + if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) { + ret = process_rxed_802_11_packet(priv, skb); + goto done; + } p_rx_pd = (struct rxpd *) skb->data; p_rx_pkt = (struct rxpackethdr *) ((u8 *)p_rx_pd + @@ -86,7 +88,7 @@ int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb) if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { lbs_deb_rx("rx err: frame received with bad length\n"); dev->stats.rx_length_errors++; - ret = 0; + ret = -EINVAL; dev_kfree_skb(skb); goto done; } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 2cd3f54e1ef..a312c653d11 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -57,6 +57,10 @@ static bool rctbl = false; module_param(rctbl, bool, 0444); MODULE_PARM_DESC(rctbl, "Handle rate control table"); +static bool support_p2p_device = true; +module_param(support_p2p_device, bool, 0444); +MODULE_PARM_DESC(support_p2p_device, "Support P2P-Device interface type"); + /** * enum hwsim_regtest - the type of regulatory tests we offer * @@ -159,14 +163,20 @@ static const struct ieee80211_regdomain hwsim_world_regdom_custom_02 = { .reg_rules = { REG_RULE(2412-10, 2462+10, 40, 0, 20, 0), REG_RULE(5725-10, 5850+10, 40, 0, 30, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + NL80211_RRF_NO_IR), } }; +static const struct ieee80211_regdomain *hwsim_world_regdom_custom[] = { + &hwsim_world_regdom_custom_01, + &hwsim_world_regdom_custom_02, +}; + struct hwsim_vif_priv { u32 magic; u8 bssid[ETH_ALEN]; bool assoc; + bool bcn_en; u16 aid; }; @@ -320,8 +330,74 @@ static const struct ieee80211_rate hwsim_rates[] = { { .bitrate = 540 } }; +static const struct ieee80211_iface_limit hwsim_if_limits[] = { + { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, + { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) }, + /* must be last, see hwsim_if_comb */ + { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) } +}; + +static const struct ieee80211_iface_limit hwsim_if_dfs_limits[] = { + { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination hwsim_if_comb[] = { + { + .limits = hwsim_if_limits, + /* remove the last entry which is P2P_DEVICE */ + .n_limits = ARRAY_SIZE(hwsim_if_limits) - 1, + .max_interfaces = 2048, + .num_different_channels = 1, + }, + { + .limits = hwsim_if_dfs_limits, + .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + } +}; + +static const struct ieee80211_iface_combination hwsim_if_comb_p2p_dev[] = { + { + .limits = hwsim_if_limits, + .n_limits = ARRAY_SIZE(hwsim_if_limits), + .max_interfaces = 2048, + .num_different_channels = 1, + }, + { + .limits = hwsim_if_dfs_limits, + .n_limits = ARRAY_SIZE(hwsim_if_dfs_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + } +}; + static spinlock_t hwsim_radio_lock; static struct list_head hwsim_radios; +static int hwsim_radio_idx; + +static struct platform_driver mac80211_hwsim_driver = { + .driver = { + .name = "mac80211_hwsim", + .owner = THIS_MODULE, + }, +}; struct mac80211_hwsim_data { struct list_head list; @@ -331,8 +407,11 @@ struct mac80211_hwsim_data { struct ieee80211_channel channels_2ghz[ARRAY_SIZE(hwsim_channels_2ghz)]; struct ieee80211_channel channels_5ghz[ARRAY_SIZE(hwsim_channels_5ghz)]; struct ieee80211_rate rates[ARRAY_SIZE(hwsim_rates)]; + struct ieee80211_iface_combination if_combination; struct mac_address addresses[2]; + int channels, idx; + bool use_chanctx; struct ieee80211_channel *tmp_chan; struct delayed_work roc_done; @@ -352,7 +431,6 @@ struct mac80211_hwsim_data { } ps; bool ps_poll_pending; struct dentry *debugfs; - struct dentry *debugfs_ps; struct sk_buff_head pending; /* packets pending */ /* @@ -361,7 +439,6 @@ struct mac80211_hwsim_data { * radio can be in more then one group. */ u64 group; - struct dentry *debugfs_group; int power_level; @@ -382,6 +459,14 @@ struct hwsim_radiotap_hdr { __le16 rt_chbitmask; } __packed; +struct hwsim_radiotap_ack_hdr { + struct ieee80211_radiotap_header hdr; + u8 rt_flags; + u8 pad; + __le16 rt_channel; + __le16 rt_chbitmask; +} __packed; + /* MAC80211_HWSIM netlinf family */ static struct genl_family hwsim_genl_family = { .id = GENL_ID_GENERATE, @@ -393,22 +478,181 @@ static struct genl_family hwsim_genl_family = { /* MAC80211_HWSIM netlink policy */ -static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { - [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, - .len = 6*sizeof(u8) }, - [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, - .len = 6*sizeof(u8) }, +static const struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = { + [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC, .len = ETH_ALEN }, + [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC, .len = ETH_ALEN }, [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY, .len = IEEE80211_MAX_DATA_LEN }, [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 }, [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 }, [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 }, [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC, - .len = IEEE80211_TX_MAX_RATES*sizeof( - struct hwsim_tx_rate)}, + .len = IEEE80211_TX_MAX_RATES * + sizeof(struct hwsim_tx_rate)}, [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 }, + [HWSIM_ATTR_CHANNELS] = { .type = NLA_U32 }, + [HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 }, + [HWSIM_ATTR_REG_HINT_ALPHA2] = { .type = NLA_STRING, .len = 2 }, + [HWSIM_ATTR_REG_CUSTOM_REG] = { .type = NLA_U32 }, + [HWSIM_ATTR_REG_STRICT_REG] = { .type = NLA_FLAG }, + [HWSIM_ATTR_SUPPORT_P2P_DEVICE] = { .type = NLA_FLAG }, }; +static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan); + +/* sysfs attributes */ +static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_pspoll *pspoll; + + if (!vp->assoc) + return; + + wiphy_debug(data->hw->wiphy, + "%s: send PS-Poll to %pM for aid %d\n", + __func__, vp->bssid, vp->aid); + + skb = dev_alloc_skb(sizeof(*pspoll)); + if (!skb) + return; + pspoll = (void *) skb_put(skb, sizeof(*pspoll)); + pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_PSPOLL | + IEEE80211_FCTL_PM); + pspoll->aid = cpu_to_le16(0xc000 | vp->aid); + memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); + memcpy(pspoll->ta, mac, ETH_ALEN); + + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_read_unlock(); +} + +static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, + struct ieee80211_vif *vif, int ps) +{ + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + + if (!vp->assoc) + return; + + wiphy_debug(data->hw->wiphy, + "%s: send data::nullfunc to %pM ps=%d\n", + __func__, vp->bssid, ps); + + skb = dev_alloc_skb(sizeof(*hdr)); + if (!skb) + return; + hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + (ps ? IEEE80211_FCTL_PM : 0)); + hdr->duration_id = cpu_to_le16(0); + memcpy(hdr->addr1, vp->bssid, ETH_ALEN); + memcpy(hdr->addr2, mac, ETH_ALEN); + memcpy(hdr->addr3, vp->bssid, ETH_ALEN); + + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_read_unlock(); +} + + +static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + hwsim_send_nullfunc(data, mac, vif, 1); +} + +static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *data = dat; + hwsim_send_nullfunc(data, mac, vif, 0); +} + +static int hwsim_fops_ps_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->ps; + return 0; +} + +static int hwsim_fops_ps_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + enum ps_mode old_ps; + + if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && + val != PS_MANUAL_POLL) + return -EINVAL; + + old_ps = data->ps; + data->ps = val; + + if (val == PS_MANUAL_POLL) { + ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_ps_poll, data); + data->ps_poll_pending = true; + } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { + ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_ps, + data); + } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { + ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_no_ps, + data); + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, + "%llu\n"); + +static int hwsim_write_simulate_radar(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + + ieee80211_radar_detected(data->hw); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(hwsim_simulate_radar, NULL, + hwsim_write_simulate_radar, "%llu\n"); + +static int hwsim_fops_group_read(void *dat, u64 *val) +{ + struct mac80211_hwsim_data *data = dat; + *val = data->group; + return 0; +} + +static int hwsim_fops_group_write(void *dat, u64 val) +{ + struct mac80211_hwsim_data *data = dat; + data->group = val; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, + hwsim_fops_group_read, hwsim_fops_group_write, + "%llx\n"); + static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -499,7 +743,7 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, const u8 *addr) { struct sk_buff *skb; - struct hwsim_radiotap_hdr *hdr; + struct hwsim_radiotap_ack_hdr *hdr; u16 flags; struct ieee80211_hdr *hdr11; @@ -510,14 +754,14 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, if (skb == NULL) return; - hdr = (struct hwsim_radiotap_hdr *) skb_put(skb, sizeof(*hdr)); + hdr = (struct hwsim_radiotap_ack_hdr *) skb_put(skb, sizeof(*hdr)); hdr->hdr.it_version = PKTHDR_RADIOTAP_VERSION; hdr->hdr.it_pad = 0; hdr->hdr.it_len = cpu_to_le16(sizeof(*hdr)); hdr->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_CHANNEL)); hdr->rt_flags = 0; - hdr->rt_rate = 0; + hdr->pad = 0; hdr->rt_channel = cpu_to_le16(chan->center_freq); flags = IEEE80211_CHAN_2GHZ; hdr->rt_chbitmask = cpu_to_le16(flags); @@ -632,7 +876,7 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, } if (nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER, - sizeof(struct mac_address), data->addresses[1].addr)) + ETH_ALEN, data->addresses[1].addr)) goto nla_put_failure; /* We get the skb->data */ @@ -819,32 +1063,6 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, ack = true; rx_status.mactime = now + data2->tsf_offset; -#if 0 - /* - * Don't enable this code by default as the OUI 00:00:00 - * is registered to Xerox so we shouldn't use it here, it - * might find its way into pcap files. - * Note that this code requires the headroom in the SKB - * that was allocated earlier. - */ - rx_status.vendor_radiotap_oui[0] = 0x00; - rx_status.vendor_radiotap_oui[1] = 0x00; - rx_status.vendor_radiotap_oui[2] = 0x00; - rx_status.vendor_radiotap_subns = 127; - /* - * Radiotap vendor namespaces can (and should) also be - * split into fields by using the standard radiotap - * presence bitmap mechanism. Use just BIT(0) here for - * the presence bitmap. - */ - rx_status.vendor_radiotap_bitmap = BIT(0); - /* We have 8 bytes of (dummy) data */ - rx_status.vendor_radiotap_len = 8; - /* For testing, also require it to be aligned */ - rx_status.vendor_radiotap_align = 8; - /* push the data */ - memcpy(skb_push(nskb, 8), "ABCDEFGH", 8); -#endif memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(data2->hw, nskb); @@ -871,7 +1089,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return; } - if (channels == 1) { + if (!data->use_chanctx) { channel = data->channel; } else if (txi->hw_queue == 4) { channel = data->tmp_chan; @@ -899,7 +1117,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, if (control->sta) hwsim_check_sta_magic(control->sta); - if (rctbl) + if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) ieee80211_get_tx_rates(txi->control.vif, control->sta, skb, txi->control.rates, ARRAY_SIZE(txi->control.rates)); @@ -1006,7 +1224,7 @@ static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, { u32 _pid = ACCESS_ONCE(wmediumd_portid); - if (rctbl) { + if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) { struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); ieee80211_get_tx_rates(txi->control.vif, NULL, skb, txi->control.rates, @@ -1043,7 +1261,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, if (skb == NULL) return; info = IEEE80211_SKB_CB(skb); - if (rctbl) + if (hw->flags & IEEE80211_HW_SUPPORTS_RC_TABLE) ieee80211_get_tx_rates(vif, NULL, skb, info->control.rates, ARRAY_SIZE(info->control.rates)); @@ -1059,6 +1277,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, mac80211_hwsim_tx_frame(hw, skb, rcu_dereference(vif->chanctx_conf)->def.chan); + + if (vif->csa_active && ieee80211_csa_is_complete(vif)) + ieee80211_csa_finish(vif); } static enum hrtimer_restart @@ -1134,7 +1355,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) data->channel = conf->chandef.chan; - WARN_ON(data->channel && channels > 1); + WARN_ON(data->channel && data->use_chanctx); data->power_level = conf->power_level; if (!data->started || !data->beacon_int) @@ -1170,6 +1391,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, *total_flags = data->rx_filter; } +static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned int *count = data; + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + + if (vp->bcn_en) + (*count)++; +} + static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -1180,7 +1411,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, hwsim_check_magic(vif); - wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed); + wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n", + __func__, changed, vif->addr); if (changed & BSS_CHANGED_BSSID) { wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n", @@ -1202,6 +1434,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_ENABLED) { wiphy_debug(hw->wiphy, " BCN EN: %d\n", info->enable_beacon); + vp->bcn_en = info->enable_beacon; if (data->started && !hrtimer_is_queued(&data->beacon_timer.timer) && info->enable_beacon) { @@ -1215,8 +1448,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, tasklet_hrtimer_start(&data->beacon_timer, ns_to_ktime(until_tbtt * 1000), HRTIMER_MODE_REL); - } else if (!info->enable_beacon) - tasklet_hrtimer_cancel(&data->beacon_timer); + } else if (!info->enable_beacon) { + unsigned int count = 0; + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_bcn_en_iter, &count); + wiphy_debug(hw->wiphy, " beaconing vifs remaining: %u", + count); + if (count == 0) + tasklet_hrtimer_cancel(&data->beacon_timer); + } } if (changed & BSS_CHANGED_ERP_CTS_PROT) { @@ -1361,8 +1602,6 @@ static const struct nla_policy hwsim_testmode_policy[HWSIM_TM_ATTR_MAX + 1] = { [HWSIM_TM_ATTR_PS] = { .type = NLA_U32 }, }; -static int hwsim_fops_ps_write(void *dat, u64 val); - static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) @@ -1437,7 +1676,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, return 0; } -static void mac80211_hwsim_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void mac80211_hwsim_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) { /* Not implemented, queues only on kernel side */ } @@ -1464,7 +1705,7 @@ static void hw_scan_work(struct work_struct *work) req->channels[hwsim->scan_chan_idx]->center_freq); hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx]; - if (hwsim->tmp_chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || + if (hwsim->tmp_chan->flags & IEEE80211_CHAN_NO_IR || !req->n_ssids) { dwell = 120; } else { @@ -1673,8 +1914,7 @@ static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw, hwsim_check_chanctx_magic(ctx); } -static struct ieee80211_ops mac80211_hwsim_ops = -{ +static const struct ieee80211_ops mac80211_hwsim_ops = { .tx = mac80211_hwsim_tx, .start = mac80211_hwsim_start, .stop = mac80211_hwsim_stop, @@ -1699,208 +1939,307 @@ static struct ieee80211_ops mac80211_hwsim_ops = .set_tsf = mac80211_hwsim_set_tsf, }; +static struct ieee80211_ops mac80211_hwsim_mchan_ops; -static void mac80211_hwsim_free(void) +static int mac80211_hwsim_create_radio(int channels, const char *reg_alpha2, + const struct ieee80211_regdomain *regd, + bool reg_strict, bool p2p_device, + bool use_chanctx) { - struct list_head tmplist, *i, *tmp; - struct mac80211_hwsim_data *data, *tmpdata; + int err; + u8 addr[ETH_ALEN]; + struct mac80211_hwsim_data *data; + struct ieee80211_hw *hw; + enum ieee80211_band band; + const struct ieee80211_ops *ops = &mac80211_hwsim_ops; + int idx; - INIT_LIST_HEAD(&tmplist); + if (WARN_ON(channels > 1 && !use_chanctx)) + return -EINVAL; spin_lock_bh(&hwsim_radio_lock); - list_for_each_safe(i, tmp, &hwsim_radios) - list_move(i, &tmplist); + idx = hwsim_radio_idx++; spin_unlock_bh(&hwsim_radio_lock); - list_for_each_entry_safe(data, tmpdata, &tmplist, list) { - debugfs_remove(data->debugfs_group); - debugfs_remove(data->debugfs_ps); - debugfs_remove(data->debugfs); - ieee80211_unregister_hw(data->hw); - device_release_driver(data->dev); - device_unregister(data->dev); - ieee80211_free_hw(data->hw); + if (use_chanctx) + ops = &mac80211_hwsim_mchan_ops; + hw = ieee80211_alloc_hw(sizeof(*data), ops); + if (!hw) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw failed\n"); + err = -ENOMEM; + goto failed; + } + data = hw->priv; + data->hw = hw; + + data->dev = device_create(hwsim_class, NULL, 0, hw, "hwsim%d", idx); + if (IS_ERR(data->dev)) { + printk(KERN_DEBUG + "mac80211_hwsim: device_create failed (%ld)\n", + PTR_ERR(data->dev)); + err = -ENOMEM; + goto failed_drvdata; + } + data->dev->driver = &mac80211_hwsim_driver.driver; + err = device_bind_driver(data->dev); + if (err != 0) { + printk(KERN_DEBUG "mac80211_hwsim: device_bind_driver failed (%d)\n", + err); + goto failed_hw; } - class_destroy(hwsim_class); -} - -static struct platform_driver mac80211_hwsim_driver = { - .driver = { - .name = "mac80211_hwsim", - .owner = THIS_MODULE, - }, -}; - -static const struct net_device_ops hwsim_netdev_ops = { - .ndo_start_xmit = hwsim_mon_xmit, - .ndo_change_mtu = eth_change_mtu, - .ndo_set_mac_address = eth_mac_addr, - .ndo_validate_addr = eth_validate_addr, -}; - -static void hwsim_mon_setup(struct net_device *dev) -{ - dev->netdev_ops = &hwsim_netdev_ops; - dev->destructor = free_netdev; - ether_setup(dev); - dev->tx_queue_len = 0; - dev->type = ARPHRD_IEEE80211_RADIOTAP; - memset(dev->dev_addr, 0, ETH_ALEN); - dev->dev_addr[0] = 0x12; -} + skb_queue_head_init(&data->pending); -static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct sk_buff *skb; - struct ieee80211_pspoll *pspoll; + SET_IEEE80211_DEV(hw, data->dev); + memset(addr, 0, ETH_ALEN); + addr[0] = 0x02; + addr[3] = idx >> 8; + addr[4] = idx; + memcpy(data->addresses[0].addr, addr, ETH_ALEN); + memcpy(data->addresses[1].addr, addr, ETH_ALEN); + data->addresses[1].addr[0] |= 0x40; + hw->wiphy->n_addresses = 2; + hw->wiphy->addresses = data->addresses; + + data->channels = channels; + data->use_chanctx = use_chanctx; + data->idx = idx; + + if (data->use_chanctx) { + hw->wiphy->max_scan_ssids = 255; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 1000; + /* For channels > 1 DFS is not allowed */ + hw->wiphy->n_iface_combinations = 1; + hw->wiphy->iface_combinations = &data->if_combination; + if (p2p_device) + data->if_combination = hwsim_if_comb_p2p_dev[0]; + else + data->if_combination = hwsim_if_comb[0]; + data->if_combination.num_different_channels = data->channels; + } else if (p2p_device) { + hw->wiphy->iface_combinations = hwsim_if_comb_p2p_dev; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(hwsim_if_comb_p2p_dev); + } else { + hw->wiphy->iface_combinations = hwsim_if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(hwsim_if_comb); + } - if (!vp->assoc) - return; + INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); + INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); + + hw->queues = 5; + hw->offchannel_tx_hw_queue = 4; + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + if (p2p_device) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE); + + hw->flags = IEEE80211_HW_MFP_CAPABLE | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_STATIC_SMPS | + IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_QUEUE_CONTROL | + IEEE80211_HW_SUPPORTS_HT_CCK_RATES | + IEEE80211_HW_CHANCTX_STA_CSA; + if (rctbl) + hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; + + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; + hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + hw->wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; + + /* ask mac80211 to reserve space for magic */ + hw->vif_data_size = sizeof(struct hwsim_vif_priv); + hw->sta_data_size = sizeof(struct hwsim_sta_priv); + hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); + + memcpy(data->channels_2ghz, hwsim_channels_2ghz, + sizeof(hwsim_channels_2ghz)); + memcpy(data->channels_5ghz, hwsim_channels_5ghz, + sizeof(hwsim_channels_5ghz)); + memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband = &data->bands[band]; + switch (band) { + case IEEE80211_BAND_2GHZ: + sband->channels = data->channels_2ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_2ghz); + sband->bitrates = data->rates; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates); + break; + case IEEE80211_BAND_5GHZ: + sband->channels = data->channels_5ghz; + sband->n_channels = ARRAY_SIZE(hwsim_channels_5ghz); + sband->bitrates = data->rates + 4; + sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; + break; + default: + continue; + } - wiphy_debug(data->hw->wiphy, - "%s: send PS-Poll to %pM for aid %d\n", - __func__, vp->bssid, vp->aid); + sband->ht_cap.ht_supported = true; + sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + sband->ht_cap.ampdu_factor = 0x3; + sband->ht_cap.ampdu_density = 0x6; + memset(&sband->ht_cap.mcs, 0, + sizeof(sband->ht_cap.mcs)); + sband->ht_cap.mcs.rx_mask[0] = 0xff; + sband->ht_cap.mcs.rx_mask[1] = 0xff; + sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + hw->wiphy->bands[band] = sband; + + sband->vht_cap.vht_supported = true; + sband->vht_cap.cap = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_2 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + sband->vht_cap.vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); + sband->vht_cap.vht_mcs.tx_mcs_map = + sband->vht_cap.vht_mcs.rx_mcs_map; + } - skb = dev_alloc_skb(sizeof(*pspoll)); - if (!skb) - return; - pspoll = (void *) skb_put(skb, sizeof(*pspoll)); - pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | - IEEE80211_STYPE_PSPOLL | - IEEE80211_FCTL_PM); - pspoll->aid = cpu_to_le16(0xc000 | vp->aid); - memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); - memcpy(pspoll->ta, mac, ETH_ALEN); + /* By default all radios belong to the first group */ + data->group = 1; + mutex_init(&data->mutex); - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); - rcu_read_unlock(); -} + /* Enable frame retransmissions for lossy channels */ + hw->max_rates = 4; + hw->max_rate_tries = 11; -static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, - struct ieee80211_vif *vif, int ps) -{ - struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - struct sk_buff *skb; - struct ieee80211_hdr *hdr; + if (reg_strict) + hw->wiphy->regulatory_flags |= REGULATORY_STRICT_REG; + if (regd) { + hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(hw->wiphy, regd); + /* give the regulatory workqueue a chance to run */ + schedule_timeout_interruptible(1); + } - if (!vp->assoc) - return; + err = ieee80211_register_hw(hw); + if (err < 0) { + printk(KERN_DEBUG "mac80211_hwsim: ieee80211_register_hw failed (%d)\n", + err); + goto failed_hw; + } - wiphy_debug(data->hw->wiphy, - "%s: send data::nullfunc to %pM ps=%d\n", - __func__, vp->bssid, ps); + wiphy_debug(hw->wiphy, "hwaddr %pM registered\n", hw->wiphy->perm_addr); - skb = dev_alloc_skb(sizeof(*hdr)); - if (!skb) - return; - hdr = (void *) skb_put(skb, sizeof(*hdr) - ETH_ALEN); - hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - (ps ? IEEE80211_FCTL_PM : 0)); - hdr->duration_id = cpu_to_le16(0); - memcpy(hdr->addr1, vp->bssid, ETH_ALEN); - memcpy(hdr->addr2, mac, ETH_ALEN); - memcpy(hdr->addr3, vp->bssid, ETH_ALEN); + if (reg_alpha2) + regulatory_hint(hw->wiphy, reg_alpha2); - rcu_read_lock(); - mac80211_hwsim_tx_frame(data->hw, skb, - rcu_dereference(vif->chanctx_conf)->def.chan); - rcu_read_unlock(); -} + data->debugfs = debugfs_create_dir("hwsim", hw->wiphy->debugfsdir); + debugfs_create_file("ps", 0666, data->debugfs, data, &hwsim_fops_ps); + debugfs_create_file("group", 0666, data->debugfs, data, + &hwsim_fops_group); + if (!data->use_chanctx) + debugfs_create_file("dfs_simulate_radar", 0222, + data->debugfs, + data, &hwsim_simulate_radar); + tasklet_hrtimer_init(&data->beacon_timer, + mac80211_hwsim_beacon, + CLOCK_MONOTONIC_RAW, HRTIMER_MODE_ABS); -static void hwsim_send_nullfunc_ps(void *dat, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - hwsim_send_nullfunc(data, mac, vif, 1); -} + spin_lock_bh(&hwsim_radio_lock); + list_add_tail(&data->list, &hwsim_radios); + spin_unlock_bh(&hwsim_radio_lock); + return idx; -static void hwsim_send_nullfunc_no_ps(void *dat, u8 *mac, - struct ieee80211_vif *vif) -{ - struct mac80211_hwsim_data *data = dat; - hwsim_send_nullfunc(data, mac, vif, 0); +failed_hw: + device_unregister(data->dev); +failed_drvdata: + ieee80211_free_hw(hw); +failed: + return err; } - -static int hwsim_fops_ps_read(void *dat, u64 *val) +static void mac80211_hwsim_destroy_radio(struct mac80211_hwsim_data *data) { - struct mac80211_hwsim_data *data = dat; - *val = data->ps; - return 0; + debugfs_remove_recursive(data->debugfs); + ieee80211_unregister_hw(data->hw); + device_release_driver(data->dev); + device_unregister(data->dev); + ieee80211_free_hw(data->hw); } -static int hwsim_fops_ps_write(void *dat, u64 val) +static void mac80211_hwsim_free(void) { - struct mac80211_hwsim_data *data = dat; - enum ps_mode old_ps; - - if (val != PS_DISABLED && val != PS_ENABLED && val != PS_AUTO_POLL && - val != PS_MANUAL_POLL) - return -EINVAL; - - old_ps = data->ps; - data->ps = val; + struct mac80211_hwsim_data *data; - if (val == PS_MANUAL_POLL) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_ps_poll, data); - data->ps_poll_pending = true; - } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_ps, - data); - } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_no_ps, - data); + spin_lock_bh(&hwsim_radio_lock); + while ((data = list_first_entry_or_null(&hwsim_radios, + struct mac80211_hwsim_data, + list))) { + list_del(&data->list); + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_destroy_radio(data); + spin_lock_bh(&hwsim_radio_lock); } - - return 0; + spin_unlock_bh(&hwsim_radio_lock); + class_destroy(hwsim_class); } -DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_ps, hwsim_fops_ps_read, hwsim_fops_ps_write, - "%llu\n"); - +static const struct net_device_ops hwsim_netdev_ops = { + .ndo_start_xmit = hwsim_mon_xmit, + .ndo_change_mtu = eth_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, +}; -static int hwsim_fops_group_read(void *dat, u64 *val) +static void hwsim_mon_setup(struct net_device *dev) { - struct mac80211_hwsim_data *data = dat; - *val = data->group; - return 0; + dev->netdev_ops = &hwsim_netdev_ops; + dev->destructor = free_netdev; + ether_setup(dev); + dev->tx_queue_len = 0; + dev->type = ARPHRD_IEEE80211_RADIOTAP; + memset(dev->dev_addr, 0, ETH_ALEN); + dev->dev_addr[0] = 0x12; } -static int hwsim_fops_group_write(void *dat, u64 val) -{ - struct mac80211_hwsim_data *data = dat; - data->group = val; - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group, - hwsim_fops_group_read, hwsim_fops_group_write, - "%llx\n"); - -static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr( - struct mac_address *addr) +static struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(const u8 *addr) { struct mac80211_hwsim_data *data; bool _found = false; spin_lock_bh(&hwsim_radio_lock); list_for_each_entry(data, &hwsim_radios, list) { - if (memcmp(data->addresses[1].addr, addr, - sizeof(struct mac_address)) == 0) { + if (memcmp(data->addresses[1].addr, addr, ETH_ALEN) == 0) { _found = true; break; } @@ -1923,27 +2262,26 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, struct hwsim_tx_rate *tx_attempts; unsigned long ret_skb_ptr; struct sk_buff *skb, *tmp; - struct mac_address *src; + const u8 *src; unsigned int hwsim_flags; - int i; bool found = false; + if (info->snd_portid != wmediumd_portid) + return -EINVAL; + if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] || - !info->attrs[HWSIM_ATTR_FLAGS] || - !info->attrs[HWSIM_ATTR_COOKIE] || - !info->attrs[HWSIM_ATTR_TX_INFO]) + !info->attrs[HWSIM_ATTR_FLAGS] || + !info->attrs[HWSIM_ATTR_COOKIE] || + !info->attrs[HWSIM_ATTR_TX_INFO]) goto out; - src = (struct mac_address *)nla_data( - info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); + src = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]); hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]); - ret_skb_ptr = nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]); data2 = get_hwsim_data_ref_from_addr(src); - - if (data2 == NULL) + if (!data2) goto out; /* look for the skb matching the cookie passed back from user */ @@ -1982,7 +2320,7 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, (hwsim_flags & HWSIM_TX_STAT_ACK)) { if (skb->len >= 16) { hdr = (struct ieee80211_hdr *) skb->data; - mac80211_hwsim_monitor_ack(txi->rate_driver_data[0], + mac80211_hwsim_monitor_ack(data2->channel, hdr->addr2); } txi->flags |= IEEE80211_TX_STAT_ACK; @@ -2000,38 +2338,37 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, struct mac80211_hwsim_data *data2; struct ieee80211_rx_status rx_status; - struct mac_address *dst; + const u8 *dst; int frame_data_len; - char *frame_data; + void *frame_data; struct sk_buff *skb = NULL; + if (info->snd_portid != wmediumd_portid) + return -EINVAL; + if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || !info->attrs[HWSIM_ATTR_FRAME] || !info->attrs[HWSIM_ATTR_RX_RATE] || !info->attrs[HWSIM_ATTR_SIGNAL]) goto out; - dst = (struct mac_address *)nla_data( - info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); - + dst = (void *)nla_data(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]); frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]); - frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); + frame_data = (void *)nla_data(info->attrs[HWSIM_ATTR_FRAME]); /* Allocate new skb here */ skb = alloc_skb(frame_data_len, GFP_KERNEL); if (skb == NULL) goto err; - if (frame_data_len <= IEEE80211_MAX_DATA_LEN) { - /* Copy the data */ - memcpy(skb_put(skb, frame_data_len), frame_data, - frame_data_len); - } else + if (frame_data_len > IEEE80211_MAX_DATA_LEN) goto err; - data2 = get_hwsim_data_ref_from_addr(dst); + /* Copy the data */ + memcpy(skb_put(skb, frame_data_len), frame_data, frame_data_len); - if (data2 == NULL) + data2 = get_hwsim_data_ref_from_addr(dst); + if (!data2) goto out; /* check if radio is configured properly */ @@ -2039,7 +2376,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, if (data2->idle || !data2->started) goto out; - /*A frame is received from user space*/ + /* A frame is received from user space */ memset(&rx_status, 0, sizeof(rx_status)); rx_status.freq = data2->channel->center_freq; rx_status.band = data2->channel->band; @@ -2061,8 +2398,24 @@ out: static int hwsim_register_received_nl(struct sk_buff *skb_2, struct genl_info *info) { - if (info == NULL) - goto out; + struct mac80211_hwsim_data *data; + int chans = 1; + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) + chans = max(chans, data->channels); + spin_unlock_bh(&hwsim_radio_lock); + + /* In the future we should revise the userspace API and allow it + * to set a flag that it does support multi-channel, then we can + * let this pass conditionally on the flag. + * For current userspace, prohibit it since it won't work right. + */ + if (chans > 1) + return -EOPNOTSUPP; + + if (wmediumd_portid) + return -EBUSY; wmediumd_portid = info->snd_portid; @@ -2070,13 +2423,65 @@ static int hwsim_register_received_nl(struct sk_buff *skb_2, "switching to wmediumd mode with pid %d\n", info->snd_portid); return 0; -out: - printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); - return -EINVAL; +} + +static int hwsim_create_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + unsigned int chans = channels; + const char *alpha2 = NULL; + const struct ieee80211_regdomain *regd = NULL; + bool reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG]; + bool p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE]; + bool use_chanctx; + + if (info->attrs[HWSIM_ATTR_CHANNELS]) + chans = nla_get_u32(info->attrs[HWSIM_ATTR_CHANNELS]); + + if (info->attrs[HWSIM_ATTR_USE_CHANCTX]) + use_chanctx = true; + else + use_chanctx = (chans > 1); + + if (info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]) + alpha2 = nla_data(info->attrs[HWSIM_ATTR_REG_HINT_ALPHA2]); + + if (info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]) { + u32 idx = nla_get_u32(info->attrs[HWSIM_ATTR_REG_CUSTOM_REG]); + + if (idx >= ARRAY_SIZE(hwsim_world_regdom_custom)) + return -EINVAL; + regd = hwsim_world_regdom_custom[idx]; + } + + return mac80211_hwsim_create_radio(chans, alpha2, regd, reg_strict, + p2p_device, use_chanctx); +} + +static int hwsim_destroy_radio_nl(struct sk_buff *msg, struct genl_info *info) +{ + struct mac80211_hwsim_data *data; + int idx; + + if (!info->attrs[HWSIM_ATTR_RADIO_ID]) + return -EINVAL; + idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]); + + spin_lock_bh(&hwsim_radio_lock); + list_for_each_entry(data, &hwsim_radios, list) { + if (data->idx != idx) + continue; + list_del(&data->list); + spin_unlock_bh(&hwsim_radio_lock); + mac80211_hwsim_destroy_radio(data); + return 0; + } + spin_unlock_bh(&hwsim_radio_lock); + + return -ENODEV; } /* Generic Netlink operations array */ -static struct genl_ops hwsim_ops[] = { +static const struct genl_ops hwsim_ops[] = { { .cmd = HWSIM_CMD_REGISTER, .policy = hwsim_genl_policy, @@ -2093,6 +2498,18 @@ static struct genl_ops hwsim_ops[] = { .policy = hwsim_genl_policy, .doit = hwsim_tx_info_frame_received_nl, }, + { + .cmd = HWSIM_CMD_CREATE_RADIO, + .policy = hwsim_genl_policy, + .doit = hwsim_create_radio_nl, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = HWSIM_CMD_DESTROY_RADIO, + .policy = hwsim_genl_policy, + .doit = hwsim_destroy_radio_nl, + .flags = GENL_ADMIN_PERM, + }, }; static int mac80211_hwsim_netlink_notify(struct notifier_block *nb, @@ -2121,14 +2538,9 @@ static int hwsim_init_netlink(void) { int rc; - /* userspace test API hasn't been adjusted for multi-channel */ - if (channels > 1) - return 0; - printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); - rc = genl_register_family_with_ops(&hwsim_genl_family, - hwsim_ops, ARRAY_SIZE(hwsim_ops)); + rc = genl_register_family_with_ops(&hwsim_genl_family, hwsim_ops); if (rc) goto failure; @@ -2145,77 +2557,36 @@ failure: static void hwsim_exit_netlink(void) { - int ret; - - /* userspace test API hasn't been adjusted for multi-channel */ - if (channels > 1) - return; - - printk(KERN_INFO "mac80211_hwsim: closing netlink\n"); /* unregister the notifier */ netlink_unregister_notifier(&hwsim_netlink_notifier); /* unregister the family */ - ret = genl_unregister_family(&hwsim_genl_family); - if (ret) - printk(KERN_DEBUG "mac80211_hwsim: " - "unregister family %i\n", ret); + genl_unregister_family(&hwsim_genl_family); } -static const struct ieee80211_iface_limit hwsim_if_limits[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC) }, - { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | -#ifdef CONFIG_MAC80211_MESH - BIT(NL80211_IFTYPE_MESH_POINT) | -#endif - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) }, - { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, -}; - -static struct ieee80211_iface_combination hwsim_if_comb = { - .limits = hwsim_if_limits, - .n_limits = ARRAY_SIZE(hwsim_if_limits), - .max_interfaces = 2048, - .num_different_channels = 1, -}; - static int __init init_mac80211_hwsim(void) { - int i, err = 0; - u8 addr[ETH_ALEN]; - struct mac80211_hwsim_data *data; - struct ieee80211_hw *hw; - enum ieee80211_band band; + int i, err; - if (radios < 1 || radios > 100) + if (radios < 0 || radios > 100) return -EINVAL; if (channels < 1) return -EINVAL; - if (channels > 1) { - hwsim_if_comb.num_different_channels = channels; - mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan; - mac80211_hwsim_ops.cancel_hw_scan = - mac80211_hwsim_cancel_hw_scan; - mac80211_hwsim_ops.sw_scan_start = NULL; - mac80211_hwsim_ops.sw_scan_complete = NULL; - mac80211_hwsim_ops.remain_on_channel = - mac80211_hwsim_roc; - mac80211_hwsim_ops.cancel_remain_on_channel = - mac80211_hwsim_croc; - mac80211_hwsim_ops.add_chanctx = - mac80211_hwsim_add_chanctx; - mac80211_hwsim_ops.remove_chanctx = - mac80211_hwsim_remove_chanctx; - mac80211_hwsim_ops.change_chanctx = - mac80211_hwsim_change_chanctx; - mac80211_hwsim_ops.assign_vif_chanctx = - mac80211_hwsim_assign_vif_chanctx; - mac80211_hwsim_ops.unassign_vif_chanctx = - mac80211_hwsim_unassign_vif_chanctx; - } + mac80211_hwsim_mchan_ops = mac80211_hwsim_ops; + mac80211_hwsim_mchan_ops.hw_scan = mac80211_hwsim_hw_scan; + mac80211_hwsim_mchan_ops.cancel_hw_scan = mac80211_hwsim_cancel_hw_scan; + mac80211_hwsim_mchan_ops.sw_scan_start = NULL; + mac80211_hwsim_mchan_ops.sw_scan_complete = NULL; + mac80211_hwsim_mchan_ops.remain_on_channel = mac80211_hwsim_roc; + mac80211_hwsim_mchan_ops.cancel_remain_on_channel = mac80211_hwsim_croc; + mac80211_hwsim_mchan_ops.add_chanctx = mac80211_hwsim_add_chanctx; + mac80211_hwsim_mchan_ops.remove_chanctx = mac80211_hwsim_remove_chanctx; + mac80211_hwsim_mchan_ops.change_chanctx = mac80211_hwsim_change_chanctx; + mac80211_hwsim_mchan_ops.assign_vif_chanctx = + mac80211_hwsim_assign_vif_chanctx; + mac80211_hwsim_mchan_ops.unassign_vif_chanctx = + mac80211_hwsim_unassign_vif_chanctx; spin_lock_init(&hwsim_radio_lock); INIT_LIST_HEAD(&hwsim_radios); @@ -2227,348 +2598,118 @@ static int __init init_mac80211_hwsim(void) hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); if (IS_ERR(hwsim_class)) { err = PTR_ERR(hwsim_class); - goto failed_unregister_driver; + goto out_unregister_driver; } - memset(addr, 0, ETH_ALEN); - addr[0] = 0x02; - for (i = 0; i < radios; i++) { - printk(KERN_DEBUG "mac80211_hwsim: Initializing radio %d\n", - i); - hw = ieee80211_alloc_hw(sizeof(*data), &mac80211_hwsim_ops); - if (!hw) { - printk(KERN_DEBUG "mac80211_hwsim: ieee80211_alloc_hw " - "failed\n"); - err = -ENOMEM; - goto failed; - } - data = hw->priv; - data->hw = hw; - - data->dev = device_create(hwsim_class, NULL, 0, hw, - "hwsim%d", i); - if (IS_ERR(data->dev)) { - printk(KERN_DEBUG - "mac80211_hwsim: device_create failed (%ld)\n", - PTR_ERR(data->dev)); - err = -ENOMEM; - goto failed_drvdata; - } - data->dev->driver = &mac80211_hwsim_driver.driver; - err = device_bind_driver(data->dev); - if (err != 0) { - printk(KERN_DEBUG - "mac80211_hwsim: device_bind_driver failed (%d)\n", - err); - goto failed_hw; - } - - skb_queue_head_init(&data->pending); - - SET_IEEE80211_DEV(hw, data->dev); - addr[3] = i >> 8; - addr[4] = i; - memcpy(data->addresses[0].addr, addr, ETH_ALEN); - memcpy(data->addresses[1].addr, addr, ETH_ALEN); - data->addresses[1].addr[0] |= 0x40; - hw->wiphy->n_addresses = 2; - hw->wiphy->addresses = data->addresses; - - hw->wiphy->iface_combinations = &hwsim_if_comb; - hw->wiphy->n_iface_combinations = 1; - - if (channels > 1) { - hw->wiphy->max_scan_ssids = 255; - hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; - hw->wiphy->max_remain_on_channel_duration = 1000; - } - - INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); - INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); - - hw->channel_change_time = 1; - hw->queues = 5; - hw->offchannel_tx_hw_queue = 4; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT) | - BIT(NL80211_IFTYPE_P2P_DEVICE); - - hw->flags = IEEE80211_HW_MFP_CAPABLE | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_STATIC_SMPS | - IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_WANT_MONITOR_VIF | - IEEE80211_HW_QUEUE_CONTROL; - if (rctbl) - hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE; - - hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_AP_UAPSD; - hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - - /* ask mac80211 to reserve space for magic */ - hw->vif_data_size = sizeof(struct hwsim_vif_priv); - hw->sta_data_size = sizeof(struct hwsim_sta_priv); - hw->chanctx_data_size = sizeof(struct hwsim_chanctx_priv); - - memcpy(data->channels_2ghz, hwsim_channels_2ghz, - sizeof(hwsim_channels_2ghz)); - memcpy(data->channels_5ghz, hwsim_channels_5ghz, - sizeof(hwsim_channels_5ghz)); - memcpy(data->rates, hwsim_rates, sizeof(hwsim_rates)); - - for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband = &data->bands[band]; - switch (band) { - case IEEE80211_BAND_2GHZ: - sband->channels = data->channels_2ghz; - sband->n_channels = - ARRAY_SIZE(hwsim_channels_2ghz); - sband->bitrates = data->rates; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates); - break; - case IEEE80211_BAND_5GHZ: - sband->channels = data->channels_5ghz; - sband->n_channels = - ARRAY_SIZE(hwsim_channels_5ghz); - sband->bitrates = data->rates + 4; - sband->n_bitrates = ARRAY_SIZE(hwsim_rates) - 4; - break; - default: - continue; - } - - sband->ht_cap.ht_supported = true; - sband->ht_cap.cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_GRN_FLD | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - sband->ht_cap.ampdu_factor = 0x3; - sband->ht_cap.ampdu_density = 0x6; - memset(&sband->ht_cap.mcs, 0, - sizeof(sband->ht_cap.mcs)); - sband->ht_cap.mcs.rx_mask[0] = 0xff; - sband->ht_cap.mcs.rx_mask[1] = 0xff; - sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - - hw->wiphy->bands[band] = sband; - - sband->vht_cap.vht_supported = true; - sband->vht_cap.cap = - IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | - IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | - IEEE80211_VHT_CAP_RXLDPC | - IEEE80211_VHT_CAP_SHORT_GI_80 | - IEEE80211_VHT_CAP_SHORT_GI_160 | - IEEE80211_VHT_CAP_TXSTBC | - IEEE80211_VHT_CAP_RXSTBC_1 | - IEEE80211_VHT_CAP_RXSTBC_2 | - IEEE80211_VHT_CAP_RXSTBC_3 | - IEEE80211_VHT_CAP_RXSTBC_4 | - IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; - sband->vht_cap.vht_mcs.rx_mcs_map = - cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | - IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | - IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); - sband->vht_cap.vht_mcs.tx_mcs_map = - sband->vht_cap.vht_mcs.rx_mcs_map; - } - /* By default all radios are belonging to the first group */ - data->group = 1; - mutex_init(&data->mutex); - - /* Enable frame retransmissions for lossy channels */ - hw->max_rates = 4; - hw->max_rate_tries = 11; + const char *reg_alpha2 = NULL; + const struct ieee80211_regdomain *regd = NULL; + bool reg_strict = false; - /* Work to be done prior to ieee80211_register_hw() */ switch (regtest) { - case HWSIM_REGTEST_DISABLED: - case HWSIM_REGTEST_DRIVER_REG_FOLLOW: - case HWSIM_REGTEST_DRIVER_REG_ALL: case HWSIM_REGTEST_DIFF_COUNTRY: - /* - * Nothing to be done for driver regulatory domain - * hints prior to ieee80211_register_hw() - */ - break; - case HWSIM_REGTEST_WORLD_ROAM: - if (i == 0) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - } - break; - case HWSIM_REGTEST_CUSTOM_WORLD: - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - break; - case HWSIM_REGTEST_CUSTOM_WORLD_2: - if (i == 0) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - } else if (i == 1) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_02); - } - break; - case HWSIM_REGTEST_STRICT_ALL: - hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; - break; - case HWSIM_REGTEST_STRICT_FOLLOW: - case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: - if (i == 0) - hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; - break; - case HWSIM_REGTEST_ALL: - if (i == 0) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_01); - } else if (i == 1) { - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy_apply_custom_regulatory(hw->wiphy, - &hwsim_world_regdom_custom_02); - } else if (i == 4) - hw->wiphy->flags |= WIPHY_FLAG_STRICT_REGULATORY; - break; - default: - break; - } - - /* give the regulatory workqueue a chance to run */ - if (regtest) - schedule_timeout_interruptible(1); - err = ieee80211_register_hw(hw); - if (err < 0) { - printk(KERN_DEBUG "mac80211_hwsim: " - "ieee80211_register_hw failed (%d)\n", err); - goto failed_hw; - } - - /* Work to be done after to ieee80211_register_hw() */ - switch (regtest) { - case HWSIM_REGTEST_WORLD_ROAM: - case HWSIM_REGTEST_DISABLED: + if (i < ARRAY_SIZE(hwsim_alpha2s)) + reg_alpha2 = hwsim_alpha2s[i]; break; case HWSIM_REGTEST_DRIVER_REG_FOLLOW: if (!i) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); + reg_alpha2 = hwsim_alpha2s[0]; break; - case HWSIM_REGTEST_DRIVER_REG_ALL: case HWSIM_REGTEST_STRICT_ALL: - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); + reg_strict = true; + case HWSIM_REGTEST_DRIVER_REG_ALL: + reg_alpha2 = hwsim_alpha2s[0]; break; - case HWSIM_REGTEST_DIFF_COUNTRY: - if (i < ARRAY_SIZE(hwsim_alpha2s)) - regulatory_hint(hw->wiphy, hwsim_alpha2s[i]); + case HWSIM_REGTEST_WORLD_ROAM: + if (i == 0) + regd = &hwsim_world_regdom_custom_01; break; case HWSIM_REGTEST_CUSTOM_WORLD: + regd = &hwsim_world_regdom_custom_01; + break; case HWSIM_REGTEST_CUSTOM_WORLD_2: - /* - * Nothing to be done for custom world regulatory - * domains after to ieee80211_register_hw - */ + if (i == 0) + regd = &hwsim_world_regdom_custom_01; + else if (i == 1) + regd = &hwsim_world_regdom_custom_02; break; case HWSIM_REGTEST_STRICT_FOLLOW: - if (i == 0) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); + if (i == 0) { + reg_strict = true; + reg_alpha2 = hwsim_alpha2s[0]; + } break; case HWSIM_REGTEST_STRICT_AND_DRIVER_REG: - if (i == 0) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - else if (i == 1) - regulatory_hint(hw->wiphy, hwsim_alpha2s[1]); + if (i == 0) { + reg_strict = true; + reg_alpha2 = hwsim_alpha2s[0]; + } else if (i == 1) { + reg_alpha2 = hwsim_alpha2s[1]; + } break; case HWSIM_REGTEST_ALL: - if (i == 2) - regulatory_hint(hw->wiphy, hwsim_alpha2s[0]); - else if (i == 3) - regulatory_hint(hw->wiphy, hwsim_alpha2s[1]); - else if (i == 4) - regulatory_hint(hw->wiphy, hwsim_alpha2s[2]); + switch (i) { + case 0: + regd = &hwsim_world_regdom_custom_01; + break; + case 1: + regd = &hwsim_world_regdom_custom_02; + break; + case 2: + reg_alpha2 = hwsim_alpha2s[0]; + break; + case 3: + reg_alpha2 = hwsim_alpha2s[1]; + break; + case 4: + reg_strict = true; + reg_alpha2 = hwsim_alpha2s[2]; + break; + } break; default: break; } - wiphy_debug(hw->wiphy, "hwaddr %pm registered\n", - hw->wiphy->perm_addr); - - data->debugfs = debugfs_create_dir("hwsim", - hw->wiphy->debugfsdir); - data->debugfs_ps = debugfs_create_file("ps", 0666, - data->debugfs, data, - &hwsim_fops_ps); - data->debugfs_group = debugfs_create_file("group", 0666, - data->debugfs, data, - &hwsim_fops_group); - - tasklet_hrtimer_init(&data->beacon_timer, - mac80211_hwsim_beacon, - CLOCK_REALTIME, HRTIMER_MODE_ABS); - - list_add_tail(&data->list, &hwsim_radios); + err = mac80211_hwsim_create_radio(channels, reg_alpha2, + regd, reg_strict, + support_p2p_device, + channels > 1); + if (err < 0) + goto out_free_radios; } hwsim_mon = alloc_netdev(0, "hwsim%d", hwsim_mon_setup); if (hwsim_mon == NULL) { err = -ENOMEM; - goto failed; + goto out_free_radios; } rtnl_lock(); - err = dev_alloc_name(hwsim_mon, hwsim_mon->name); - if (err < 0) - goto failed_mon; - + if (err < 0) { + rtnl_unlock(); + goto out_free_radios; + } err = register_netdevice(hwsim_mon); - if (err < 0) - goto failed_mon; - + if (err < 0) { + rtnl_unlock(); + goto out_free_mon; + } rtnl_unlock(); err = hwsim_init_netlink(); if (err < 0) - goto failed_nl; + goto out_free_mon; return 0; -failed_nl: - printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n"); - return err; - -failed_mon: - rtnl_unlock(); +out_free_mon: free_netdev(hwsim_mon); +out_free_radios: mac80211_hwsim_free(); - return err; - -failed_hw: - device_unregister(data->dev); -failed_drvdata: - ieee80211_free_hw(hw); -failed: - mac80211_hwsim_free(); -failed_unregister_driver: +out_unregister_driver: platform_driver_unregister(&mac80211_hwsim_driver); return err; } diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h index afaad5a443b..c9d0315575b 100644 --- a/drivers/net/wireless/mac80211_hwsim.h +++ b/drivers/net/wireless/mac80211_hwsim.h @@ -65,6 +65,9 @@ enum hwsim_tx_control_flags { * kernel, uses: * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS, * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE + * @HWSIM_CMD_CREATE_RADIO: create a new radio with the given parameters, + * returns the radio ID (>= 0) or negative on errors + * @HWSIM_CMD_DESTROY_RADIO: destroy a radio * @__HWSIM_CMD_MAX: enum limit */ enum { @@ -72,6 +75,8 @@ enum { HWSIM_CMD_REGISTER, HWSIM_CMD_FRAME, HWSIM_CMD_TX_INFO_FRAME, + HWSIM_CMD_CREATE_RADIO, + HWSIM_CMD_DESTROY_RADIO, __HWSIM_CMD_MAX, }; #define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1) @@ -94,6 +99,18 @@ enum { space * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame + * @HWSIM_ATTR_CHANNELS: u32 attribute used with the %HWSIM_CMD_CREATE_RADIO + * command giving the number of channels supported by the new radio + * @HWSIM_ATTR_RADIO_ID: u32 attribute used with %HWSIM_CMD_DESTROY_RADIO + * only to destroy a radio + * @HWSIM_ATTR_REG_HINT_ALPHA2: alpha2 for regulatoro driver hint + * (nla string, length 2) + * @HWSIM_ATTR_REG_CUSTOM_REG: custom regulatory domain index (u32 attribute) + * @HWSIM_ATTR_REG_STRICT_REG: request REGULATORY_STRICT_REG (flag attribute) + * @HWSIM_ATTR_SUPPORT_P2P_DEVICE: support P2P Device virtual interface (flag) + * @HWSIM_ATTR_USE_CHANCTX: used with the %HWSIM_CMD_CREATE_RADIO + * command to force use of channel contexts even when only a + * single channel is supported * @__HWSIM_ATTR_MAX: enum limit */ @@ -108,6 +125,13 @@ enum { HWSIM_ATTR_SIGNAL, HWSIM_ATTR_TX_INFO, HWSIM_ATTR_COOKIE, + HWSIM_ATTR_CHANNELS, + HWSIM_ATTR_RADIO_ID, + HWSIM_ATTR_REG_HINT_ALPHA2, + HWSIM_ATTR_REG_CUSTOM_REG, + HWSIM_ATTR_REG_STRICT_REG, + HWSIM_ATTR_SUPPORT_P2P_DEVICE, + HWSIM_ATTR_USE_CHANCTX, __HWSIM_ATTR_MAX, }; #define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1) diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c index 5e0eec4d71c..706831df1fa 100644 --- a/drivers/net/wireless/mwifiex/11ac.c +++ b/drivers/net/wireless/mwifiex/11ac.c @@ -23,6 +23,31 @@ #include "main.h" #include "11ac.h" +/* Tables of the MCS map to the highest data rate (in Mbps) supported + * for long GI. + */ +static const u16 max_rate_lgi_80MHZ[8][3] = { + {0x124, 0x15F, 0x186}, /* NSS = 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ + {0x36D, 0x41D, 0x492}, /* NSS = 3 */ + {0x492, 0x57C, 0x618}, /* NSS = 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ + {0x924, 0xAF8, 0xC30} /* NSS = 8 */ +}; + +static const u16 max_rate_lgi_160MHZ[8][3] = { + {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ + {0x492, 0x57C, 0x618}, /* NSS = 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ +}; + /* This function converts the 2-bit MCS map to the highest long GI * VHT data rate. */ @@ -30,33 +55,10 @@ static u16 mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, u8 bands, u16 mcs_map) { - u8 i, nss, max_mcs; + u8 i, nss, mcs; u16 max_rate = 0; u32 usr_vht_cap_info = 0; struct mwifiex_adapter *adapter = priv->adapter; - /* tables of the MCS map to the highest data rate (in Mbps) - * supported for long GI - */ - u16 max_rate_lgi_80MHZ[8][3] = { - {0x124, 0x15F, 0x186}, /* NSS = 1 */ - {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ - {0x36D, 0x41D, 0x492}, /* NSS = 3 */ - {0x492, 0x57C, 0x618}, /* NSS = 4 */ - {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ - {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ - {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ - {0x924, 0xAF8, 0xC30} /* NSS = 8 */ - }; - u16 max_rate_lgi_160MHZ[8][3] = { - {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ - {0x492, 0x57C, 0x618}, /* NSS = 2 */ - {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ - {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ - {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ - {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ - {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ - {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ - }; if (bands & BAND_AAC) usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; @@ -64,29 +66,29 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; /* find the max NSS supported */ - nss = 0; - for (i = 0; i < 8; i++) { - max_mcs = (mcs_map >> (2 * i)) & 0x3; - if (max_mcs < 3) + nss = 1; + for (i = 1; i <= 8; i++) { + mcs = GET_VHTNSSMCS(mcs_map, i); + if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) nss = i; } - max_mcs = (mcs_map >> (2 * nss)) & 0x3; + mcs = GET_VHTNSSMCS(mcs_map, nss); - /* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9 */ - if (max_mcs >= 3) - max_mcs = 2; + /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ + if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { /* support 160 MHz */ - max_rate = max_rate_lgi_160MHZ[nss][max_mcs]; + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; if (!max_rate) /* MCS9 is not supported in NSS6 */ - max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1]; + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; } else { - max_rate = max_rate_lgi_80MHZ[nss][max_mcs]; + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; if (!max_rate) /* MCS9 is not supported in NSS3 */ - max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1]; + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; } return max_rate; @@ -94,21 +96,20 @@ mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, static void mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, - struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands) + struct ieee80211_vht_cap *vht_cap, u8 bands) { struct mwifiex_adapter *adapter = priv->adapter; if (bands & BAND_A) - vht_cap->vht_cap.vht_cap_info = + vht_cap->vht_cap_info = cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); else - vht_cap->vht_cap.vht_cap_info = + vht_cap->vht_cap_info = cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); } -static void -mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct mwifiex_ie_types_vhtcap *vht_cap, u8 bands) +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands) { struct mwifiex_adapter *adapter = priv->adapter; u16 mcs_map_user, mcs_map_resp, mcs_map_result; @@ -119,46 +120,48 @@ mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.rx_mcs_map); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); mcs_map_result = 0; for (nss = 1; nss <= 8; nss++) { mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - if ((mcs_user == NO_NSS_SUPPORT) || - (mcs_resp == NO_NSS_SUPPORT)) - SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); else SET_VHTNSSMCS(mcs_map_result, nss, min(mcs_user, mcs_resp)); } - vht_cap->vht_cap.supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); + vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->vht_cap.supp_mcs.rx_highest = cpu_to_le16(tmp); + vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->vht_cap.supp_mcs.tx_mcs_map); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.tx_mcs_map); mcs_map_result = 0; for (nss = 1; nss <= 8; nss++) { mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); - if ((mcs_user == NO_NSS_SUPPORT) || - (mcs_resp == NO_NSS_SUPPORT)) - SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); else SET_VHTNSSMCS(mcs_map_result, nss, min(mcs_user, mcs_resp)); } - vht_cap->vht_cap.supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); + vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->vht_cap.supp_mcs.tx_highest = cpu_to_le16(tmp); + vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); return; } @@ -189,11 +192,11 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, vht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_vht_cap)); memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_cap + - sizeof(struct ieee_types_header), + (u8 *)bss_desc->bcn_vht_cap, le16_to_cpu(vht_cap->header.len)); - mwifiex_fill_vht_cap_tlv(priv, vht_cap, bss_desc->bss_band); + mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, + bss_desc->bss_band); *buffer += sizeof(*vht_cap); ret_len += sizeof(*vht_cap); } @@ -209,8 +212,7 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, sizeof(struct mwifiex_ie_types_header)); memcpy((u8 *)vht_op + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_oper + - sizeof(struct ieee_types_header), + (u8 *)bss_desc->bcn_vht_oper, le16_to_cpu(vht_op->header.len)); /* negotiate the channel width and central freq @@ -300,3 +302,81 @@ void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv) return; } + +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + vht_oper = bss_desc->bcn_vht_oper; + + if (!bss_desc->bcn_vht_cap || !vht_oper) + return false; + + if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT) + return false; + + return true; +} + +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw) +{ + u8 center_freq_idx = 0; + + if (band & BAND_AAC) { + switch (pri_chan) { + case 36: + case 40: + case 44: + case 48: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 42; + break; + case 52: + case 56: + case 60: + case 64: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 58; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 50; + break; + case 100: + case 104: + case 108: + case 112: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 106; + break; + case 116: + case 120: + case 124: + case 128: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 122; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 114; + break; + case 132: + case 136: + case 140: + case 144: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 138; + break; + case 149: + case 153: + case 157: + case 161: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 155; + break; + default: + center_freq_idx = 42; + } + } + + return center_freq_idx; +} diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h index 7c2c69b5b3e..0b02cb6cfcb 100644 --- a/drivers/net/wireless/mwifiex/11ac.h +++ b/drivers/net/wireless/mwifiex/11ac.h @@ -40,4 +40,6 @@ int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, u16 cmd_action, struct mwifiex_11ac_vht_cfg *cfg); +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands); #endif /* _MWIFIEX_11AC_H_ */ diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c index 8d683070bdb..e76b0db4e3e 100644 --- a/drivers/net/wireless/mwifiex/11h.c +++ b/drivers/net/wireless/mwifiex/11h.c @@ -73,8 +73,8 @@ static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) { u32 enable = flag; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11H_I, &enable); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); } /* This functions processes TLV buffer for a pending BSS Join command. diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 0b803c05cab..e1c2f67ae85 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -34,22 +34,26 @@ * * RD responder bit to set to clear in the extended capability header. */ -void -mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, - struct mwifiex_ie_types_htcap *ht_cap) +int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, + struct ieee80211_ht_cap *ht_cap) { - uint16_t ht_ext_cap = le16_to_cpu(ht_cap->ht_cap.extended_ht_cap_info); + uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); struct ieee80211_supported_band *sband = priv->wdev->wiphy->bands[radio_type]; - ht_cap->ht_cap.ampdu_params_info = + if (WARN_ON_ONCE(!sband)) { + dev_err(priv->adapter->dev, "Invalid radio type!\n"); + return -EINVAL; + } + + ht_cap->ampdu_params_info = (sband->ht_cap.ampdu_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) | ((sband->ht_cap.ampdu_density << IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & IEEE80211_HT_AMPDU_PARM_DENSITY); - memcpy((u8 *) &ht_cap->ht_cap.mcs, &sband->ht_cap.mcs, + memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); if (priv->bss_mode == NL80211_IFTYPE_STATION || @@ -57,13 +61,18 @@ mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, (priv->adapter->sec_chan_offset != IEEE80211_HT_PARAM_CHA_SEC_NONE))) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(ht_cap->ht_cap.mcs.rx_mask); + SETHT_MCS32(ht_cap->mcs.rx_mask); /* Clear RD responder bit */ ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; - ht_cap->ht_cap.cap_info = cpu_to_le16(sband->ht_cap.cap); - ht_cap->ht_cap.extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap); + ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); + + return 0; } /* @@ -150,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; @@ -308,11 +323,10 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, ht_cap->header.len = cpu_to_le16(sizeof(struct ieee80211_ht_cap)); memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *) bss_desc->bcn_ht_cap + - sizeof(struct ieee_types_header), + (u8 *)bss_desc->bcn_ht_cap, le16_to_cpu(ht_cap->header.len)); - mwifiex_fill_cap_info(priv, radio_type, ht_cap); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); *buffer += sizeof(struct mwifiex_ie_types_htcap); ret_len += sizeof(struct mwifiex_ie_types_htcap); @@ -331,8 +345,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, memcpy((u8 *) ht_info + sizeof(struct mwifiex_ie_types_header), - (u8 *) bss_desc->bcn_ht_oper + - sizeof(struct ieee_types_header), + (u8 *)bss_desc->bcn_ht_oper, le16_to_cpu(ht_info->header.len)); if (!(sband->ht_cap.cap & @@ -483,7 +496,7 @@ mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (!memcmp(tx_ba_tsr_tbl->ra, ra, ETH_ALEN) && + if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && tx_ba_tsr_tbl->tid == tid) { spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); @@ -528,16 +541,39 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) { struct host_cmd_ds_11n_addba_req add_ba_req; + struct mwifiex_sta_node *sta_ptr; + 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); - add_ba_req.block_ack_param_set = cpu_to_le16( - (u16) ((tid << BLOCKACKPARAM_TID_POS) | - (priv->add_ba_param. - tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | - IMMEDIATE_BLOCK_ACK)); + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { + sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); + if (!sta_ptr) { + dev_warn(priv->adapter->dev, + "BA setup with unknown TDLS peer %pM!\n", + peer_mac); + return -1; + } + if (sta_ptr->is_11ac_enabled) + tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; + } + + 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; @@ -549,8 +585,8 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); /* We don't wait for the response of this command */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_REQ, - 0, 0, &add_ba_req); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, + 0, 0, &add_ba_req, false); return ret; } @@ -577,8 +613,8 @@ int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); /* We don't wait for the response of this command */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, - HostCmd_ACT_GEN_SET, 0, &delba); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, + HostCmd_ACT_GEN_SET, 0, &delba, false); return ret; } @@ -652,6 +688,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) @@ -707,5 +744,50 @@ 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; } + +u8 mwifiex_get_sec_chan_offset(int chan) +{ + u8 sec_offset; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case 165: + default: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + + return sec_offset; +} diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index 375db01442b..0b73fa08f5d 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -34,8 +34,8 @@ int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc, u8 **buffer); -void mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, - struct mwifiex_ie_types_htcap *); +int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, + struct ieee80211_ht_cap *); int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, u16 action, int *htcap_cfg); void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, @@ -63,15 +63,48 @@ int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, int cmd_action, struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); +u8 mwifiex_get_sec_chan_offset(int chan); -/* - * This function checks whether AMPDU is allowed or not for a particular TID. - */ static inline u8 -mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, int tid) +mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) { - return ((priv->aggr_prio_tbl[tid].ampdu_ap != BA_STREAM_NOT_ALLOWED) - ? true : false); + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra); + + if (unlikely(!node)) + return false; + + 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, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + } else { + if (ptr->tdls_link) + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + + return (priv->aggr_prio_tbl[tid].ampdu_ap != + BA_STREAM_NOT_ALLOWED) ? true : false; + } } /* @@ -165,4 +198,14 @@ static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, return node->is_11n_enabled; } + +static inline u8 +mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) +{ + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); + if (node) + return node->is_11n_enabled; + + return false; +} #endif /* !_MWIFIEX_11N_H_ */ diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 1214c587fd0..fe0f66f7350 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -69,9 +69,9 @@ mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); /* Copy SNAP header */ - snap.snap_type = - le16_to_cpu(*(__le16 *) ((u8 *)skb_src->data + dt_offset)); - dt_offset += sizeof(u16); + snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; + + dt_offset += sizeof(__be16); memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); @@ -100,6 +100,7 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, struct sk_buff *skb) { struct txpd *local_tx_pd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); skb_push(skb, sizeof(*local_tx_pd)); @@ -118,6 +119,9 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - sizeof(*local_tx_pd)); + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + if (local_tx_pd->tx_control == 0) /* TxCtrl set by user or default */ local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); @@ -160,6 +164,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, int pad = 0, ret; struct mwifiex_tx_param tx_param; struct txpd *ptx_pd = NULL; + struct timeval tv; int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; skb_src = skb_peek(&pra_list->skb_head); @@ -180,10 +185,17 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); + memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); tx_info_aggr->bss_type = tx_info_src->bss_type; tx_info_aggr->bss_num = tx_info_src->bss_num; + + if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; skb_aggr->priority = skb_src->priority; + do_gettimeofday(&tv); + skb_aggr->tstamp = timeval_to_ktime(tv); + do { /* Check if AMSDU can accommodate this MSDU */ if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) @@ -236,18 +248,11 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_USB_EP_DATA, skb_aggr, NULL); } else { - /* - * Padding per MSDU will affect the length of next - * packet and hence the exact length of next packet - * is uncertain here. - * - * Also, aggregation of transmission buffer, while - * downloading the data to the card, wont gain much - * on the AMSDU packets as the AMSDU packets utilizes - * the transmission buffer space to the maximum - * (adapter->tx_buf_size). - */ - tx_param.next_pkt_len = 0; + if (skb_src) + tx_param.next_pkt_len = + skb_src->len + sizeof(struct txpd); + else + tx_param.next_pkt_len = 0; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, skb_aggr, &tx_param); diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index ada809f576f..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,14 +178,15 @@ 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(&tbl->timer_context.timer); + del_timer_sync(&tbl->timer_context.timer); spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); list_del(&tbl->list); @@ -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 */ @@ -279,6 +323,8 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->tid = tid; memcpy(new_node->ta, ta, ETH_ALEN); new_node->start_win = seq_num; + new_node->init_win = seq_num; + new_node->flags = 0; if (mwifiex_queuing_ra_based(priv)) { dev_dbg(priv->adapter->dev, @@ -290,15 +336,20 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, last_seq = node->rx_seq[tid]; } } else { - last_seq = priv->rx_seq[tid]; + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; } if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && - last_seq >= new_node->start_win) + last_seq >= new_node->start_win) { new_node->start_win = last_seq + 1; + new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; + } new_node->win_size = win_size; - new_node->flags = 0; new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, GFP_KERNEL); @@ -358,10 +409,28 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, *cmd_addba_req) { struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; + struct mwifiex_sta_node *sta_ptr; + u32 rx_win_size = priv->add_ba_param.rx_win_size; u8 tid; int win_size; uint16_t block_ack_param_set; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { + sta_ptr = mwifiex_get_sta_entry(priv, + cmd_addba_req->peer_mac_addr); + if (!sta_ptr) { + dev_warn(priv->adapter->dev, + "BA setup with unknown TDLS peer %pM!\n", + cmd_addba_req->peer_mac_addr); + return -1; + } + if (sta_ptr->is_11ac_enabled) + rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + } + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); @@ -376,10 +445,12 @@ 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; - block_ack_param_set |= (priv->add_ba_param.rx_win_size << - BLOCKACKPARAM_WINSIZE_POS); + + /* 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) & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) @@ -431,33 +502,46 @@ int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, struct mwifiex_rx_reorder_tbl *tbl; int start_win, end_win, win_size; u16 pkt_index; + bool init_window_shift = false; 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); - del_timer(&tbl->timer_context.timer); + if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { + init_window_shift = true; + tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; + } mod_timer(&tbl->timer_context.timer, jiffies + msecs_to_jiffies(MIN_FLUSH_TIMER_MS * win_size)); - /* - * If seq_num is less then starting win then ignore and drop the - * packet - */ if (tbl->flags & RXREOR_FORCE_NO_DROP) { dev_dbg(priv->adapter->dev, "RXREOR_FORCE_NO_DROP when HS is activated\n"); tbl->flags &= ~RXREOR_FORCE_NO_DROP; + } else if (init_window_shift && seq_num < start_win && + seq_num >= tbl->init_win) { + dev_dbg(priv->adapter->dev, + "Sender TID sequence number reset %d->%d for SSN %d\n", + start_win, seq_num, tbl->init_win); + tbl->start_win = start_win = seq_num; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); } else { + /* + * If seq_num is less then starting win then ignore and drop + * the packet + */ if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { if (seq_num >= ((start_win + TWOPOW11) & (MAX_TID_VALUE - 1)) && @@ -485,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) { @@ -576,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); @@ -593,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; } @@ -615,7 +710,7 @@ void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, delba.del_ba_param_set |= cpu_to_le16( (u16) event->origninator << DELBA_INITIATOR_POS); delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); - mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); } /* diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h index 4064041ac85..0fc76e4a60f 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.h +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h @@ -42,7 +42,8 @@ #define BA_SETUP_PACKET_OFFSET 16 enum mwifiex_rxreor_flags { - RXREOR_FORCE_NO_DROP = 1<<0, + RXREOR_FORCE_NO_DROP = 1<<0, + RXREOR_INIT_WINDOW_SHIFT = 1<<1, }; static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index f7ff4725506..ecdf34505b5 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -31,12 +31,12 @@ config MWIFIEX_PCIE mwifiex_pcie. config MWIFIEX_USB - tristate "Marvell WiFi-Ex Driver for USB8797" + tristate "Marvell WiFi-Ex Driver for USB8797/8897" depends on MWIFIEX && USB select FW_LOADER ---help--- This adds support for wireless adapters based on Marvell - Avastar 88W8797 chipset with USB interface. + 8797/8897 chipset with USB interface. If you choose to build it as a module, it will be called mwifiex_usb. diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index a42a506fd32..2aa208ffbe2 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -41,6 +41,7 @@ mwifiex-y += uap_txrx.o mwifiex-y += cfg80211.o mwifiex-y += ethtool.o mwifiex-y += 11h.o +mwifiex-y += tdls.o mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_MWIFIEX) += mwifiex.o diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README index 3d64613ebb2..3b55ce5690a 100644 --- a/drivers/net/wireless/mwifiex/README +++ b/drivers/net/wireless/mwifiex/README @@ -131,7 +131,7 @@ info hs_configured = <0/1, host sleep not configured/configured> hs_activated = <0/1, extended host sleep not activated/activated> num_tx_timeout = <number of Tx timeout> - num_cmd_timeout = <number of timeout commands> + is_cmd_timedout = <0/1 command timeout not occurred/occurred> timeout_cmd_id = <command id of the last timeout command> timeout_cmd_act = <command action of the last timeout command> last_cmd_id = <command id of the last several commands sent to device> @@ -200,4 +200,11 @@ getlog cat getlog +fw_dump + This command is used to dump firmware memory into files. + Separate file will be created for each memory segment. + Usage: + + cat fw_dump + =============================================================================== diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index fbad00a5abc..b511613bba2 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -50,24 +50,24 @@ static const struct ieee80211_regdomain mwifiex_world_regdom_custom = { REG_RULE(2412-10, 2462+10, 40, 3, 20, 0), /* Channel 12 - 13 */ REG_RULE(2467-10, 2472+10, 20, 3, 20, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + NL80211_RRF_NO_IR), /* Channel 14 */ REG_RULE(2484-10, 2484+10, 20, 3, 20, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_IR | NL80211_RRF_NO_OFDM), /* Channel 36 - 48 */ REG_RULE(5180-10, 5240+10, 40, 3, 20, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + NL80211_RRF_NO_IR), /* Channel 149 - 165 */ REG_RULE(5745-10, 5825+10, 40, 3, 20, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), + NL80211_RRF_NO_IR), /* Channel 52 - 64 */ REG_RULE(5260-10, 5320+10, 40, 3, 30, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_IR | NL80211_RRF_DFS), /* Channel 100 - 140 */ REG_RULE(5500-10, 5700+10, 40, 3, 30, - NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_IR | NL80211_RRF_DFS), } }; @@ -184,10 +184,10 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) */ static int mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct ieee80211_channel *chan, bool offchan, - unsigned int wait, const u8 *buf, size_t len, - bool no_cck, bool dont_wait_for_ack, u64 *cookie) + struct cfg80211_mgmt_tx_params *params, u64 *cookie) { + const u8 *buf = params->buf; + size_t len = params->len; struct sk_buff *skb; u16 pkt_len; const struct ieee80211_mgmt *mgmt; @@ -220,8 +220,10 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = pkt_len; mwifiex_form_mgmt_frame(skb, buf, len); mwifiex_queue_tx_pkt(priv, skb); @@ -251,9 +253,9 @@ mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, if (mask != priv->mgmt_frame_mask) { priv->mgmt_frame_mask = mask; - mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG, - HostCmd_ACT_GEN_SET, 0, - &priv->mgmt_frame_mask); + mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); wiphy_dbg(wiphy, "info: mgmt frame registered\n"); } } @@ -514,8 +516,8 @@ static int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { wiphy_err(wiphy, "11D: setting domain info in FW\n"); return -1; } @@ -537,23 +539,33 @@ static void mwifiex_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n", request->alpha2[0], request->alpha2[1]); - memcpy(adapter->country_code, request->alpha2, sizeof(request->alpha2)); - switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: - break; - /* Todo: apply driver specific changes in channel flags based - on the request initiator if necessary. */ case NL80211_REGDOM_SET_BY_COUNTRY_IE: break; + default: + wiphy_err(wiphy, "unknown regdom initiator: %d\n", + request->initiator); + return; + } + + /* Don't send world or same regdom info to firmware */ + if (strncmp(request->alpha2, "00", 2) && + strncmp(request->alpha2, adapter->country_code, + sizeof(request->alpha2))) { + memcpy(adapter->country_code, request->alpha2, + sizeof(request->alpha2)); + mwifiex_send_domain_info_cmd_fw(wiphy); + mwifiex_dnld_txpwr_table(priv); } - mwifiex_send_domain_info_cmd_fw(wiphy); } /* @@ -569,9 +581,9 @@ mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) frag_thr > MWIFIEX_FRAG_MAX_VALUE) frag_thr = MWIFIEX_FRAG_MAX_VALUE; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, FRAG_THRESH_I, - &frag_thr); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, FRAG_THRESH_I, + &frag_thr, true); } /* @@ -586,9 +598,9 @@ mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) rts_thr = MWIFIEX_RTS_MAX_VALUE; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, RTS_THRESH_I, - &rts_thr); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, RTS_THRESH_I, + &rts_thr, true); } /* @@ -626,20 +638,19 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) bss_started = priv->bss_started; - ret = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, - NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, + NULL, true); if (ret) { wiphy_err(wiphy, "Failed to stop the BSS\n"); kfree(bss_cfg); return ret; } - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, + false); kfree(bss_cfg); @@ -651,10 +662,9 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) if (!bss_started) break; - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, - NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, + NULL, false); if (ret) { wiphy_err(wiphy, "Failed to start BSS\n"); return ret; @@ -689,8 +699,8 @@ mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_STA); - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; return 0; @@ -710,13 +720,13 @@ mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) return -1; mode = P2P_MODE_DEVICE; - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; mode = P2P_MODE_CLIENT; - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; return 0; @@ -736,13 +746,13 @@ mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) return -1; mode = P2P_MODE_DEVICE; - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; mode = P2P_MODE_GO; - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) @@ -842,8 +852,8 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true); return ret; } @@ -931,8 +941,8 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; /* Get signal information from the firmware */ - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, true)) { dev_err(priv->adapter->dev, "failed to get signal information\n"); return -EFAULT; } @@ -943,9 +953,9 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, } /* Get DTIM period information from firmware */ - mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, - &priv->dtim_period); + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, + &priv->dtim_period, true); mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate); @@ -985,7 +995,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, */ static int mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); @@ -1149,9 +1159,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; } @@ -1170,13 +1181,20 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, else bitmap_rates[1] = mask->control[band].legacy; - /* Fill MCS rates */ - bitmap_rates[2] = mask->control[band].mcs[0]; - if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[2] |= mask->control[band].mcs[1] << 8; + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; + 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_sync(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_SET, 0, bitmap_rates); + return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); } /* @@ -1205,14 +1223,14 @@ static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; - return mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt); + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); } else { subsc_evt.action = HostCmd_ACT_BITWISE_CLR; - return mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt); + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); } return 0; @@ -1253,7 +1271,7 @@ static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, */ static int mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac) + const u8 *mac) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct mwifiex_sta_node *sta_node; @@ -1265,10 +1283,9 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, if (!mac || is_broadcast_ether_addr(mac)) { wiphy_dbg(wiphy, "%s: NULL/broadcast mac address\n", __func__); list_for_each_entry(sta_node, &priv->sta_list, list) { - if (mwifiex_send_cmd_sync(priv, - HostCmd_CMD_UAP_STA_DEAUTH, - HostCmd_ACT_GEN_SET, 0, - sta_node->mac_addr)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr, true)) return -1; mwifiex_uap_del_sta_data(priv, sta_node); } @@ -1278,10 +1295,9 @@ mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, sta_node = mwifiex_get_sta_entry(priv, mac); spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); if (sta_node) { - if (mwifiex_send_cmd_sync(priv, - HostCmd_CMD_UAP_STA_DEAUTH, - HostCmd_ACT_GEN_SET, 0, - sta_node->mac_addr)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + sta_node->mac_addr, true)) return -1; mwifiex_uap_del_sta_data(priv, sta_node); } @@ -1317,13 +1333,40 @@ mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) tx_ant = RF_ANTENNA_AUTO; rx_ant = RF_ANTENNA_AUTO; } + } else { + struct ieee80211_sta_ht_cap *ht_info; + int rx_mcs_supp; + enum ieee80211_band band; + + if ((tx_ant == 0x1 && rx_ant == 0x1)) { + adapter->user_dev_mcs_support = HT_STREAM_1X1; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_1X1; + } else { + adapter->user_dev_mcs_support = HT_STREAM_2X2; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_2X2; + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!adapter->wiphy->bands[band]) + continue; + + ht_info = &adapter->wiphy->bands[band]->ht_cap; + rx_mcs_supp = + GET_RXMCSSUPP(adapter->user_dev_mcs_support); + memset(&ht_info->mcs, 0, adapter->number_of_antenna); + memset(&ht_info->mcs, 0xff, rx_mcs_supp); + } } ant_cfg.tx_ant = tx_ant; ant_cfg.rx_ant = rx_ant; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_RF_ANTENNA, - HostCmd_ACT_GEN_SET, 0, &ant_cfg); + return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA, + HostCmd_ACT_GEN_SET, 0, &ant_cfg, true); } /* cfg80211 operation handler for stop ap. @@ -1338,8 +1381,8 @@ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) priv->ap_11n_enabled = 0; - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { wiphy_err(wiphy, "Failed to stop the BSS\n"); return -1; } @@ -1405,9 +1448,6 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT) config_bands |= BAND_GN; - - if (params->chandef.width > NL80211_CHAN_WIDTH_40) - config_bands |= BAND_GAC; } else { bss_cfg->band_cfg = BAND_CONFIG_A; config_bands = BAND_A; @@ -1453,16 +1493,16 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; } - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { wiphy_err(wiphy, "Failed to stop the BSS\n"); kfree(bss_cfg); return -1; } - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, false)) { wiphy_err(wiphy, "Failed to set the SSID\n"); kfree(bss_cfg); return -1; @@ -1470,8 +1510,8 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, kfree(bss_cfg); - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, NULL)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { wiphy_err(wiphy, "Failed to start the BSS\n"); return -1; } @@ -1481,9 +1521,9 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, else priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true)) return -1; return 0; @@ -1572,8 +1612,9 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) * the function notifies the CFG802.11 subsystem of the new BSS connection. */ static int -mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, u8 *ssid, - u8 *bssid, int mode, struct ieee80211_channel *channel, +mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, + const u8 *ssid, const u8 *bssid, int mode, + struct ieee80211_channel *channel, struct cfg80211_connect_params *sme, bool privacy) { struct cfg80211_ssid req_ssid; @@ -1870,7 +1911,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, params->privacy); done: if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); dev_dbg(priv->adapter->dev, "info: joined/created adhoc network with bssid" " %pM successfully\n", priv->cfg_bssid); @@ -1968,7 +2010,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, user_scan_cfg->chan_list[i].chan_number = chan->hw_value; user_scan_cfg->chan_list[i].radio_type = chan->band; - if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) + if (chan->flags & IEEE80211_CHAN_NO_IR) user_scan_cfg->chan_list[i].scan_type = MWIFIEX_SCAN_TYPE_PASSIVE; else @@ -2059,10 +2101,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; @@ -2087,8 +2129,8 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; ht_info->cap |= IEEE80211_HT_CAP_SM_PS; - rx_mcs_supp = GET_RXMCSSUPP(adapter->hw_dev_mcs_support); - /* Set MCS for 1x1 */ + rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ memset(mcs, 0xff, rx_mcs_supp); /* Clear all the other values */ memset(&mcs[rx_mcs_supp], 0, @@ -2210,8 +2252,10 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_started = 0; priv->bss_num = 0; - if (mwifiex_cfg80211_init_p2p_client(priv)) - return ERR_PTR(-EFAULT); + if (mwifiex_cfg80211_init_p2p_client(priv)) { + wdev = ERR_PTR(-EFAULT); + goto done; + } break; default: @@ -2224,7 +2268,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); + wdev = ERR_PTR(-ENOMEM); + goto done; } mwifiex_init_priv_params(priv, dev); @@ -2264,7 +2309,9 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, wiphy_err(wiphy, "cannot register virtual network device\n"); free_netdev(dev); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-EFAULT); + priv->netdev = NULL; + wdev = ERR_PTR(-EFAULT); + goto done; } sema_init(&priv->async_sem, 1); @@ -2274,6 +2321,13 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, #ifdef CONFIG_DEBUG_FS mwifiex_dev_debugfs_init(priv); #endif + +done: + if (IS_ERR(wdev)) { + kfree(priv->wdev); + priv->wdev = NULL; + } + return wdev; } EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); @@ -2298,7 +2352,10 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) unregister_netdevice(wdev->netdev); /* Clear the priv in adapter */ + priv->netdev->ieee80211_ptr = NULL; priv->netdev = NULL; + kfree(wdev); + priv->wdev = NULL; priv->media_connected = false; @@ -2423,7 +2480,7 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, ETH_ALEN); mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = ETH_ALEN; - mef_entry->filter[filt_num].offset = 14; + mef_entry->filter[filt_num].offset = 28; mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (filt_num) mef_entry->filter[filt_num].filt_action = TYPE_OR; @@ -2434,9 +2491,8 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, MWIFIEX_CRITERIA_UNICAST | MWIFIEX_CRITERIA_MULTICAST; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MEF_CFG, - HostCmd_ACT_GEN_SET, 0, - &mef_cfg); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, + HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); kfree(mef_entry); return ret; @@ -2548,9 +2604,9 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, if (!coalesce) { dev_dbg(adapter->dev, "Disable coalesce and reset all previous rules\n"); - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG, - HostCmd_ACT_GEN_SET, 0, - &coalesce_cfg); + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, + &coalesce_cfg, true); } coalesce_cfg.num_of_rules = coalesce->n_rules; @@ -2565,8 +2621,171 @@ static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, } } - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG, - HostCmd_ACT_GEN_SET, 0, &coalesce_cfg); + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true); +} + +/* cfg80211 ops handler for tdls_mgmt. + * Function prepares TDLS action frame packets and forwards them to FW + */ +static int +mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + const u8 *extra_ies, size_t extra_ies_len) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + dev_dbg(priv->adapter->dev, + "Send TDLS Setup Request to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + dev_dbg(priv->adapter->dev, + "Send TDLS Setup Response to %pM status_code=%d\n", + peer, status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + dev_dbg(priv->adapter->dev, + "Send TDLS Confirm to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_TEARDOWN: + dev_dbg(priv->adapter->dev, "Send TDLS Tear down to %pM\n", + peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + dev_dbg(priv->adapter->dev, + "Send TDLS Discovery Request to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + dev_dbg(priv->adapter->dev, + "Send TDLS Discovery Response to %pM\n", peer); + ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + default: + dev_warn(priv->adapter->dev, + "Unknown TDLS mgmt/action frame %pM\n", peer); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, enum nl80211_tdls_operation action) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + dev_dbg(priv->adapter->dev, + "TDLS peer=%pM, oper=%d\n", peer, action); + + switch (action) { + case NL80211_TDLS_ENABLE_LINK: + action = MWIFIEX_TDLS_ENABLE_LINK; + break; + case NL80211_TDLS_DISABLE_LINK: + action = MWIFIEX_TDLS_DISABLE_LINK; + break; + case NL80211_TDLS_TEARDOWN: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: teardown from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_SETUP: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: setup from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_DISCOVERY_REQ: + /* shouldn't happen!*/ + dev_warn(priv->adapter->dev, + "tdls_oper: discovery from driver not supported\n"); + return -EINVAL; + default: + dev_err(priv->adapter->dev, + "tdls_oper: operation not supported\n"); + return -ENOTSUPP; + } + + return mwifiex_tdls_oper(priv, peer, action); +} + +static int +mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); +} + +static int +mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) +{ + int ret; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + /* we support change_station handler only for TDLS peers*/ + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + priv->sta_params = params; + + ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); + priv->sta_params = NULL; + + return ret; } /* station cfg80211 operations */ @@ -2604,6 +2823,10 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .set_wakeup = mwifiex_cfg80211_set_wakeup, #endif .set_coalesce = mwifiex_cfg80211_set_coalesce, + .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, + .tdls_oper = mwifiex_cfg80211_tdls_oper, + .add_station = mwifiex_cfg80211_add_station, + .change_station = mwifiex_cfg80211_change_station, }; #ifdef CONFIG_PM @@ -2651,6 +2874,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) struct wiphy *wiphy; struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; u8 *country_code; + u32 thr, retry; /* create a new wiphy for use with cfg80211 */ wiphy = wiphy_new(&mwifiex_cfg80211_ops, @@ -2687,10 +2911,16 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_STRICT_REGULATORY | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + + wiphy->regulatory_flags |= + REGULATORY_CUSTOM_REG | + REGULATORY_STRICT_REG; + wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom); #ifdef CONFIG_PM @@ -2708,7 +2938,8 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->features |= NL80211_FEATURE_HT_IBSS | NL80211_FEATURE_INACTIVITY_TIMER | - NL80211_FEATURE_LOW_PRIORITY_SCAN; + NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_NEED_OBSS_SCAN; /* Reserve space for mwifiex specific private data for BSS */ wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); @@ -2739,6 +2970,19 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) country_code); } + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); + wiphy->frag_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true); + wiphy->rts_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); + wiphy->retry_short = (u8) retry; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); + wiphy->retry_long = (u8) retry; + adapter->wiphy = wiphy; return ret; } diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index 9eefacbc844..0ddec3d4b05 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -71,6 +71,95 @@ u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; +/* For every mcs_rate line, the first 8 bytes are for stream 1x1, + * and all 16 bytes are for stream 2x2. + */ +static const u16 mcs_rate[4][16] = { + /* LGI 40M */ + { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, + 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, + + /* SGI 40M */ + { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, + 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, + + /* LGI 20M */ + { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, + 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, + + /* SGI 20M */ + { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, + 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } +}; + +/* AC rates */ +static const u16 ac_mcs_rate_nss1[8][10] = { + /* LG 160M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 160M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 80M */ + { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, + 0x249, 0x2BE, 0x30C }, + + /* SG 80M */ + { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, + 0x28A, 0x30C, 0x363 }, + + /* LG 40M */ + { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, + 0x10E, 0x144, 0x168 }, + + /* SG 40M */ + { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, + 0x12C, 0x168, 0x190 }, + + /* LG 20M */ + { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, + + /* SG 20M */ + { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, +}; + +/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ +static const u16 ac_mcs_rate_nss2[8][10] = { + /* LG 160M */ + { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, + 0x924, 0xAF8, 0xC30 }, + + /* SG 160M */ + { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, + 0xA28, 0xC30, 0xD8B }, + + /* LG 80M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 80M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 40M */ + { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, + 0x21C, 0x288, 0x2D0 }, + + /* SG 40M */ + { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, + 0x258, 0x2D0, 0x320 }, + + /* LG 20M */ + { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, + 0x138, 0x00 }, + + /* SG 20M */ + { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, + 0x15B, 0x00 }, +}; + struct region_code_mapping { u8 code; u8 region[IEEE80211_COUNTRY_STRING_LEN]; @@ -109,95 +198,6 @@ u8 *mwifiex_11d_code_2_region(u8 code) u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { - /* - * For every mcs_rate line, the first 8 bytes are for stream 1x1, - * and all 16 bytes are for stream 2x2. - */ - u16 mcs_rate[4][16] = { - /* LGI 40M */ - { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, - 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, - - /* SGI 40M */ - { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, - 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, - - /* LGI 20M */ - { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, - 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, - - /* SGI 20M */ - { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, - 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } - }; - /* AC rates */ - u16 ac_mcs_rate_nss1[8][10] = { - /* LG 160M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 160M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 80M */ - { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, - 0x249, 0x2BE, 0x30C }, - - /* SG 80M */ - { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, - 0x28A, 0x30C, 0x363 }, - - /* LG 40M */ - { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, - 0x10E, 0x144, 0x168 }, - - /* SG 40M */ - { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, - 0x12C, 0x168, 0x190 }, - - /* LG 20M */ - { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, - - /* SG 20M */ - { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, - }; - /* NSS2 note: the value in the table is 2 multiplier of the actual - * rate - */ - u16 ac_mcs_rate_nss2[8][10] = { - /* LG 160M */ - { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, - 0x924, 0xAF8, 0xC30 }, - - /* SG 160M */ - { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, - 0xA28, 0xC30, 0xD8B }, - - /* LG 80M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 80M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 40M */ - { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, - 0x21C, 0x288, 0x2D0 }, - - /* SG 40M */ - { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, - 0x258, 0x2D0, 0x320 }, - - /* LG 20M */ - { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, - 0x138, 0x00 }, - - /* SG 20M */ - { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, - 0x15B, 0x00 }, - }; u32 rate = 0; u8 mcs_index = 0; u8 bw = 0; @@ -252,28 +252,8 @@ u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, u8 index, u8 ht_info) { - /* For every mcs_rate line, the first 8 bytes are for stream 1x1, - * and all 16 bytes are for stream 2x2. - */ - u16 mcs_rate[4][16] = { - /* LGI 40M */ - { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, - 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, - - /* SGI 40M */ - { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, - 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, - - /* LGI 20M */ - { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, - 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, - - /* SGI 20M */ - { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, - 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } - }; u32 mcs_num_supp = - (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; + (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; u32 rate; if (priv->adapter->is_hw_11ac_capable) @@ -458,7 +438,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) break; case BAND_G: case BAND_G | BAND_GN: - case BAND_G | BAND_GN | BAND_GAC: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_g\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_g, @@ -469,10 +448,7 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) case BAND_A | BAND_B: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN: case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC: - case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | - BAND_AAC | BAND_GAC: case BAND_B | BAND_G | BAND_GN: - case BAND_B | BAND_G | BAND_GN | BAND_GAC: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_bg\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_bg, @@ -496,7 +472,6 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) sizeof(supported_rates_a)); break; case BAND_GN: - case BAND_GN | BAND_GAC: dev_dbg(adapter->dev, "info: infra band=%d " "supported_rates_n\n", adapter->config_bands); k = mwifiex_copy_rates(rates, k, supported_rates_n, diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index a6c46f3b6e3..c161141f6c3 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -37,13 +37,12 @@ static void mwifiex_init_cmd_node(struct mwifiex_private *priv, struct cmd_ctrl_node *cmd_node, - u32 cmd_oid, void *data_buf) + u32 cmd_oid, void *data_buf, bool sync) { cmd_node->priv = priv; cmd_node->cmd_oid = cmd_oid; - if (priv->adapter->cmd_wait_q_required) { - cmd_node->wait_q_enabled = priv->adapter->cmd_wait_q_required; - priv->adapter->cmd_wait_q_required = false; + if (sync) { + cmd_node->wait_q_enabled = true; cmd_node->cmd_wait_q_woken = false; cmd_node->condition = &cmd_node->cmd_wait_q_woken; } @@ -166,8 +165,10 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, dev_err(adapter->dev, "DNLD_CMD: FW in reset state, ignore cmd %#x\n", cmd_code); - mwifiex_complete_cmd(adapter, cmd_node); + if (cmd_node->wait_q_enabled) + mwifiex_complete_cmd(adapter, cmd_node); mwifiex_recycle_cmd_node(adapter, cmd_node); + queue_work(adapter->workqueue, &adapter->main_work); return -1; } @@ -276,11 +277,11 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + adapter->seq_num++; sleep_cfm_buf->seq_num = cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO (adapter->seq_num, priv->bss_num, priv->bss_type))); - adapter->seq_num++; if (adapter->iface_type == MWIFIEX_USB) { sleep_cfm_tmp = @@ -312,14 +313,14 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) } if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY)) == MWIFIEX_BSS_ROLE_STA) { - if (!sleep_cfm_buf->resp_ctrl) + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) /* Response is not needed for sleep confirm command */ adapter->ps_state = PS_STATE_SLEEP; else adapter->ps_state = PS_STATE_SLEEP_CFM; - if (!sleep_cfm_buf->resp_ctrl && + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && (adapter->is_hs_configured && !adapter->sleep_period.period)) { adapter->pm_wakeup_card_req = true; @@ -452,6 +453,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) if (skb) { rx_info = MWIFIEX_SKB_RXCB(skb); + memset(rx_info, 0, sizeof(*rx_info)); rx_info->bss_num = priv->bss_num; rx_info->bss_type = priv->bss_type; } @@ -480,28 +482,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) } /* - * This function is used to send synchronous command to the firmware. - * - * it allocates a wait queue for the command and wait for the command - * response. - */ -int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - - adapter->cmd_wait_q_required = true; - - ret = mwifiex_send_cmd_async(priv, cmd_no, cmd_action, cmd_oid, - data_buf); - - return ret; -} - - -/* - * This function prepares a command and asynchronously send it to the firmware. + * This function prepares a command and send it to the firmware. * * Preparation includes - * - Sanity tests to make sure the card is still present or the FW @@ -511,8 +492,8 @@ int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, * - Fill up the non-default parameters and buffer pointers * - Add the command to pending queue */ -int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf) +int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync) { int ret; struct mwifiex_adapter *adapter = priv->adapter; @@ -529,11 +510,21 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, return -1; } + if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) { + dev_err(adapter->dev, "PREP_CMD: host entering sleep state\n"); + return -1; + } + if (adapter->surprise_removed) { dev_err(adapter->dev, "PREP_CMD: card is removed\n"); return -1; } + if (adapter->is_cmd_timedout) { + dev_err(adapter->dev, "PREP_CMD: FW is in bad state\n"); + return -1; + } + if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { if (cmd_no != HostCmd_CMD_FUNC_INIT) { dev_err(adapter->dev, "PREP_CMD: FW in reset state\n"); @@ -550,7 +541,7 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, } /* Initialize the command node */ - mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf); + mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync); if (!cmd_node->cmd_skb) { dev_err(adapter->dev, "PREP_CMD: no free cmd buf\n"); @@ -595,7 +586,8 @@ int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, } /* Send command */ - if (cmd_no == HostCmd_CMD_802_11_SCAN) { + if (cmd_no == HostCmd_CMD_802_11_SCAN || + cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { mwifiex_queue_scan_cmd(priv, cmd_node); } else { mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); @@ -785,7 +777,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) unsigned long flags; /* Now we got response from FW, cancel the command timer */ - del_timer(&adapter->cmd_timer); + del_timer_sync(&adapter->cmd_timer); if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { resp = (struct host_cmd_ds_command *) adapter->upld_buf; @@ -794,7 +786,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) return -1; } - adapter->num_cmd_timeout = 0; + adapter->is_cmd_timedout = 0; resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; if (adapter->curr_cmd->cmd_flag & CMD_F_CANCELED) { @@ -905,8 +897,7 @@ mwifiex_cmd_timeout_func(unsigned long function_context) struct cmd_ctrl_node *cmd_node; struct timeval tstamp; - adapter->num_cmd_timeout++; - adapter->dbg.num_cmd_timeout++; + adapter->is_cmd_timedout = 1; if (!adapter->curr_cmd) { dev_dbg(adapter->dev, "cmd: empty curr_cmd\n"); return; @@ -929,8 +920,8 @@ mwifiex_cmd_timeout_func(unsigned long function_context) dev_err(adapter->dev, "num_cmd_h2c_failure = %d\n", adapter->dbg.num_cmd_host_to_card_failure); - dev_err(adapter->dev, "num_cmd_timeout = %d\n", - adapter->dbg.num_cmd_timeout); + dev_err(adapter->dev, "is_cmd_timedout = %d\n", + adapter->is_cmd_timedout); dev_err(adapter->dev, "num_tx_timeout = %d\n", adapter->dbg.num_tx_timeout); @@ -965,8 +956,6 @@ mwifiex_cmd_timeout_func(unsigned long function_context) adapter->cmd_wait_q.status = -ETIMEDOUT; wake_up_interruptible(&adapter->cmd_wait_q.wait); mwifiex_cancel_pending_ioctl(adapter); - /* reset cmd_sent flag to unblock new commands */ - adapter->cmd_sent = false; } } if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) @@ -987,13 +976,14 @@ void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) { struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - unsigned long flags; + unsigned long flags, cmd_flags; + struct mwifiex_private *priv; + int i; + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); /* Cancel current cmd */ if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->curr_cmd->wait_q_enabled = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); adapter->cmd_wait_q.status = -1; mwifiex_complete_cmd(adapter, adapter->curr_cmd); } @@ -1013,6 +1003,7 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); } spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); /* Cancel all pending scan command */ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); @@ -1027,9 +1018,21 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + if (adapter->scan_processing) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } + } + } } /* @@ -1048,7 +1051,8 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; unsigned long cmd_flags; unsigned long scan_pending_q_flags; - uint16_t cancel_scan_cmd = false; + struct mwifiex_private *priv; + int i; if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { @@ -1074,15 +1078,24 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) mwifiex_insert_cmd_to_free_q(adapter, cmd_node); spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_pending_q_flags); - cancel_scan_cmd = true; } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_pending_q_flags); - if (cancel_scan_cmd) { + if (adapter->scan_processing) { spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); adapter->scan_processing = false; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } + } } adapter->cmd_wait_q.status = -1; } @@ -1454,7 +1467,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, { struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; struct mwifiex_adapter *adapter = priv->adapter; - int i; + struct mwifiex_ie_types_header *tlv; + struct hw_spec_fw_api_rev *api_rev; + u16 resp_size, api_id; + int i, left_len, parsed_len = 0; adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); @@ -1490,6 +1506,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)) { @@ -1498,8 +1515,10 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, /* Copy 11AC cap */ adapter->hw_dot_11ac_dev_cap = le32_to_cpu(hw_spec->dot_11ac_dev_cap); - adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap; - adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap; + adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; + adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; /* Copy 11AC mcs */ adapter->hw_dot_11ac_mcs_support = @@ -1510,6 +1529,46 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, adapter->is_hw_11ac_capable = false; } + resp_size = le16_to_cpu(resp->size) - S_DS_GEN; + if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { + /* we have variable HW SPEC information */ + left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); + while (left_len > sizeof(struct mwifiex_ie_types_header)) { + tlv = (void *)&hw_spec->tlvs + parsed_len; + switch (le16_to_cpu(tlv->type)) { + case TLV_TYPE_FW_API_REV: + api_rev = (struct hw_spec_fw_api_rev *)tlv; + api_id = le16_to_cpu(api_rev->api_id); + switch (api_id) { + case KEY_API_VER_ID: + adapter->fw_key_api_major_ver = + api_rev->major_ver; + adapter->fw_key_api_minor_ver = + api_rev->minor_ver; + dev_dbg(adapter->dev, + "fw_key_api v%d.%d\n", + adapter->fw_key_api_major_ver, + adapter->fw_key_api_minor_ver); + break; + default: + dev_warn(adapter->dev, + "Unknown FW api_id: %d\n", + api_id); + break; + } + break; + default: + dev_warn(adapter->dev, + "Unknown GET_HW_SPEC TLV type: %#x\n", + le16_to_cpu(tlv->type)); + break; + } + parsed_len += le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); + left_len -= parsed_len; + } + } + dev_dbg(adapter->dev, "info: GET_HW_SPEC: fw_release_number- %#x\n", adapter->fw_release_number); dev_dbg(adapter->dev, "info: GET_HW_SPEC: permanent addr: %pM\n", @@ -1538,6 +1597,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; + adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; if (adapter->if_ops.update_mp_end_port) adapter->if_ops.update_mp_end_port(adapter, diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index a5f9875cfd6..7b419bbcd54 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -85,8 +85,8 @@ static struct mwifiex_debug_data items[] = { item_addr(hs_activated), 1}, {"num_tx_timeout", item_size(num_tx_timeout), item_addr(num_tx_timeout), 1}, - {"num_cmd_timeout", item_size(num_cmd_timeout), - item_addr(num_cmd_timeout), 1}, + {"is_cmd_timedout", item_size(is_cmd_timedout), + item_addr(is_cmd_timedout), 1}, {"timeout_cmd_id", item_size(timeout_cmd_id), item_addr(timeout_cmd_id), 1}, {"timeout_cmd_act", item_size(timeout_cmd_act), @@ -257,6 +257,29 @@ free_and_exit: } /* + * Proc firmware dump read handler. + * + * This function is called when the 'fw_dump' file is opened for + * reading. + * This function dumps firmware memory in different files + * (ex. DTCM, ITCM, SQRAM etc.) based on the the segments for + * debugging. + */ +static ssize_t +mwifiex_fw_dump_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = file->private_data; + + if (!priv->adapter->if_ops.fw_dump) + return -EIO; + + priv->adapter->if_ops.fw_dump(priv->adapter); + + return 0; +} + +/* * Proc getlog file read handler. * * This function is called when the 'getlog' file is opened for reading @@ -493,7 +516,7 @@ mwifiex_regrdwr_write(struct file *file, { unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; - size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1)); + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); int ret; u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; @@ -594,7 +617,7 @@ mwifiex_rdeeprom_write(struct file *file, { unsigned long addr = get_zeroed_page(GFP_KERNEL); char *buf = (char *) addr; - size_t buf_size = min(count, (size_t) (PAGE_SIZE - 1)); + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); int ret = 0; int offset = -1, bytes = -1; @@ -699,6 +722,7 @@ static const struct file_operations mwifiex_dfs_##name##_fops = { \ MWIFIEX_DFS_FILE_READ_OPS(info); MWIFIEX_DFS_FILE_READ_OPS(debug); MWIFIEX_DFS_FILE_READ_OPS(getlog); +MWIFIEX_DFS_FILE_READ_OPS(fw_dump); MWIFIEX_DFS_FILE_OPS(regrdwr); MWIFIEX_DFS_FILE_OPS(rdeeprom); @@ -722,6 +746,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(getlog); MWIFIEX_DFS_ADD_FILE(regrdwr); MWIFIEX_DFS_ADD_FILE(rdeeprom); + MWIFIEX_DFS_ADD_FILE(fw_dump); } /* diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 5c85d7803d0..38da6ff6f41 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -42,12 +42,12 @@ #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 -#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 16 -#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 32 +#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 #define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32 #define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16 -#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 32 -#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 48 +#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64 #define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 48 #define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 32 @@ -75,10 +75,16 @@ #define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) #define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) +#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) #define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 #define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 +#define MWIFIEX_TDLS_DISABLE_LINK 0x00 +#define MWIFIEX_TDLS_ENABLE_LINK 0x01 +#define MWIFIEX_TDLS_CREATE_LINK 0x02 +#define MWIFIEX_TDLS_CONFIG_LINK 0x03 + enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, @@ -92,6 +98,23 @@ enum mwifiex_bss_role { MWIFIEX_BSS_ROLE_ANY = 0xff, }; +enum mwifiex_tdls_status { + TDLS_NOT_SETUP = 0, + TDLS_SETUP_INPROGRESS, + TDLS_SETUP_COMPLETE, + TDLS_SETUP_FAILURE, + TDLS_LINK_TEARDOWN, +}; + +enum mwifiex_tdls_error_code { + TDLS_ERR_NO_ERROR = 0, + TDLS_ERR_INTERNAL_ERROR, + TDLS_ERR_MAX_LINKS_EST, + TDLS_ERR_LINK_EXISTS, + TDLS_ERR_LINK_NONEXISTENT, + TDLS_ERR_PEER_STA_UNREACHABLE = 25, +}; + #define BSS_ROLE_BIT_MASK BIT(0) #define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) @@ -130,6 +153,7 @@ struct mwifiex_txinfo { u8 flags; u8 bss_num; u8 bss_type; + u32 pkt_len; }; enum mwifiex_wmm_ac_e { diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index f80f30b6160..3175dd04834 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -30,7 +30,7 @@ struct rfc_1042_hdr { u8 llc_ssap; u8 llc_ctrl; u8 snap_oui[3]; - u16 snap_type; + __be16 snap_type; }; struct rx_packet_hdr { @@ -50,21 +50,23 @@ struct tx_packet_hdr { #define HOSTCMD_SUPPORTED_RATES 14 #define N_SUPPORTED_RATES 3 #define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \ - BAND_AN | BAND_GAC | BAND_AAC) + BAND_AN | BAND_AAC) #define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \ - BIT(12) | BIT(13)) + BIT(13)) #define IS_SUPPORT_MULTI_BANDS(adapter) \ (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) -/* shift bit 12 and bit 13 in fw_cap_info from the firmware to bit 13 and 14 - * for 11ac so that bit 11 is for GN, bit 12 for AN, bit 13 for GAC, and bit - * bit 14 for AAC, in order to be compatible with the band capability - * defined in the driver after right shift of 8 bits. +/* bit 13: 11ac BAND_AAC + * bit 12: reserved for lab testing, will be reused for BAND_AN + * bit 11: 11n BAND_GN + * bit 10: 11a BAND_A + * bit 9: 11g BAND_G + * bit 8: 11b BAND_B + * Map these bits to band capability by right shifting 8 bits. */ #define GET_FW_DEFAULT_BANDS(adapter) \ - (((((adapter->fw_cap_info & 0x3000) << 1) | \ - (adapter->fw_cap_info & ~0xF000)) >> 8) & \ + (((adapter->fw_cap_info & 0x2f00) >> 8) & \ ALL_802_11_BANDS) #define HostCmd_WEP_KEY_INDEX_MASK 0x3fff @@ -77,12 +79,21 @@ enum KEY_TYPE_ID { KEY_TYPE_ID_WAPI, KEY_TYPE_ID_AES_CMAC, }; + +#define WPA_PN_SIZE 8 +#define KEY_PARAMS_FIXED_LEN 10 +#define KEY_INDEX_MASK 0xf +#define FW_KEY_API_VER_MAJOR_V2 2 + #define KEY_MCAST BIT(0) #define KEY_UNICAST BIT(1) #define KEY_ENABLED BIT(2) +#define KEY_DEFAULT BIT(3) +#define KEY_TX_KEY BIT(4) +#define KEY_RX_KEY BIT(5) #define KEY_IGTK BIT(10) -#define WAPI_KEY_LEN 50 +#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) #define MAX_POLL_TRIES 100 #define MAX_FIRMWARE_POLL_TRIES 100 @@ -130,6 +141,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) #define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) #define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) #define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) #define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) #define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) @@ -144,6 +156,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) #define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) #define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) +#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) +#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) #define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) #define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) #define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) @@ -154,6 +168,9 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) #define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) #define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) +#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) +#define TLV_TYPE_FW_API_REV (PROPRIETARY_TLV_BASE_ID + 199) #define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 @@ -176,13 +193,21 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 #define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) +#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) #define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ IEEE80211_HT_CAP_SM_PS) +#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008 + #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 @@ -204,6 +229,8 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) #define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) +#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) +#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) /* httxcfg bitmap * 0 reserved @@ -216,8 +243,21 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { */ #define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) +/* 11AC Tx and Rx MCS map for 1x1 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams + */ +#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe + +/* 11AC Tx and Rx MCS map for 2x2 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams + */ +#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa + #define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) #define SETHT_MCS32(x) (x[4] |= 1) +#define HT_STREAM_1X1 0x11 #define HT_STREAM_2X2 0x22 #define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) @@ -226,17 +266,24 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { /* HW_SPEC fw_cap_info */ -#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & (BIT(13)|BIT(14))) +#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13)) #define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3) #define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3) #define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \ (2 * (nss - 1))) -#define NO_NSS_SUPPORT 0x3 - #define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) #define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) +/* Clear SU Beanformer, MU beanformer, MU beanformee and + * sounding dimensions bits + */ +#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \ + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) + #define MOD_CLASS_HR_DSSS 0x03 #define MOD_CLASS_OFDM 0x07 #define MOD_CLASS_HT 0x08 @@ -295,10 +342,12 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_CAU_REG_ACCESS 0x00ed #define HostCmd_CMD_SET_BSS_MODE 0x00f7 #define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa +#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 #define HostCmd_CMD_COALESCE_CFG 0x010a #define HostCmd_CMD_MGMT_FRAME_REG 0x010c #define HostCmd_CMD_REMAIN_ON_CHAN 0x010d #define HostCmd_CMD_11AC_CFG 0x0112 +#define HostCmd_CMD_TDLS_OPER 0x0122 #define PROTOCOL_NO_SECURITY 0x01 #define PROTOCOL_STATIC_WEP 0x02 @@ -356,7 +405,7 @@ enum P2P_MODES { #define HS_CFG_CANCEL 0xffffffff #define HS_CFG_COND_DEF 0x00000000 #define HS_CFG_GPIO_DEF 0xff -#define HS_CFG_GAP_DEF 0 +#define HS_CFG_GAP_DEF 0xff #define HS_CFG_COND_BROADCAST_DATA 0x00000001 #define HS_CFG_COND_UNICAST_DATA 0x00000002 #define HS_CFG_COND_MAC_EVENT 0x00000004 @@ -440,6 +489,8 @@ enum P2P_MODES { #define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c #define EVENT_HOSTWAKE_STAIE 0x0000004d #define EVENT_CHANNEL_SWITCH_ANN 0x00000050 +#define EVENT_TDLS_GENERIC_EVENT 0x00000052 +#define EVENT_EXT_SCAN_REPORT 0x00000058 #define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f #define EVENT_ID_MASK 0xffff @@ -468,7 +519,12 @@ enum P2P_MODES { #define MWIFIEX_CRITERIA_UNICAST BIT(1) #define MWIFIEX_CRITERIA_MULTICAST BIT(3) -#define CFG_DATA_TYPE_CAL 2 +#define ACT_TDLS_DELETE 0x00 +#define ACT_TDLS_CREATE 0x01 +#define ACT_TDLS_CONFIG 0x02 +#define TDLS_EVENT_LINK_TEAR_DOWN 3 + +#define MWIFIEX_FW_V15 15 struct mwifiex_ie_types_header { __le16 type; @@ -482,6 +538,8 @@ struct mwifiex_ie_types_data { #define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 #define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 +#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 +#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 struct txpd { u8 bss_type; @@ -524,7 +582,7 @@ struct rxpd { * [Bit 7] Reserved */ u8 ht_info; - u8 reserved; + u8 flags; } __packed; struct uap_txpd { @@ -610,12 +668,12 @@ struct mwifiex_ie_types_tsf_timestamp { struct mwifiex_cf_param_set { u8 cfp_cnt; u8 cfp_period; - u16 cfp_max_duration; - u16 cfp_duration_remaining; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; } __packed; struct mwifiex_ibss_param_set { - u16 atim_window; + __le16 atim_window; } __packed; struct mwifiex_ie_types_ss_param_set { @@ -627,7 +685,7 @@ struct mwifiex_ie_types_ss_param_set { } __packed; struct mwifiex_fh_param_set { - u16 dwell_time; + __le16 dwell_time; u8 hop_set; u8 hop_pattern; u8 hop_index; @@ -655,6 +713,13 @@ struct mwifiex_ie_types_vendor_param_set { u8 ie[MWIFIEX_MAX_VSIE_LEN]; }; +#define MWIFIEX_TDLS_IDLE_TIMEOUT 60 + +struct mwifiex_ie_types_tdls_idle_timeout { + struct mwifiex_ie_types_header header; + __le16 value; +} __packed; + struct mwifiex_ie_types_rsn_param_set { struct mwifiex_ie_types_header header; u8 rsn_ie[1]; @@ -678,16 +743,66 @@ struct mwifiex_cmac_param { u8 key[WLAN_KEY_LEN_AES_CMAC]; } __packed; +struct mwifiex_wep_param { + __le16 key_len; + u8 key[WLAN_KEY_LEN_WEP104]; +} __packed; + +struct mwifiex_tkip_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_TKIP]; +} __packed; + +struct mwifiex_aes_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_CCMP]; +} __packed; + +struct mwifiex_wapi_param { + u8 pn[PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_SMS4]; +} __packed; + +struct mwifiex_cmac_aes_param { + u8 ipn[IGTK_PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct mwifiex_ie_type_key_param_set_v2 { + __le16 type; + __le16 len; + u8 mac_addr[ETH_ALEN]; + u8 key_idx; + u8 key_type; + __le16 key_info; + union { + struct mwifiex_wep_param wep; + struct mwifiex_tkip_param tkip; + struct mwifiex_aes_param aes; + struct mwifiex_wapi_param wapi; + struct mwifiex_cmac_aes_param cmac_aes; + } key_params; +} __packed; + +struct host_cmd_ds_802_11_key_material_v2 { + __le16 action; + struct mwifiex_ie_type_key_param_set_v2 key_param_set; +} __packed; + struct host_cmd_ds_802_11_key_material { __le16 action; struct mwifiex_ie_type_key_param_set key_param_set; } __packed; struct host_cmd_ds_gen { - u16 command; - u16 size; - u16 seq_num; - u16 result; + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; }; #define S_DS_GEN sizeof(struct host_cmd_ds_gen) @@ -729,6 +844,17 @@ struct host_cmd_ds_802_11_ps_mode_enh { } params; } __packed; +enum FW_API_VER_ID { + KEY_API_VER_ID = 1, +}; + +struct hw_spec_fw_api_rev { + struct mwifiex_ie_types_header header; + __le16 api_id; + u8 major_ver; + u8 minor_ver; +} __packed; + struct host_cmd_ds_get_hw_spec { __le16 hw_if_version; __le16 version; @@ -750,6 +876,7 @@ struct host_cmd_ds_get_hw_spec { __le32 reserved_6; __le32 dot_11ac_dev_cap; __le32 dot_11ac_mcs_support; + u8 tlvs[0]; } __packed; struct host_cmd_ds_802_11_rssi_info { @@ -820,8 +947,8 @@ struct ieee_types_cf_param_set { u8 len; u8 cfp_cnt; u8 cfp_period; - u16 cfp_max_duration; - u16 cfp_duration_remaining; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; } __packed; struct ieee_types_ibss_param_set { @@ -957,7 +1084,7 @@ struct mwifiex_hs_config_param { } __packed; struct hs_activate_param { - u16 resp_ctrl; + __le16 resp_ctrl; } __packed; struct host_cmd_ds_802_11_hs_cfg_enh { @@ -995,6 +1122,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 { @@ -1020,8 +1148,8 @@ struct mwifiex_power_group { } __packed; struct mwifiex_types_power_group { - u16 type; - u16 length; + __le16 type; + __le16 length; } __packed; struct host_cmd_ds_txpwr_cfg { @@ -1049,14 +1177,28 @@ struct host_cmd_ds_rf_ant_siso { __le16 ant_mode; }; -struct mwifiex_bcn_param { - u8 bssid[ETH_ALEN]; - u8 rssi; +struct host_cmd_ds_tdls_oper { + __le16 tdls_action; + __le16 reason; + u8 peer_mac[ETH_ALEN]; +} __packed; + +struct mwifiex_fixed_bcn_param { __le64 timestamp; __le16 beacon_period; __le16 cap_info_bitmap; } __packed; +struct mwifiex_event_scan_result { + __le16 event_id; + u8 bss_index; + u8 bss_type; + u8 more_event; + u8 reserved[3]; + __le16 buf_size; + u8 num_of_set; +} __packed; + #define MWIFIEX_USER_SCAN_CHAN_MAX 50 #define MWIFIEX_MAX_SSID_LIST_LENGTH 10 @@ -1126,12 +1268,34 @@ struct host_cmd_ds_802_11_scan_rsp { u8 bss_desc_and_tlv_buffer[1]; } __packed; +struct host_cmd_ds_802_11_scan_ext { + u32 reserved; + u8 tlv_buffer[1]; +} __packed; + +struct mwifiex_ie_types_bss_scan_rsp { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; + u8 frame_body[1]; +} __packed; + +struct mwifiex_ie_types_bss_scan_info { + struct mwifiex_ie_types_header header; + __le16 rssi; + __le16 anpi; + u8 cca_busy_fraction; + u8 radio_type; + u8 channel; + u8 reserved; + __le64 tsf; +} __packed; + struct host_cmd_ds_802_11_bg_scan_query { u8 flush; } __packed; struct host_cmd_ds_802_11_bg_scan_query_rsp { - u32 report_condition; + __le32 report_condition; struct host_cmd_ds_802_11_scan_rsp scan_resp; } __packed; @@ -1230,7 +1394,7 @@ struct mwifiex_ie_types_wmm_queue_status { struct mwifiex_ie_types_header header; u8 queue_index; u8 disabled; - u16 medium_time; + __le16 medium_time; u8 flow_required; u8 flow_created; u32 reserved; @@ -1298,6 +1462,11 @@ struct mwifiex_ie_types_vhtcap { struct ieee80211_vht_cap vht_cap; } __packed; +struct mwifiex_ie_types_aid { + struct mwifiex_ie_types_header header; + __le16 aid; +} __packed; + struct mwifiex_ie_types_oper_mode_ntf { struct mwifiex_ie_types_header header; u8 oper_mode; @@ -1310,7 +1479,7 @@ struct mwifiex_ie_types_vht_oper { u8 chan_center_freq_1; u8 chan_center_freq_2; /* Basic MCS set map, each 2 bits stands for a NSS */ - u16 basic_mcs_map; + __le16 basic_mcs_map; } __packed; struct mwifiex_ie_types_wmmcap { @@ -1333,6 +1502,11 @@ struct mwifiex_ie_types_extcap { u8 ext_capab[0]; } __packed; +struct mwifiex_ie_types_qos_info { + struct mwifiex_ie_types_header header; + u8 qos_info; +} __packed; + struct host_cmd_ds_mac_reg_access { __le16 action; __le16 offset; @@ -1443,6 +1617,11 @@ struct host_cmd_tlv_rates { u8 rates[0]; } __packed; +struct mwifiex_ie_types_bssid_list { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; +} __packed; + struct host_cmd_tlv_bcast_ssid { struct mwifiex_ie_types_header header; u8 bcast_ctl; @@ -1578,6 +1757,15 @@ struct host_cmd_ds_802_11_subsc_evt { __le16 events; } __packed; +struct mwifiex_tdls_generic_event { + __le16 type; + u8 peer_mac[ETH_ALEN]; + union { + __le16 reason_code; + __le16 reserved; + } u; +} __packed; + struct mwifiex_ie { __le16 ie_index; __le16 mgmt_subtype_mask; @@ -1592,12 +1780,6 @@ struct mwifiex_ie_list { struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; } __packed; -struct host_cmd_ds_802_11_cfg_data { - __le16 action; - __le16 type; - __le16 data_len; -} __packed; - struct coalesce_filt_field_param { u8 operation; u8 operand_len; @@ -1642,6 +1824,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; struct host_cmd_ds_802_11_scan scan; + struct host_cmd_ds_802_11_scan_ext ext_scan; struct host_cmd_ds_802_11_scan_rsp scan_resp; struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; @@ -1661,6 +1844,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_11n_cfg htcfg; struct host_cmd_ds_wmm_get_status get_wmm_status; struct host_cmd_ds_802_11_key_material key_material; + struct host_cmd_ds_802_11_key_material_v2 key_material_v2; struct host_cmd_ds_version_ext verext; struct host_cmd_ds_mgmt_frame_reg reg_mask; struct host_cmd_ds_remain_on_chan roc_cfg; @@ -1678,8 +1862,8 @@ struct host_cmd_ds_command { struct host_cmd_ds_sys_config uap_sys_config; struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; - struct host_cmd_ds_802_11_cfg_data cfg_data; struct host_cmd_ds_coalesce_cfg coalesce_cfg; + struct host_cmd_ds_tdls_oper tdls_oper; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index 220af4fe0fc..3bf3d58bbc0 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -82,7 +82,7 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, struct mwifiex_ie_list *ie_list) { u16 travel_len, index, mask; - s16 input_len; + s16 input_len, tlv_len; struct mwifiex_ie *ie; u8 *tmp; @@ -91,11 +91,13 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, ie_list->len = 0; - while (input_len > 0) { + while (input_len >= sizeof(struct mwifiex_ie_types_header)) { ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len); - input_len -= le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE; - travel_len += le16_to_cpu(ie->ie_length) + MWIFIEX_IE_HDR_SIZE; + tlv_len = le16_to_cpu(ie->ie_length); + travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE; + if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE) + return -1; index = le16_to_cpu(ie->ie_index); mask = le16_to_cpu(ie->mgmt_subtype_mask); @@ -132,12 +134,13 @@ mwifiex_update_autoindex_ies(struct mwifiex_private *priv, le16_add_cpu(&ie_list->len, le16_to_cpu(priv->mgmt_ie[index].ie_length) + MWIFIEX_IE_HDR_SIZE); + input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE; } if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - return mwifiex_send_cmd_async(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_CUSTOM_IE_I, ie_list); + return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_CUSTOM_IE_I, ie_list, false); return 0; } diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 6499117fce4..4ecd0b208ac 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -137,6 +137,7 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->csa_expire_time = 0; priv->del_list_idx = 0; priv->hs2_enabled = false; + memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); return mwifiex_add_bss_prio_tbl(priv); } @@ -233,7 +234,6 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->pm_wakeup_fw_try = false; - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; adapter->is_hs_configured = false; @@ -281,6 +281,9 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) adapter->arp_filter_size = 0; adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; adapter->empty_tx_q_cnt = 0; + adapter->ext_scan = true; + adapter->fw_key_api_major_ver = 0; + adapter->fw_key_api_minor_ver = 0; } /* @@ -450,6 +453,7 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); INIT_LIST_HEAD(&priv->sta_list); + skb_queue_head_init(&priv->tdls_txq); spin_lock_init(&priv->tx_ba_stream_tbl_lock); spin_lock_init(&priv->rx_reorder_tbl_lock); @@ -615,7 +619,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) /* cancel current command */ if (adapter->curr_cmd) { dev_warn(adapter->dev, "curr_cmd is still in processing\n"); - del_timer(&adapter->cmd_timer); + del_timer_sync(&adapter->cmd_timer); mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); adapter->curr_cmd = NULL; } @@ -643,7 +647,8 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) if (priv) priv->stats.rx_dropped++; - adapter->if_ops.data_complete(adapter, skb); + dev_kfree_skb_any(skb); + adapter->if_ops.data_complete(adapter); } } diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 00a95f4c6a6..1b576722671 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -60,8 +60,7 @@ enum { BAND_A = 4, BAND_GN = 8, BAND_AN = 16, - BAND_GAC = 32, - BAND_AAC = 64, + BAND_AAC = 32, }; #define MWIFIEX_WPA_PASSHPHRASE_LEN 64 @@ -86,6 +85,10 @@ struct wep_key { #define BAND_CONFIG_A 0x01 #define MWIFIEX_SUPPORTED_RATES 14 #define MWIFIEX_SUPPORTED_RATES_EXT 32 +#define MWIFIEX_TDLS_SUPPORTED_RATES 8 +#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf +#define MWIFIEX_PRIO_BK 2 +#define MWIFIEX_PRIO_VI 5 struct mwifiex_uap_bss_param { u8 channel; @@ -174,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 @@ -206,7 +210,7 @@ struct mwifiex_debug_info { u32 num_cmd_assoc_success; u32 num_cmd_assoc_failure; u32 num_tx_timeout; - u32 num_cmd_timeout; + u8 is_cmd_timedout; u16 timeout_cmd_id; u16 timeout_cmd_act; u16 last_cmd_id[DBG_CMD_NUM]; @@ -233,7 +237,10 @@ struct mwifiex_ds_encrypt_key { u8 mac_addr[ETH_ALEN]; u32 is_wapi_key; u8 pn[PN_LEN]; /* packet number */ + u8 pn_len; u8 is_igtk_key; + u8 is_current_wep_key; + u8 is_rx_seq_valid; }; struct mwifiex_power_cfg { @@ -296,7 +303,7 @@ struct mwifiex_ds_ant_cfg { u32 rx_ant; }; -#define MWIFIEX_NUM_OF_CMD_BUFFER 20 +#define MWIFIEX_NUM_OF_CMD_BUFFER 50 #define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 enum { @@ -432,4 +439,16 @@ struct mwifiex_ds_coalesce_cfg { struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; }; +struct mwifiex_ds_tdls_oper { + u16 tdls_action; + u8 peer_mac[ETH_ALEN]; + u16 capability; + u8 qos_info; + u8 *ext_capab; + u8 ext_capab_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *ht_capab; +}; + #endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 37f873bb342..89dc62a467f 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -515,8 +515,7 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && !bss_desc->disable_11n && !bss_desc->disable_11ac && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos); /* Append vendor specific IE TLV */ @@ -621,7 +620,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, int ret = 0; struct ieee_types_assoc_rsp *assoc_rsp; struct mwifiex_bssdescriptor *bss_desc; - u8 enable_data = true; + bool enable_data = true; u16 cap_info, status_code; assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; @@ -902,9 +901,9 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, mwifiex_get_active_data_rates(priv, adhoc_start->data_rate); if ((adapter->adhoc_start_band & BAND_G) && (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, false)) { dev_err(adapter->dev, "ADHOC_S_CMD: G Protection config failed\n"); return -1; @@ -983,7 +982,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, cpu_to_le16(sizeof(struct ieee80211_ht_cap)); radio_type = mwifiex_band_to_radio_type( priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, ht_cap); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); if (adapter->sec_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_NONE) { @@ -1074,9 +1073,9 @@ mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, priv-> curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &curr_pkt_filter)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &curr_pkt_filter, false)) { dev_err(priv->adapter->dev, "ADHOC_J_CMD: G Protection config failed\n"); return -1; @@ -1300,8 +1299,7 @@ int mwifiex_associate(struct mwifiex_private *priv, if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && !bss_desc->disable_11n && !bss_desc->disable_11ac && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_set_11ac_ba_params(priv); else mwifiex_set_ba_params(priv); @@ -1314,8 +1312,8 @@ int mwifiex_associate(struct mwifiex_private *priv, retrieval */ priv->assoc_rsp_size = 0; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_ASSOCIATE, - HostCmd_ACT_GEN_SET, 0, bss_desc); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE, + HostCmd_ACT_GEN_SET, 0, bss_desc, true); } /* @@ -1335,14 +1333,13 @@ mwifiex_adhoc_start(struct mwifiex_private *priv, priv->curr_bss_params.band); if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_set_11ac_ba_params(priv); else mwifiex_set_ba_params(priv); - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_START, - HostCmd_ACT_GEN_SET, 0, adhoc_ssid); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START, + HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true); } /* @@ -1376,8 +1373,7 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv, if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && !bss_desc->disable_11n && !bss_desc->disable_11ac && - (priv->adapter->config_bands & BAND_GAC || - priv->adapter->config_bands & BAND_AAC)) + priv->adapter->config_bands & BAND_AAC) mwifiex_set_11ac_ba_params(priv); else mwifiex_set_ba_params(priv); @@ -1387,8 +1383,8 @@ int mwifiex_adhoc_join(struct mwifiex_private *priv, dev_dbg(priv->adapter->dev, "info: curr_bss_params.band = %c\n", priv->curr_bss_params.band); - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, - HostCmd_ACT_GEN_SET, 0, bss_desc); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, + HostCmd_ACT_GEN_SET, 0, bss_desc, true); } /* @@ -1407,8 +1403,8 @@ static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) else memcpy(mac_address, mac, ETH_ALEN); - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, - HostCmd_ACT_GEN_SET, 0, mac_address); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, mac_address, true); return ret; } @@ -1436,19 +1432,31 @@ int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) GFP_KERNEL); break; case NL80211_IFTYPE_ADHOC: - return mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_AD_HOC_STOP, - HostCmd_ACT_GEN_SET, 0, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true); case NL80211_IFTYPE_AP: - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true); default: break; } return ret; } -EXPORT_SYMBOL_GPL(mwifiex_deauthenticate); + +/* This function deauthenticates/disconnects from all BSS. */ +void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } +} +EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all); /* * This function converts band to radio type used in channel TLV. diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index c2b91f566e0..e91cd0fa5ca 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -38,7 +38,8 @@ static void scan_delay_timer_fn(unsigned long data) if (adapter->surprise_removed) return; - if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT) { + if (adapter->scan_delay_cnt == MWIFIEX_MAX_SCAN_DELAY_CNT || + !adapter->scan_processing) { /* * Abort scan operation by cancelling all pending scan * commands @@ -194,7 +195,7 @@ static int mwifiex_unregister(struct mwifiex_adapter *adapter) if (adapter->if_ops.cleanup_if) adapter->if_ops.cleanup_if(adapter); - del_timer(&adapter->cmd_timer); + del_timer_sync(&adapter->cmd_timer); /* Free private structures */ for (i = 0; i < adapter->priv_num; i++) { @@ -291,6 +292,12 @@ process_start: while ((skb = skb_dequeue(&adapter->usb_rx_data_q))) mwifiex_handle_rx_packet(adapter, skb); + /* Check for event */ + if (adapter->event_received) { + adapter->event_received = false; + mwifiex_process_event(adapter); + } + /* Check for Cmd Resp */ if (adapter->cmd_resp_received) { adapter->cmd_resp_received = false; @@ -303,12 +310,6 @@ process_start: } } - /* Check for event */ - if (adapter->event_received) { - adapter->event_received = false; - mwifiex_process_event(adapter); - } - /* Check if we need to confirm Sleep Request received previously */ if (adapter->ps_state == PS_STATE_PRE_SLEEP) { @@ -411,13 +412,14 @@ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) */ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) { - int ret, i; + int ret; char fmt[64]; struct mwifiex_private *priv; struct mwifiex_adapter *adapter = context; struct mwifiex_fw_image fw; struct semaphore *sem = adapter->card_sem; bool init_failed = false; + struct wireless_dev *wdev; if (!firmware) { dev_err(adapter->dev, @@ -469,14 +471,16 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; if (mwifiex_register_cfg80211(adapter)) { dev_err(adapter->dev, "cannot register with cfg80211\n"); - goto err_register_cfg80211; + goto err_init_fw; } rtnl_lock(); /* Create station interface by default */ - if (!mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", - NL80211_IFTYPE_STATION, NULL, NULL)) { + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", + NL80211_IFTYPE_STATION, NULL, NULL); + if (IS_ERR(wdev)) { dev_err(adapter->dev, "cannot create default STA interface\n"); + rtnl_unlock(); goto err_add_intf; } rtnl_unlock(); @@ -486,17 +490,6 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) goto done; err_add_intf: - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - - if (!priv) - continue; - - if (priv->wdev && priv->netdev) - mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev); - } - rtnl_unlock(); -err_register_cfg80211: wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); err_init_fw: @@ -528,7 +521,6 @@ done: release_firmware(adapter->firmware); adapter->firmware = NULL; } - complete(&adapter->fw_load); if (init_failed) mwifiex_free_adapter(adapter); up(sem); @@ -542,7 +534,6 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) { int ret; - init_completion(&adapter->fw_load); ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, adapter->dev, GFP_KERNEL, adapter, mwifiex_fw_dpc); @@ -654,8 +645,10 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = skb->len; /* Record the current time the packet was queued; used to * determine the amount of time the packet was queued in @@ -685,8 +678,8 @@ mwifiex_set_mac_address(struct net_device *dev, void *addr) memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); /* Send request to firmware */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_MAC_ADDRESS, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, + HostCmd_ACT_GEN_SET, 0, NULL, true); if (!ret) memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); @@ -754,9 +747,10 @@ static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) } static u16 -mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb) +mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) { - skb->priority = cfg80211_classify8021d(skb); + skb->priority = cfg80211_classify8021d(skb, NULL); return mwifiex_1d_to_wmm_queue[skb->priority]; } @@ -877,12 +871,13 @@ mwifiex_add_card(void *card, struct semaphore *sem, adapter->is_suspended = false; adapter->hs_activated = false; init_waitqueue_head(&adapter->hs_activate_wait_q); - adapter->cmd_wait_q_required = false; init_waitqueue_head(&adapter->cmd_wait_q.wait); adapter->cmd_wait_q.status = 0; adapter->scan_wait_q_woken = false; - adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE"); + adapter->workqueue = + alloc_workqueue("MWIFIEX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); if (!adapter->workqueue) goto err_kmalloc; @@ -997,18 +992,8 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) rtnl_unlock(); } - priv = adapter->priv[0]; - if (!priv || !priv->wdev) - goto exit_remove; - - wiphy_unregister(priv->wdev->wiphy); - wiphy_free(priv->wdev->wiphy); - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - kfree(priv->wdev); - } + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); mwifiex_terminate_workqueue(adapter); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 1d72f13adb9..1398afa8406 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -32,6 +32,7 @@ #include <net/lib80211.h> #include <linux/firmware.h> #include <linux/ctype.h> +#include <linux/of.h> #include "decl.h" #include "ioctl.h" @@ -58,7 +59,7 @@ enum { #define MWIFIEX_UPLD_SIZE (2312) -#define MAX_EVENT_SIZE 1024 +#define MAX_EVENT_SIZE 2048 #define ARP_FILTER_MAX_BUF_SIZE 68 @@ -115,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 @@ -144,7 +145,6 @@ struct mwifiex_dbg { u32 num_cmd_assoc_success; u32 num_cmd_assoc_failure; u32 num_tx_timeout; - u32 num_cmd_timeout; u16 timeout_cmd_id; u16 timeout_cmd_act; u16 last_cmd_id[DBG_CMD_NUM]; @@ -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 { @@ -209,6 +211,7 @@ struct mwifiex_ra_list_tbl { u16 ba_pkt_count; u8 ba_packet_thr; u16 total_pkt_count; + bool tdls_link; }; struct mwifiex_tid_tbl { @@ -261,6 +264,31 @@ struct ieee_types_generic { u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; } __packed; +struct ieee_types_bss_co_2040 { + struct ieee_types_header ieee_hdr; + u8 bss_2040co; +} __packed; + +struct ieee_types_extcap { + struct ieee_types_header ieee_hdr; + u8 ext_capab[8]; +} __packed; + +struct ieee_types_vht_cap { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_cap vhtcap; +} __packed; + +struct ieee_types_vht_oper { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_operation vhtoper; +} __packed; + +struct ieee_types_aid { + struct ieee_types_header ieee_hdr; + u16 aid; +} __packed; + struct mwifiex_bssdescriptor { u8 mac_address[ETH_ALEN]; struct cfg80211_ssid ssid; @@ -442,6 +470,7 @@ struct mwifiex_private { u8 wpa_ie_len; u8 wpa_is_gtk_set; struct host_cmd_ds_802_11_key_material aes_key; + struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; u8 wapi_ie[256]; u8 wapi_ie_len; u8 *wps_ie; @@ -460,6 +489,7 @@ struct mwifiex_private { struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; struct mwifiex_add_ba_param add_ba_param; u16 rx_seq[MAX_NUM_TID]; + u8 tos_to_tid_inv[MAX_NUM_TID]; struct list_head rx_reorder_tbl_ptr; /* spin lock for rx_reorder_tbl_ptr queue */ spinlock_t rx_reorder_tbl_lock; @@ -517,6 +547,8 @@ struct mwifiex_private { unsigned long csa_expire_time; u8 del_list_idx; bool hs2_enabled; + struct station_parameters *sta_params; + struct sk_buff_head tdls_txq; }; enum mwifiex_ba_status { @@ -530,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; @@ -544,10 +577,12 @@ struct mwifiex_rx_reorder_tbl { struct list_head list; int tid; u8 ta[ETH_ALEN]; + int init_win; int start_win; int win_size; void **rx_reorder_ptr; struct reorder_tmr_cnxt timer_context; + u8 amsdu; u8 flags; }; @@ -582,17 +617,35 @@ struct mwifiex_bss_priv { u64 fw_tsf; }; -/* This is AP specific structure which stores information - * about associated STA +struct mwifiex_tdls_capab { + __le16 capab; + u8 rates[32]; + u8 rates_len; + u8 qos_info; + u8 coex_2040; + u16 aid; + struct ieee80211_ht_cap ht_capb; + struct ieee80211_ht_operation ht_oper; + struct ieee_types_extcap extcap; + struct ieee_types_generic rsn_ie; + struct ieee80211_vht_cap vhtcap; + struct ieee80211_vht_operation vhtoper; +}; + +/* This is AP/TDLS specific structure which stores information + * about associated/peer STA */ struct mwifiex_sta_node { struct list_head list; u8 mac_addr[ETH_ALEN]; u8 is_wmm_enabled; u8 is_11n_enabled; + u8 is_11ac_enabled; u8 ampdu_sta[MAX_NUM_TID]; u16 rx_seq[MAX_NUM_TID]; u16 max_amsdu; + u8 tdls_status; + struct mwifiex_tdls_capab tdls_cap; }; struct mwifiex_if_ops { @@ -615,10 +668,11 @@ struct mwifiex_if_ops { void (*cleanup_mpa_buf) (struct mwifiex_adapter *); int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); - int (*data_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*data_complete) (struct mwifiex_adapter *); int (*init_fw_port) (struct mwifiex_adapter *); int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); void (*card_reset) (struct mwifiex_adapter *); + void (*fw_dump)(struct mwifiex_adapter *); int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); }; @@ -670,7 +724,7 @@ struct mwifiex_adapter { struct cmd_ctrl_node *curr_cmd; /* spin lock for command */ spinlock_t mwifiex_cmd_lock; - u32 num_cmd_timeout; + u8 is_cmd_timedout; u16 last_init_cmd; struct timer_list cmd_timer; struct list_head cmd_free_q; @@ -721,24 +775,25 @@ struct mwifiex_adapter { u16 hs_activate_wait_q_woken; wait_queue_head_t hs_activate_wait_q; bool is_suspended; + bool hs_enabling; u8 event_body[MAX_EVENT_SIZE]; u32 hw_dot_11n_dev_cap; u8 hw_dev_mcs_support; + u8 user_dev_mcs_support; u8 adhoc_11n_enabled; u8 sec_chan_offset; struct mwifiex_dbg dbg; u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; u32 arp_filter_size; - u16 cmd_wait_q_required; struct mwifiex_wait_queue cmd_wait_q; u8 scan_wait_q_woken; spinlock_t queue_lock; /* lock for tx queues */ - struct completion fw_load; u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; u16 max_mgmt_ie_index; u8 scan_delay_cnt; u8 empty_tx_q_cnt; const struct firmware *cal_data; + struct device_node *dt_node; /* 11AC */ u32 is_hw_11ac_capable; @@ -751,6 +806,9 @@ struct mwifiex_adapter { atomic_t is_tx_received; 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; }; int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); @@ -786,11 +844,8 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter); int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node); -int mwifiex_send_cmd_async(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf); - -int mwifiex_send_cmd_sync(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf); +int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync); void mwifiex_cmd_timeout_func(unsigned long function_context); @@ -855,8 +910,6 @@ int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, struct sk_buff *skb); int mwifiex_process_sta_event(struct mwifiex_private *); int mwifiex_process_uap_event(struct mwifiex_private *); -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac); void mwifiex_delete_all_station_list(struct mwifiex_private *priv); void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); @@ -878,6 +931,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason); u8 mwifiex_band_to_radio_type(u8 band); int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); +void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter); int mwifiex_adhoc_start(struct mwifiex_private *priv, struct cfg80211_ssid *adhoc_ssid); int mwifiex_adhoc_join(struct mwifiex_private *priv, @@ -936,6 +990,12 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv, struct cfg80211_ap_settings *params); void mwifiex_set_ba_params(struct mwifiex_private *priv); void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv); +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv); +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf); /* * This function checks if the queuing is RA based or not. @@ -1039,7 +1099,7 @@ mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) return 0; /* Clear csa channel, if DFS channel move time has passed */ - if (jiffies > priv->csa_expire_time) { + if (time_after(jiffies, priv->csa_expire_time)) { priv->csa_chan = 0; priv->csa_expire_time = 0; } @@ -1076,7 +1136,7 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, const u8 *key, int key_len, u8 key_index, const u8 *mac_addr, int disable); -int mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len); +int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); int mwifiex_get_ver_ext(struct mwifiex_private *priv); @@ -1151,9 +1211,38 @@ void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix); +void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); extern const struct ethtool_ops mwifiex_ethtool_ops; +void mwifiex_del_all_sta_list(struct mwifiex_private *priv); +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node); +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len); +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw); + #ifdef CONFIG_DEBUG_FS void mwifiex_debugfs_init(void); void mwifiex_debugfs_remove(void); diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 52da8ee7599..2cc9b6fca49 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -39,20 +39,31 @@ static struct semaphore add_remove_card_sem; static int mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, - int size, int flags) + size_t size, int flags) { struct pcie_service_card *card = adapter->card; - dma_addr_t buf_pa; + struct mwifiex_dma_mapping mapping; - buf_pa = pci_map_single(card->dev, skb->data, size, flags); - if (pci_dma_mapping_error(card->dev, buf_pa)) { + mapping.addr = pci_map_single(card->dev, skb->data, size, flags); + if (pci_dma_mapping_error(card->dev, mapping.addr)) { dev_err(adapter->dev, "failed to map pci memory!\n"); return -1; } - memcpy(skb->cb, &buf_pa, sizeof(dma_addr_t)); + mapping.len = size; + mwifiex_store_mapping(skb, &mapping); return 0; } +static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int flags) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_dma_mapping mapping; + + mwifiex_get_mapping(skb, &mapping); + pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); +} + /* * This function reads sleep cookie and checks if FW is ready */ @@ -93,7 +104,7 @@ static int mwifiex_pcie_suspend(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { - card = (struct pcie_service_card *) pci_get_drvdata(pdev); + card = pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; @@ -109,6 +120,7 @@ static int mwifiex_pcie_suspend(struct device *dev) /* Indicate device suspended */ adapter->is_suspended = true; + adapter->hs_enabling = false; return 0; } @@ -128,7 +140,7 @@ static int mwifiex_pcie_resume(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { - card = (struct pcie_service_card *) pci_get_drvdata(pdev); + card = pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; @@ -179,6 +191,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, card->pcie.firmware = data->firmware; card->pcie.reg = data->reg; card->pcie.blksz_fw_dl = data->blksz_fw_dl; + card->pcie.tx_buf_size = data->tx_buf_size; } if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, @@ -199,7 +212,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) struct pcie_service_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; - int i; card = pci_get_drvdata(pdev); if (!card) @@ -209,20 +221,13 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) if (!adapter || !adapter->priv_num) return; - /* In case driver is removed when asynchronous FW load is in progress */ - wait_for_completion(&adapter->fw_load); - if (user_rmmod) { #ifdef CONFIG_PM_SLEEP if (adapter->is_suspended) mwifiex_pcie_resume(&pdev->dev); #endif - for (i = 0; i < adapter->priv_num; i++) - if ((GET_BSS_ROLE(adapter->priv[i]) == - MWIFIEX_BSS_ROLE_STA) && - adapter->priv[i]->media_connected) - mwifiex_deauthenticate(adapter->priv[i], NULL); + mwifiex_deauthenticate_all(adapter); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); @@ -232,7 +237,6 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev) } mwifiex_remove_card(card->adapter, &add_remove_card_sem); - kfree(card); } static void mwifiex_pcie_shutdown(struct pci_dev *pdev) @@ -321,6 +325,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) { @@ -457,7 +485,7 @@ static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); dev_dbg(adapter->dev, "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", @@ -514,7 +542,7 @@ static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); dev_dbg(adapter->dev, "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", @@ -550,8 +578,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) desc2 = card->txbd_ring[i]; if (card->tx_buf_list[i]) { skb = card->tx_buf_list[i]; - pci_unmap_single(card->dev, desc2->paddr, - skb->len, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); dev_kfree_skb_any(skb); } memset(desc2, 0, sizeof(*desc2)); @@ -559,8 +587,8 @@ static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) desc = card->txbd_ring[i]; if (card->tx_buf_list[i]) { skb = card->tx_buf_list[i]; - pci_unmap_single(card->dev, desc->paddr, - skb->len, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); dev_kfree_skb_any(skb); } memset(desc, 0, sizeof(*desc)); @@ -588,8 +616,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) desc2 = card->rxbd_ring[i]; if (card->rx_buf_list[i]) { skb = card->rx_buf_list[i]; - pci_unmap_single(card->dev, desc2->paddr, - skb->len, PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } memset(desc2, 0, sizeof(*desc2)); @@ -597,8 +625,8 @@ static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) desc = card->rxbd_ring[i]; if (card->rx_buf_list[i]) { skb = card->rx_buf_list[i]; - pci_unmap_single(card->dev, desc->paddr, - skb->len, PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } memset(desc, 0, sizeof(*desc)); @@ -623,8 +651,8 @@ static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) desc = card->evtbd_ring[i]; if (card->evt_buf_list[i]) { skb = card->evt_buf_list[i]; - pci_unmap_single(card->dev, desc->paddr, MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); } card->evt_buf_list[i] = NULL; @@ -862,7 +890,6 @@ static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) { struct pcie_service_card *card; - dma_addr_t buf_pa; if (!adapter) return 0; @@ -870,16 +897,14 @@ static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) card = adapter->card; if (card && card->cmdrsp_buf) { - MWIFIEX_SKB_PACB(card->cmdrsp_buf, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, + PCI_DMA_FROMDEVICE); dev_kfree_skb_any(card->cmdrsp_buf); } if (card && card->cmd_buf) { - MWIFIEX_SKB_PACB(card->cmd_buf, &buf_pa); - pci_unmap_single(card->dev, buf_pa, card->cmd_buf->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); } return 0; } @@ -957,7 +982,6 @@ static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) { struct sk_buff *skb; - dma_addr_t buf_pa; u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; struct mwifiex_pcie_buf_desc *desc; struct mwifiex_pfu_buf_desc *desc2; @@ -987,13 +1011,13 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) reg->tx_start_ptr; skb = card->tx_buf_list[wrdoneidx]; + if (skb) { dev_dbg(adapter->dev, "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", skb, wrdoneidx); - MWIFIEX_SKB_PACB(skb, &buf_pa); - pci_unmap_single(card->dev, buf_pa, skb->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); unmap_count++; @@ -1007,7 +1031,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) card->tx_buf_list[wrdoneidx] = NULL; if (reg->pfu_enabled) { - desc2 = (void *)card->txbd_ring[wrdoneidx]; + desc2 = card->txbd_ring[wrdoneidx]; memset(desc2, 0, sizeof(*desc2)); } else { desc = card->txbd_ring[wrdoneidx]; @@ -1047,6 +1071,7 @@ static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. * Download ready interrupt to FW is deffered if Tx ring is not full and * additional payload can be accomodated. + * Caller must ensure tx_param parameter to this function is not NULL. */ static int mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, @@ -1083,16 +1108,16 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, tmp = (__le16 *)&payload[2]; *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); - if (mwifiex_map_pci_memory(adapter, skb, skb->len , + if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) return -1; wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); card->tx_buf_list[wrindx] = skb; if (reg->pfu_enabled) { - desc2 = (void *)card->txbd_ring[wrindx]; + desc2 = card->txbd_ring[wrindx]; desc2->paddr = buf_pa; desc2->len = (u16)skb->len; desc2->frag_len = (u16)skb->len; @@ -1163,8 +1188,7 @@ mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, return -EINPROGRESS; done_unmap: - MWIFIEX_SKB_PACB(skb, &buf_pa); - pci_unmap_single(card->dev, buf_pa, skb->len, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); card->tx_buf_list[wrindx] = NULL; if (reg->pfu_enabled) memset(desc2, 0, sizeof(*desc2)); @@ -1212,9 +1236,13 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) rd_index = card->rxbd_rdptr & reg->rx_mask; skb_data = card->rx_buf_list[rd_index]; - MWIFIEX_SKB_PACB(skb_data, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_RX_DATA_BUF_SIZE, - PCI_DMA_FROMDEVICE); + /* If skb allocation was failed earlier for Rx packet, + * rx_buf_list[rd_index] would have been left with a NULL. + */ + if (!skb_data) + return -ENOMEM; + + mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE); card->rx_buf_list[rd_index] = NULL; /* Get data length from interface header - @@ -1241,7 +1269,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb_tmp, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); dev_dbg(adapter->dev, "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", @@ -1249,7 +1277,7 @@ static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) card->rx_buf_list[rd_index] = skb_tmp; if (reg->pfu_enabled) { - desc2 = (void *)card->rxbd_ring[rd_index]; + desc2 = card->rxbd_ring[rd_index]; desc2->paddr = buf_pa; desc2->len = skb_tmp->len; desc2->frag_len = skb_tmp->len; @@ -1317,7 +1345,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); /* Write the lower 32bits of the physical address to low command * address scratch register @@ -1326,8 +1354,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1339,8 +1366,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write download command to boot code.\n", __func__); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1349,8 +1375,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) dev_err(adapter->dev, "%s: failed to write command len to cmd_size scratch reg\n", __func__); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1359,8 +1384,7 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) CPU_INTR_DOOR_BELL)) { dev_err(adapter->dev, "%s: failed to assert door-bell intr\n", __func__); - pci_unmap_single(card->dev, buf_pa, - MWIFIEX_UPLD_SIZE, PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); return -1; } @@ -1434,7 +1458,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) */ if (card->cmdrsp_buf) { - MWIFIEX_SKB_PACB(card->cmdrsp_buf, &cmdrsp_buf_pa); + cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); /* Write the lower 32bits of the cmdrsp buffer physical address */ if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, @@ -1455,7 +1479,7 @@ mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) } } - MWIFIEX_SKB_PACB(card->cmd_buf, &cmd_buf_pa); + cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)cmd_buf_pa)) { @@ -1509,13 +1533,17 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) int count = 0; u16 rx_len; __le16 pkt_len; - dma_addr_t buf_pa; dev_dbg(adapter->dev, "info: Rx CMD Response\n"); - MWIFIEX_SKB_PACB(skb, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); + + /* Unmap the command as a response has been received. */ + if (card->cmd_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); + card->cmd_buf = NULL; + } pkt_len = *((__le16 *)skb->data); rx_len = le16_to_cpu(pkt_len); @@ -1526,6 +1554,16 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (adapter->ps_state == PS_STATE_SLEEP_CFM) { mwifiex_process_sleep_confirm_resp(adapter, skb->data, skb->len); + mwifiex_pcie_enable_host_int(adapter); + if (mwifiex_write_reg(adapter, + PCIE_CPU_INT_EVENT, + CPU_INTR_SLEEP_CFM_DONE)) { + dev_warn(adapter->dev, + "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); @@ -1539,8 +1577,6 @@ static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, PCI_DMA_FROMDEVICE)) return -1; - - MWIFIEX_SKB_PACB(skb, &buf_pa); } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { adapter->curr_cmd->resp_skb = skb; adapter->cmd_resp_received = true; @@ -1575,8 +1611,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, struct sk_buff *skb) { struct pcie_service_card *card = adapter->card; - dma_addr_t buf_pa; - struct sk_buff *skb_tmp; if (skb) { card->cmdrsp_buf = skb; @@ -1586,14 +1620,6 @@ static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, return -1; } - skb_tmp = card->cmd_buf; - if (skb_tmp) { - MWIFIEX_SKB_PACB(skb_tmp, &buf_pa); - pci_unmap_single(card->dev, buf_pa, skb_tmp->len, - PCI_DMA_FROMDEVICE); - card->cmd_buf = NULL; - } - return 0; } @@ -1606,7 +1632,6 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr, event; - dma_addr_t buf_pa; struct mwifiex_evt_buf_desc *desc; if (!mwifiex_pcie_ok_to_access_hw(adapter)) @@ -1642,9 +1667,7 @@ static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) dev_dbg(adapter->dev, "info: Read Index: %d\n", rdptr); skb_cmd = card->evt_buf_list[rdptr]; - MWIFIEX_SKB_PACB(skb_cmd, &buf_pa); - pci_unmap_single(card->dev, buf_pa, MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE); + mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE); /* Take the pointer and set it to event pointer in adapter and will return back after event handling callback */ @@ -1690,7 +1713,6 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, int ret = 0; u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; u32 wrptr; - dma_addr_t buf_pa; struct mwifiex_evt_buf_desc *desc; if (!skb) @@ -1715,11 +1737,9 @@ static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, MAX_EVENT_SIZE, PCI_DMA_FROMDEVICE)) return -1; - MWIFIEX_SKB_PACB(skb, &buf_pa); card->evt_buf_list[rdptr] = skb; - MWIFIEX_SKB_PACB(skb, &buf_pa); desc = card->evtbd_ring[rdptr]; - desc->paddr = buf_pa; + desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); desc->len = (u16)skb->len; desc->flags = 0; skb = NULL; @@ -1769,7 +1789,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, struct sk_buff *skb; u32 txlen, tx_blocks = 0, tries, len; u32 block_retry_cnt = 0; - dma_addr_t buf_pa; struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; @@ -1867,8 +1886,6 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, goto done; } - MWIFIEX_SKB_PACB(skb, &buf_pa); - /* Wait for the command done interrupt */ do { if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, @@ -1876,16 +1893,15 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, dev_err(adapter->dev, "%s: Failed to read " "interrupt status during fw dnld.\n", __func__); - pci_unmap_single(card->dev, buf_pa, skb->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); ret = -1; goto done; } } while ((ireg_intr & CPU_INTR_DOOR_BELL) == CPU_INTR_DOOR_BELL); - pci_unmap_single(card->dev, buf_pa, skb->len, - PCI_DMA_TODEVICE); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); offset += txlen; } while (true); @@ -1994,23 +2010,9 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) adapter->int_status |= pcie_ireg; spin_unlock_irqrestore(&adapter->int_lock, flags); - if (pcie_ireg & HOST_INTR_CMD_DONE) { - if ((adapter->ps_state == PS_STATE_SLEEP_CFM) || - (adapter->ps_state == PS_STATE_SLEEP)) { - mwifiex_pcie_enable_host_int(adapter); - if (mwifiex_write_reg(adapter, - PCIE_CPU_INT_EVENT, - CPU_INTR_SLEEP_CFM_DONE) - ) { - dev_warn(adapter->dev, - "Write register failed\n"); - return; - - } - } - } else if (!adapter->pps_uapsd_mode && - adapter->ps_state == PS_STATE_SLEEP && - mwifiex_pcie_ok_to_access_hw(adapter)) { + if (!adapter->pps_uapsd_mode && + adapter->ps_state == PS_STATE_SLEEP && + mwifiex_pcie_ok_to_access_hw(adapter)) { /* Potentially for PCIe we could get other * interrupts like shared. Don't change power * state until cookie is set */ @@ -2037,7 +2039,7 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) goto exit; } - card = (struct pcie_service_card *) pci_get_drvdata(pdev); + card = pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, card ? card->adapter : NULL); @@ -2313,6 +2315,7 @@ static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) pci_release_region(pdev, 0); pci_set_drvdata(pdev, NULL); } + kfree(card); } /* @@ -2338,6 +2341,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) } adapter->dev = &pdev->dev; + adapter->tx_buf_size = card->pcie.tx_buf_size; strcpy(adapter->fw_name, card->pcie.firmware); return 0; diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index d322ab8604e..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; @@ -195,18 +197,21 @@ struct mwifiex_pcie_device { const char *firmware; const struct mwifiex_pcie_card_reg *reg; u16 blksz_fw_dl; + u16 tx_buf_size; }; static const struct mwifiex_pcie_device mwifiex_pcie8766 = { .firmware = PCIE8766_DEFAULT_FW_NAME, .reg = &mwifiex_reg_8766, .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, }; static const struct mwifiex_pcie_device mwifiex_pcie8897 = { .firmware = PCIE8897_DEFAULT_FW_NAME, .reg = &mwifiex_reg_8897, .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, }; struct mwifiex_evt_buf_desc { diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 8cf7d50a760..45c5b3450cf 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -29,9 +29,6 @@ #define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 #define MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD 4 -#define MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD 15 -#define MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD 27 -#define MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD 35 /* Memory needed to store a max sized Channel List TLV for a firmware scan */ #define CHAN_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_header) \ @@ -515,14 +512,14 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, scan_chan_list[chan_idx].max_scan_time = cpu_to_le16((u16) user_scan_in-> chan_list[0].scan_time); - else if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + else if (ch->flags & IEEE80211_CHAN_NO_IR) scan_chan_list[chan_idx].max_scan_time = cpu_to_le16(adapter->passive_scan_time); else scan_chan_list[chan_idx].max_scan_time = cpu_to_le16(adapter->active_scan_time); - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + if (ch->flags & IEEE80211_CHAN_NO_IR) scan_chan_list[chan_idx].chan_scan_mode_bitmap |= MWIFIEX_PASSIVE_SCAN; else @@ -591,11 +588,13 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, *chan_tlv_out, struct mwifiex_chan_scan_param_set *scan_chan_list) { + struct mwifiex_adapter *adapter = priv->adapter; int ret = 0; struct mwifiex_chan_scan_param_set *tmp_chan_list; struct mwifiex_chan_scan_param_set *start_chan; - - u32 tlv_idx, rates_size; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + u32 tlv_idx, rates_size, cmd_no; u32 total_scan_time; u32 done_early; u8 radio_type; @@ -733,9 +732,13 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, /* Send the scan command to the firmware with the specified cfg */ - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN, - HostCmd_ACT_GEN_SET, 0, - scan_cfg_out); + if (priv->adapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + + ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET, + 0, scan_cfg_out, false); /* rate IE is updated per scan command but same starting * pointer is used each time so that rate IE from earlier @@ -744,8 +747,19 @@ mwifiex_scan_channel_list(struct mwifiex_private *priv, scan_cfg_out->tlv_buf_len -= sizeof(struct mwifiex_ie_types_header) + rates_size; - if (ret) + if (ret) { + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, + list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); break; + } } if (ret) @@ -786,6 +800,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_ie_types_num_probes *num_probes_tlv; struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct mwifiex_ie_types_bssid_list *bssid_tlv; u8 *tlv_pos; u32 num_probes; u32 ssid_len; @@ -848,6 +863,17 @@ mwifiex_config_scan(struct mwifiex_private *priv, user_scan_in->specific_bssid, sizeof(scan_cfg_out->specific_bssid)); + if (adapter->ext_scan && + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { + bssid_tlv = + (struct mwifiex_ie_types_bssid_list *)tlv_pos; + bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); + memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, + ETH_ALEN); + tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); + } + for (i = 0; i < user_scan_in->num_ssids; i++) { ssid_len = user_scan_in->ssid_list[i].ssid_len; @@ -941,7 +967,7 @@ mwifiex_config_scan(struct mwifiex_private *priv, cpu_to_le16(sizeof(struct ieee80211_ht_cap)); radio_type = mwifiex_band_to_radio_type(priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, ht_cap); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); tlv_pos += sizeof(struct mwifiex_ie_types_htcap); } @@ -1026,20 +1052,10 @@ mwifiex_config_scan(struct mwifiex_private *priv, /* * In associated state we will reduce the number of channels scanned per - * scan command to avoid any traffic delay/loss. This number is decided - * based on total number of channels to be scanned due to constraints - * of command buffers. + * scan command to 1 to avoid any traffic delay/loss. */ - if (priv->media_connected) { - if (chan_num < MWIFIEX_LIMIT_1_CHANNEL_PER_SCAN_CMD) + if (priv->media_connected) *max_chan_per_scan = 1; - else if (chan_num < MWIFIEX_LIMIT_2_CHANNELS_PER_SCAN_CMD) - *max_chan_per_scan = 2; - else if (chan_num < MWIFIEX_LIMIT_3_CHANNELS_PER_SCAN_CMD) - *max_chan_per_scan = 3; - else - *max_chan_per_scan = 4; - } } /* @@ -1324,23 +1340,17 @@ int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, bss_entry->beacon_buf); break; case WLAN_EID_BSS_COEX_2040: - bss_entry->bcn_bss_co_2040 = current_ptr + - sizeof(struct ieee_types_header); - bss_entry->bss_co_2040_offset = (u16) (current_ptr + - sizeof(struct ieee_types_header) - - bss_entry->beacon_buf); + bss_entry->bcn_bss_co_2040 = current_ptr; + bss_entry->bss_co_2040_offset = + (u16) (current_ptr - bss_entry->beacon_buf); break; case WLAN_EID_EXT_CAPABILITY: - bss_entry->bcn_ext_cap = current_ptr + - sizeof(struct ieee_types_header); - bss_entry->ext_cap_offset = (u16) (current_ptr + - sizeof(struct ieee_types_header) - - bss_entry->beacon_buf); + bss_entry->bcn_ext_cap = current_ptr; + bss_entry->ext_cap_offset = + (u16) (current_ptr - bss_entry->beacon_buf); break; case WLAN_EID_OPMODE_NOTIF: - bss_entry->oper_mode = - (void *)(current_ptr + - sizeof(struct ieee_types_header)); + bss_entry->oper_mode = (void *)current_ptr; bss_entry->oper_mode_offset = (u16)((u8 *)bss_entry->oper_mode - bss_entry->beacon_buf); @@ -1576,6 +1586,234 @@ done: return 0; } +static int +mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, + u32 *bytes_left, u64 fw_tsf, u8 *radio_type, + bool ext_scan, s32 rssi_val) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_chan_freq_power *cfp; + struct cfg80211_bss *bss; + u8 bssid[ETH_ALEN]; + s32 rssi; + const u8 *ie_buf; + size_t ie_len; + u16 channel = 0; + u16 beacon_size = 0; + u32 curr_bcn_bytes; + u32 freq; + u16 beacon_period; + u16 cap_info_bitmap; + u8 *current_ptr; + u64 timestamp; + struct mwifiex_fixed_bcn_param *bcn_param; + struct mwifiex_bss_priv *bss_priv; + + if (*bytes_left >= sizeof(beacon_size)) { + /* Extract & convert beacon size from command buffer */ + memcpy(&beacon_size, *bss_info, sizeof(beacon_size)); + *bytes_left -= sizeof(beacon_size); + *bss_info += sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + *bss_info += *bytes_left; + *bytes_left = 0; + return -EFAULT; + } + + /* Initialize the current working beacon pointer for this BSS + * iteration + */ + current_ptr = *bss_info; + + /* Advance the return beacon pointer past the current beacon */ + *bss_info += beacon_size; + *bytes_left -= beacon_size; + + curr_bcn_bytes = beacon_size; + + /* First 5 fields are bssid, RSSI(for legacy scan only), + * time stamp, beacon interval, and capability information + */ + if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + + sizeof(struct mwifiex_fixed_bcn_param)) { + dev_err(adapter->dev, "InterpretIE: not enough bytes left\n"); + return -EFAULT; + } + + memcpy(bssid, current_ptr, ETH_ALEN); + current_ptr += ETH_ALEN; + curr_bcn_bytes -= ETH_ALEN; + + if (!ext_scan) { + rssi = (s32) *current_ptr; + rssi = (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr += sizeof(u8); + curr_bcn_bytes -= sizeof(u8); + dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); + } else { + rssi = rssi_val; + } + + bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; + current_ptr += sizeof(*bcn_param); + curr_bcn_bytes -= sizeof(*bcn_param); + + timestamp = le64_to_cpu(bcn_param->timestamp); + beacon_period = le16_to_cpu(bcn_param->beacon_period); + + cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); + dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n", + cap_info_bitmap); + + /* Rest of the current buffer are IE's */ + ie_buf = current_ptr; + ie_len = curr_bcn_bytes; + dev_dbg(adapter->dev, "info: InterpretIE: IELength for this AP = %d\n", + curr_bcn_bytes); + + while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { + u8 element_id, element_len; + + element_id = *current_ptr; + element_len = *(current_ptr + 1); + if (curr_bcn_bytes < element_len + + sizeof(struct ieee_types_header)) { + dev_err(adapter->dev, + "%s: bytes left < IE length\n", __func__); + return -EFAULT; + } + if (element_id == WLAN_EID_DS_PARAMS) { + channel = *(current_ptr + + sizeof(struct ieee_types_header)); + break; + } + + current_ptr += element_len + sizeof(struct ieee_types_header); + curr_bcn_bytes -= element_len + + sizeof(struct ieee_types_header); + } + + if (channel) { + struct ieee80211_channel *chan; + u8 band; + + /* Skip entry if on csa closed channel */ + if (channel == priv->csa_chan) { + dev_dbg(adapter->dev, + "Dropping entry on csa closed channel\n"); + return 0; + } + + band = BAND_G; + if (radio_type) + band = mwifiex_radio_type_to_band(*radio_type & + (BIT(0) | BIT(1))); + + cfp = mwifiex_get_cfp(priv, band, channel, 0); + + freq = cfp ? cfp->freq : 0; + + chan = ieee80211_get_channel(priv->wdev->wiphy, freq); + + if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(priv->wdev->wiphy, + chan, bssid, timestamp, + cap_info_bitmap, beacon_period, + ie_buf, ie_len, rssi, GFP_KERNEL); + bss_priv = (struct mwifiex_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + if (priv->media_connected && + !memcmp(bssid, priv->curr_bss_params.bss_descriptor + .mac_address, ETH_ALEN)) + mwifiex_update_curr_bss_params(priv, bss); + cfg80211_put_bss(priv->wdev->wiphy, bss); + } + } else { + dev_dbg(adapter->dev, "missing BSS channel IE\n"); + } + + return 0; +} + +static void mwifiex_complete_scan(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = 0; + if (!priv->scan_request) { + dev_dbg(adapter->dev, "complete internal scan\n"); + mwifiex_complete_cmd(adapter, adapter->curr_cmd); + } + } +} + +static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + unsigned long flags; + + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + if (list_empty(&adapter->scan_pending_q)) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + if (!adapter->ext_scan) + mwifiex_complete_scan(priv); + + if (priv->report_scan_result) + priv->report_scan_result = false; + + if (priv->scan_request) { + dev_dbg(adapter->dev, "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, 0); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + dev_dbg(adapter->dev, "info: scan already aborted\n"); + } + } else { + if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; + mod_timer(&priv->scan_delay_timer, jiffies); + dev_dbg(priv->adapter->dev, + "info: %s: triggerring scan abort\n", __func__); + } else if (!mwifiex_wmm_lists_empty(adapter) && + (priv->scan_request && (priv->scan_request->flags & + NL80211_SCAN_FLAG_LOW_PRIORITY))) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + adapter->scan_delay_cnt = 1; + mod_timer(&priv->scan_delay_timer, jiffies + + msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); + dev_dbg(priv->adapter->dev, + "info: %s: deferring scan\n", __func__); + } else { + /* Get scan command from scan_pending_q and put to + * cmd_pending_q + */ + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + } + } + + return; +} + /* * This function handles the command response of scan. * @@ -1600,7 +1838,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, { int ret = 0; struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node; struct host_cmd_ds_802_11_scan_rsp *scan_rsp; struct mwifiex_ie_types_data *tlv_data; struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; @@ -1609,12 +1846,11 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, u32 bytes_left; u32 idx; u32 tlv_buf_size; - struct mwifiex_chan_freq_power *cfp; struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; struct chan_band_param_set *chan_band; u8 is_bgscan_resp; - unsigned long flags; - struct cfg80211_bss *bss; + __le64 fw_tsf = 0; + u8 *radio_type; is_bgscan_resp = (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_BG_SCAN_QUERY); @@ -1676,220 +1912,197 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, &chan_band_tlv); for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { - u8 bssid[ETH_ALEN]; - s32 rssi; - const u8 *ie_buf; - size_t ie_len; - u16 channel = 0; - u64 fw_tsf = 0; - u16 beacon_size = 0; - u32 curr_bcn_bytes; - u32 freq; - u16 beacon_period; - u16 cap_info_bitmap; - u8 *current_ptr; - u64 timestamp; - struct mwifiex_bcn_param *bcn_param; - struct mwifiex_bss_priv *bss_priv; - - if (bytes_left >= sizeof(beacon_size)) { - /* Extract & convert beacon size from command buffer */ - memcpy(&beacon_size, bss_info, sizeof(beacon_size)); - bytes_left -= sizeof(beacon_size); - bss_info += sizeof(beacon_size); + /* + * If the TSF TLV was appended to the scan results, save this + * entry's TSF value in the fw_tsf field. It is the firmware's + * TSF value at the time the beacon or probe response was + * received. + */ + if (tsf_tlv) + memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(fw_tsf)); + + if (chan_band_tlv) { + chan_band = &chan_band_tlv->chan_band_param[idx]; + radio_type = &chan_band->radio_type; + } else { + radio_type = NULL; } - if (!beacon_size || beacon_size > bytes_left) { - bss_info += bytes_left; - bytes_left = 0; - ret = -1; + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, + le64_to_cpu(fw_tsf), + radio_type, false, 0); + if (ret) goto check_next_scan; - } + } - /* Initialize the current working beacon pointer for this BSS - * iteration */ - current_ptr = bss_info; +check_next_scan: + mwifiex_check_next_scan_command(priv); + return ret; +} - /* Advance the return beacon pointer past the current beacon */ - bss_info += beacon_size; - bytes_left -= beacon_size; +/* + * This function prepares an extended scan command to be sent to the firmware + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a extended scan command + * structure to send to firmware. + */ +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; + struct mwifiex_scan_cmd_config *scan_cfg = data_buf; - curr_bcn_bytes = beacon_size; + memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); - /* - * First 5 fields are bssid, RSSI, time stamp, beacon interval, - * and capability information - */ - if (curr_bcn_bytes < sizeof(struct mwifiex_bcn_param)) { - dev_err(adapter->dev, - "InterpretIE: not enough bytes left\n"); - continue; - } - bcn_param = (struct mwifiex_bcn_param *)current_ptr; - current_ptr += sizeof(*bcn_param); - curr_bcn_bytes -= sizeof(*bcn_param); + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); - memcpy(bssid, bcn_param->bssid, ETH_ALEN); + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) + + scan_cfg->tlv_buf_len + S_DS_GEN)); - rssi = (s32) bcn_param->rssi; - rssi = (-rssi) * 100; /* Convert dBm to mBm */ - dev_dbg(adapter->dev, "info: InterpretIE: RSSI=%d\n", rssi); + return 0; +} - timestamp = le64_to_cpu(bcn_param->timestamp); - beacon_period = le16_to_cpu(bcn_param->beacon_period); +/* This function handles the command response of extended scan */ +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv) +{ + dev_dbg(priv->adapter->dev, "info: EXT scan returns successfully\n"); - cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); - dev_dbg(adapter->dev, "info: InterpretIE: capabilities=0x%X\n", - cap_info_bitmap); + mwifiex_complete_scan(priv); - /* Rest of the current buffer are IE's */ - ie_buf = current_ptr; - ie_len = curr_bcn_bytes; - dev_dbg(adapter->dev, - "info: InterpretIE: IELength for this AP = %d\n", - curr_bcn_bytes); + return 0; +} - while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { - u8 element_id, element_len; +/* This function This function handles the event extended scan report. It + * parses extended scan results and informs to cfg80211 stack. + */ +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + u8 *bss_info; + u32 bytes_left, bytes_left_for_tlv, idx; + u16 type, len; + struct mwifiex_ie_types_data *tlv; + struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; + struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; + u8 *radio_type; + u64 fw_tsf = 0; + s32 rssi = 0; + struct mwifiex_event_scan_result *event_scan = buf; + u8 num_of_set = event_scan->num_of_set; + u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); + u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); + + if (num_of_set > MWIFIEX_MAX_AP) { + dev_err(adapter->dev, + "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + num_of_set); + ret = -1; + goto check_next_scan; + } - element_id = *current_ptr; - element_len = *(current_ptr + 1); - if (curr_bcn_bytes < element_len + - sizeof(struct ieee_types_header)) { - dev_err(priv->adapter->dev, - "%s: bytes left < IE length\n", - __func__); - goto check_next_scan; - } - if (element_id == WLAN_EID_DS_PARAMS) { - channel = *(current_ptr + sizeof(struct ieee_types_header)); - break; - } + bytes_left = scan_resp_size; + dev_dbg(adapter->dev, + "EXT_SCAN: size %d, returned %d APs...", + scan_resp_size, num_of_set); - current_ptr += element_len + - sizeof(struct ieee_types_header); - curr_bcn_bytes -= element_len + - sizeof(struct ieee_types_header); + tlv = (struct mwifiex_ie_types_data *)scan_resp; + + for (idx = 0; idx < num_of_set && bytes_left; idx++) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { + dev_err(adapter->dev, "EXT_SCAN: Error bytes left < TLV length\n"); + break; } + scan_rsp_tlv = NULL; + scan_info_tlv = NULL; + bytes_left_for_tlv = bytes_left; - /* - * If the TSF TLV was appended to the scan results, save this - * entry's TSF value in the fw_tsf field. It is the firmware's - * TSF value at the time the beacon or probe response was - * received. + /* BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor */ - if (tsf_tlv) - memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], - sizeof(fw_tsf)); - - if (channel) { - struct ieee80211_channel *chan; - u8 band; + if (type != TLV_TYPE_BSS_SCAN_RSP) + break; - /* Skip entry if on csa closed channel */ - if (channel == priv->csa_chan) { - dev_dbg(adapter->dev, - "Dropping entry on csa closed channel\n"); + bss_info = (u8 *)tlv; + scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + + while (bytes_left_for_tlv >= + sizeof(struct mwifiex_ie_types_header) && + le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left_for_tlv < + sizeof(struct mwifiex_ie_types_header) + len) { + dev_err(adapter->dev, + "EXT_SCAN: Error in processing TLV, bytes left < TLV length\n"); + scan_rsp_tlv = NULL; + bytes_left_for_tlv = 0; continue; } - - band = BAND_G; - if (chan_band_tlv) { - chan_band = - &chan_band_tlv->chan_band_param[idx]; - band = mwifiex_radio_type_to_band( - chan_band->radio_type - & (BIT(0) | BIT(1))); - } - - cfp = mwifiex_get_cfp(priv, band, channel, 0); - - freq = cfp ? cfp->freq : 0; - - chan = ieee80211_get_channel(priv->wdev->wiphy, freq); - - if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(priv->wdev->wiphy, - chan, bssid, timestamp, - cap_info_bitmap, beacon_period, - ie_buf, ie_len, rssi, GFP_KERNEL); - bss_priv = (struct mwifiex_bss_priv *)bss->priv; - bss_priv->band = band; - bss_priv->fw_tsf = fw_tsf; - if (priv->media_connected && - !memcmp(bssid, - priv->curr_bss_params.bss_descriptor - .mac_address, ETH_ALEN)) - mwifiex_update_curr_bss_params(priv, - bss); - cfg80211_put_bss(priv->wdev->wiphy, bss); + switch (type) { + case TLV_TYPE_BSS_SCAN_INFO: + scan_info_tlv = + (struct mwifiex_ie_types_bss_scan_info *)tlv; + if (len != + sizeof(struct mwifiex_ie_types_bss_scan_info) - + sizeof(struct mwifiex_ie_types_header)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; } - } else { - dev_dbg(adapter->dev, "missing BSS channel IE\n"); + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left -= + (len + sizeof(struct mwifiex_ie_types_header)); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); } - } -check_next_scan: - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - if (list_empty(&adapter->scan_pending_q)) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + if (!scan_rsp_tlv) + break; - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - adapter->cmd_wait_q.status = 0; - if (!priv->scan_request) { - dev_dbg(adapter->dev, - "complete internal scan\n"); - mwifiex_complete_cmd(adapter, - adapter->curr_cmd); - } - } - if (priv->report_scan_result) - priv->report_scan_result = false; + /* Advance pointer to the beacon buffer length and + * update the bytes count so that the function + * wlan_interpret_bss_desc_with_ie() can handle the + * scan buffer withut any change + */ + bss_info += sizeof(u16); + bytes_left -= sizeof(u16); - if (priv->scan_request) { - dev_dbg(adapter->dev, "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - dev_dbg(adapter->dev, "info: scan already aborted\n"); - } - } else { - if ((priv->scan_aborting && !priv->scan_request) || - priv->scan_block) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - adapter->scan_delay_cnt = MWIFIEX_MAX_SCAN_DELAY_CNT; - mod_timer(&priv->scan_delay_timer, jiffies); - dev_dbg(priv->adapter->dev, - "info: %s: triggerring scan abort\n", __func__); - } else if (!mwifiex_wmm_lists_empty(adapter) && - (priv->scan_request && (priv->scan_request->flags & - NL80211_SCAN_FLAG_LOW_PRIORITY))) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - adapter->scan_delay_cnt = 1; - mod_timer(&priv->scan_delay_timer, jiffies + - msecs_to_jiffies(MWIFIEX_SCAN_DELAY_MSEC)); - dev_dbg(priv->adapter->dev, - "info: %s: deferring scan\n", __func__); + if (scan_info_tlv) { + rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); + rssi *= 100; /* Convert dBm to mBm */ + dev_dbg(adapter->dev, + "info: InterpretIE: RSSI=%d\n", rssi); + fw_tsf = le64_to_cpu(scan_info_tlv->tsf); + radio_type = &scan_info_tlv->radio_type; } else { - /* Get scan command from scan_pending_q and put to - cmd_pending_q */ - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); + radio_type = NULL; } + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, fw_tsf, + radio_type, true, rssi); + if (ret) + goto check_next_scan; } +check_next_scan: + if (!event_scan->more_event) + mwifiex_check_next_scan_command(priv); + return ret; } @@ -2101,12 +2314,12 @@ mwifiex_save_curr_bcn(struct mwifiex_private *priv) curr_bss->ht_info_offset); if (curr_bss->bcn_vht_cap) - curr_bss->bcn_ht_cap = (void *)(curr_bss->beacon_buf + - curr_bss->vht_cap_offset); + curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + + curr_bss->vht_cap_offset); if (curr_bss->bcn_vht_oper) - curr_bss->bcn_ht_oper = (void *)(curr_bss->beacon_buf + - curr_bss->vht_info_offset); + curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + + curr_bss->vht_info_offset); if (curr_bss->bcn_bss_co_2040) curr_bss->bcn_bss_co_2040 = diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 1576104e3d9..4ce3d7b3399 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -84,6 +84,9 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; card->supports_sdio_new_mode = data->supports_sdio_new_mode; card->has_control_mask = data->has_control_mask; + card->tx_buf_size = data->tx_buf_size; + card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; + card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; } sdio_claim_host(func); @@ -165,7 +168,6 @@ mwifiex_sdio_remove(struct sdio_func *func) struct sdio_mmc_card *card; struct mwifiex_adapter *adapter; struct mwifiex_private *priv; - int i; pr_debug("info: SDIO func num=%d\n", func->num); @@ -177,18 +179,11 @@ mwifiex_sdio_remove(struct sdio_func *func) if (!adapter || !adapter->priv_num) return; - /* In case driver is removed when asynchronous FW load is in progress */ - wait_for_completion(&adapter->fw_load); - if (user_rmmod) { if (adapter->is_suspended) mwifiex_sdio_resume(adapter->dev); - for (i = 0; i < adapter->priv_num; i++) - if ((GET_BSS_ROLE(adapter->priv[i]) == - MWIFIEX_BSS_ROLE_STA) && - adapter->priv[i]->media_connected) - mwifiex_deauthenticate(adapter->priv[i], NULL); + mwifiex_deauthenticate_all(adapter); priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); mwifiex_disable_auto_ds(priv); @@ -196,7 +191,6 @@ mwifiex_sdio_remove(struct sdio_func *func) } mwifiex_remove_card(card->adapter, &add_remove_card_sem); - kfree(card); } /* @@ -242,6 +236,7 @@ static int mwifiex_sdio_suspend(struct device *dev) /* Enable the Host Sleep */ if (!mwifiex_enable_hs(adapter)) { dev_err(adapter->dev, "cmd: failed to suspend\n"); + adapter->hs_enabling = false; return -EFAULT; } @@ -250,6 +245,7 @@ static int mwifiex_sdio_suspend(struct device *dev) /* Indicate device suspended */ adapter->is_suspended = true; + adapter->hs_enabling = false; return ret; } @@ -1029,7 +1025,10 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb, u32 upld_typ) { u8 *cmd_buf; + __le16 *curr_ptr = (__le16 *)skb->data; + u16 pkt_len = le16_to_cpu(*curr_ptr); + skb_trim(skb, pkt_len); skb_pull(skb, INTF_HEADER_LEN); switch (upld_typ) { @@ -1679,8 +1678,12 @@ static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, if (ret) { if (type == MWIFIEX_TYPE_CMD) adapter->cmd_sent = false; - if (type == MWIFIEX_TYPE_DATA) + if (type == MWIFIEX_TYPE_DATA) { adapter->data_sent = false; + /* restore curr_wr_port in error cases */ + card->curr_wr_port = port; + card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); + } } else { if (type == MWIFIEX_TYPE_DATA) { if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) @@ -1742,7 +1745,6 @@ mwifiex_unregister_dev(struct mwifiex_adapter *adapter) sdio_claim_host(card->func); sdio_disable_func(card->func); sdio_release_host(card->func); - sdio_set_drvdata(card->func, NULL); } } @@ -1759,6 +1761,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) /* save adapter pointer in card */ card->adapter = adapter; + adapter->tx_buf_size = card->tx_buf_size; sdio_claim_host(func); @@ -1770,7 +1773,6 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) return ret; } - sdio_set_drvdata(func, card); adapter->dev = &func->dev; @@ -1798,6 +1800,8 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) int ret; u8 sdio_ireg; + sdio_set_drvdata(card->func, card); + /* * Read the HOST_INT_STATUS_REG for ACK the first interrupt got * from the bootloader. If we don't do this we get a interrupt @@ -1841,8 +1845,8 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * card->mp_agg_pkt_limit, GFP_KERNEL); ret = mwifiex_alloc_sdio_mpa_buffers(adapter, - SDIO_MP_TX_AGGR_DEF_BUF_SIZE, - SDIO_MP_RX_AGGR_DEF_BUF_SIZE); + card->mp_tx_agg_buf_size, + card->mp_rx_agg_buf_size); if (ret) { dev_err(adapter->dev, "failed to alloc sdio mp-a buffers\n"); kfree(card->mp_regs); @@ -1880,6 +1884,8 @@ static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) kfree(card->mpa_rx.len_arr); kfree(card->mpa_tx.buf); kfree(card->mpa_rx.buf); + sdio_set_drvdata(card->func, NULL); + kfree(card); } /* diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 532ae0ac4df..6eea30b43ed 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -64,10 +64,8 @@ #define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) #define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) -#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (8192) /* 8K */ - -/* Multi port RX aggregation buffer size */ -#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (16384) /* 16K */ +#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384) +#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768) /* Misc. Config Register : Auto Re-enable interrupts */ #define AUTO_RE_ENABLE_INT BIT(4) @@ -233,6 +231,9 @@ struct sdio_mmc_card { u8 mp_agg_pkt_limit; bool supports_sdio_new_mode; bool has_control_mask; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; u32 mp_rd_bitmap; u32 mp_wr_bitmap; @@ -256,6 +257,9 @@ struct mwifiex_sdio_device { u8 mp_agg_pkt_limit; bool supports_sdio_new_mode; bool has_control_mask; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { @@ -312,6 +316,9 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { .mp_agg_pkt_limit = 8, .supports_sdio_new_mode = false, .has_control_mask = true, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { @@ -321,6 +328,9 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { .mp_agg_pkt_limit = 8, .supports_sdio_new_mode = false, .has_control_mask = true, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { @@ -330,6 +340,9 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { .mp_agg_pkt_limit = 8, .supports_sdio_new_mode = false, .has_control_mask = true, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { @@ -339,6 +352,9 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { .mp_agg_pkt_limit = 16, .supports_sdio_new_mode = true, .has_control_mask = false, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, }; /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index c0268b59774..88202ce0c13 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 + @@ -239,14 +253,14 @@ static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, memmove(cmd_txp_cfg, txp, sizeof(struct host_cmd_ds_txpwr_cfg) + sizeof(struct mwifiex_types_power_group) + - pg_tlv->length); + le16_to_cpu(pg_tlv->length)); pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) cmd_txp_cfg + sizeof(struct host_cmd_ds_txpwr_cfg)); cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + sizeof(struct mwifiex_types_power_group) + - pg_tlv->length); + le16_to_cpu(pg_tlv->length)); } else { memmove(cmd_txp_cfg, txp, sizeof(*txp)); } @@ -327,7 +341,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; - u16 hs_activate = false; + bool hs_activate = false; if (!hscfg_param) /* New Activate command */ @@ -354,7 +368,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, } if (hs_activate) { hs_cfg->action = cpu_to_le16(HS_ACTIVATE); - hs_cfg->params.hs_activate.resp_ctrl = RESP_NEEDED; + hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); } else { hs_cfg->action = cpu_to_le16(HS_CONFIGURE); hs_cfg->params.hs_config.conditions = hscfg_param->conditions; @@ -532,8 +546,228 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, return 0; } +/* This function populates key material v2 command + * to set network key for AES & CMAC AES. + */ +static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_encrypt_key *enc_key, + struct host_cmd_ds_802_11_key_material_v2 *km) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 size, len = KEY_PARAMS_FIXED_LEN; + + if (enc_key->is_igtk_key) { + dev_dbg(adapter->dev, "%s: Set CMAC AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.cmac_aes.ipn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); + km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); + km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + km->key_param_set.key_params.cmac_aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.cmac_aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_cmac_aes_param); + } else { + dev_dbg(adapter->dev, "%s: Set AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.aes.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_AES; + km->key_param_set.key_params.aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_aes_param); + } + + km->key_param_set.len = cpu_to_le16(len); + size = len + sizeof(struct mwifiex_ie_types_header) + + sizeof(km->action) + S_DS_GEN; + cmd->size = cpu_to_le16(size); + + return 0; +} + +/* This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V2 format. + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting WEP keys, WAPI keys or WPA keys along with required + * encryption (TKIP, AES) (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 *mac = enc_key->mac_addr; + u16 key_info, len = KEY_PARAMS_FIXED_LEN; + struct host_cmd_ds_802_11_key_material_v2 *km = + &cmd->params.key_material_v2; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + km->action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + dev_dbg(adapter->dev, "%s: Get key\n", __func__); + km->key_param_set.key_idx = + enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + key_info = KEY_UNICAST; + else + key_info = KEY_MCAST; + + if (enc_key->is_igtk_key) + key_info |= KEY_IGTK; + + km->key_param_set.key_info = cpu_to_le16(key_info); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + memset(&km->key_param_set, 0, + sizeof(struct mwifiex_ie_type_key_param_set_v2)); + + if (enc_key->key_disable) { + dev_dbg(adapter->dev, "%s: Remove key\n", __func__); + km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + key_info = KEY_MCAST | KEY_UNICAST; + km->key_param_set.key_info = cpu_to_le16(key_info); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + km->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + key_info = KEY_ENABLED; + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { + dev_dbg(adapter->dev, "%s: Set WEP Key\n", __func__); + len += sizeof(struct mwifiex_wep_param); + km->key_param_set.len = cpu_to_le16(len); + km->key_param_set.key_type = KEY_TYPE_ID_WEP; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + key_info |= KEY_MCAST | KEY_UNICAST; + } else { + if (enc_key->is_current_wep_key) { + key_info |= KEY_MCAST | KEY_UNICAST; + if (km->key_param_set.key_idx == + (priv->wep_key_curr_index & KEY_INDEX_MASK)) + key_info |= KEY_DEFAULT; + } else { + if (mac) { + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST; + else + key_info |= KEY_UNICAST | + KEY_DEFAULT; + } else { + key_info |= KEY_MCAST; + } + } + } + km->key_param_set.key_info = cpu_to_le16(key_info); + + km->key_param_set.key_params.wep.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wep.key, + enc_key->key_material, enc_key->key_len); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST | KEY_RX_KEY; + else + key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; + + if (enc_key->is_wapi_key) { + dev_dbg(adapter->dev, "%s: Set WAPI Key\n", __func__); + km->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn, + PN_LEN); + km->key_param_set.key_params.wapi.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wapi.key, + enc_key->key_material, enc_key->key_len); + if (is_broadcast_ether_addr(mac)) + priv->sec_info.wapi_key_on = true; + + if (!priv->sec_info.wapi_key_on) + key_info |= KEY_DEFAULT; + km->key_param_set.key_info = cpu_to_le16(key_info); + + len += sizeof(struct mwifiex_wapi_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + key_info |= KEY_DEFAULT; + /* Enable unicast bit for WPA-NONE/ADHOC_AES */ + if (!priv->sec_info.wpa2_enabled && + !is_broadcast_ether_addr(mac)) + key_info |= KEY_UNICAST; + } else { + /* Enable default key for WPA/WPA2 */ + if (!priv->wpa_is_gtk_set) + key_info |= KEY_DEFAULT; + } + + km->key_param_set.key_info = cpu_to_le16(key_info); + + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) + return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km); + + if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + dev_dbg(adapter->dev, "%s: Set TKIP Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.tkip.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_TKIP; + km->key_param_set.key_params.tkip.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.tkip.key, + enc_key->key_material, enc_key->key_len); + + len += sizeof(struct mwifiex_tkip_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + } + + return 0; +} + /* * This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V1 format. * * Preparation includes - * - Setting command ID, action and proper size @@ -542,10 +776,10 @@ mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, * - Ensuring correct endian-ness */ static int -mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) +mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) { struct host_cmd_ds_802_11_key_material *key_material = &cmd->params.key_material; @@ -724,6 +958,24 @@ mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, return ret; } +/* Wrapper function for setting network key depending upon FW KEY API version */ +static int +mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + return mwifiex_cmd_802_11_key_material_v2(priv, cmd, + cmd_action, cmd_oid, + enc_key); + + else + return mwifiex_cmd_802_11_key_material_v1(priv, cmd, + cmd_action, cmd_oid, + enc_key); +} + /* * This function prepares command to set/get 11d domain information. * @@ -1156,30 +1408,62 @@ static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) return d - dst; } +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix) +{ +#ifdef CONFIG_OF + struct property *prop; + size_t len = strlen(prefix); + int ret; + + /* look for all matching property names */ + for_each_property_of_node(node, prop) { + if (len > strlen(prop->name) || + strncmp(prop->name, prefix, len)) + continue; + + /* property header is 6 bytes, data must fit in cmd buffer */ + if (prop && prop->value && prop->length > 6 && + prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, + prop, true); + if (ret) + return ret; + } + } +#endif + return 0; +} + /* This function prepares command of set_cfg_data. */ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action) + struct host_cmd_ds_command *cmd, void *data_buf) { - struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data; struct mwifiex_adapter *adapter = priv->adapter; - u32 len, cal_data_offset; - u8 *tmp_cmd = (u8 *)cmd; + struct property *prop = data_buf; + u32 len; + u8 *data = (u8 *)cmd + S_DS_GEN; + int ret; - cal_data_offset = S_DS_GEN + sizeof(*cfg_data); - if ((adapter->cal_data->data) && (adapter->cal_data->size > 0)) + if (prop) { + len = prop->length; + ret = of_property_read_u8_array(adapter->dt_node, prop->name, + data, len); + if (ret) + return ret; + dev_dbg(adapter->dev, + "download cfg_data from device tree: %s\n", prop->name); + } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, - adapter->cal_data->size, - (u8 *)(tmp_cmd + cal_data_offset)); - else + adapter->cal_data->size, data); + dev_dbg(adapter->dev, "download cfg_data from config file\n"); + } else { return -1; - - cfg_data->action = cpu_to_le16(cmd_action); - cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL); - cfg_data->data_len = cpu_to_le16(len); + } cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len); + cmd->size = cpu_to_le16(S_DS_GEN + len); return 0; } @@ -1248,6 +1532,134 @@ mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, return 0; } +static int +mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; + struct mwifiex_ds_tdls_oper *oper = data_buf; + struct mwifiex_sta_node *sta_ptr; + struct host_cmd_tlv_rates *tlv_rates; + struct mwifiex_ie_types_htcap *ht_capab; + struct mwifiex_ie_types_qos_info *wmm_qos_info; + struct mwifiex_ie_types_extcap *extcap; + struct mwifiex_ie_types_vhtcap *vht_capab; + struct mwifiex_ie_types_aid *aid; + struct mwifiex_ie_types_tdls_idle_timeout *timeout; + u8 *pos, qos_info; + u16 config_len = 0; + struct station_parameters *params = priv->sta_params; + + cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); + cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); + + tdls_oper->reason = 0; + memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); + sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); + + pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); + + switch (oper->tdls_action) { + case MWIFIEX_TDLS_DISABLE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); + break; + case MWIFIEX_TDLS_CREATE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); + break; + case MWIFIEX_TDLS_CONFIG_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG); + + if (!params) { + dev_err(priv->adapter->dev, + "TDLS config params not available for %pM\n", + oper->peer_mac); + return -ENODATA; + } + + *(__le16 *)pos = cpu_to_le16(params->capability); + config_len += sizeof(params->capability); + + qos_info = params->uapsd_queues | (params->max_sp << 5); + wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + + config_len); + wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); + wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); + wmm_qos_info->qos_info = qos_info; + config_len += sizeof(struct mwifiex_ie_types_qos_info); + + if (params->ht_capa) { + ht_capab = (struct mwifiex_ie_types_htcap *)(pos + + config_len); + ht_capab->header.type = + cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy(&ht_capab->ht_cap, params->ht_capa, + sizeof(struct ieee80211_ht_cap)); + config_len += sizeof(struct mwifiex_ie_types_htcap); + } + + if (params->supported_rates && params->supported_rates_len) { + tlv_rates = (struct host_cmd_tlv_rates *)(pos + + config_len); + tlv_rates->header.type = + cpu_to_le16(WLAN_EID_SUPP_RATES); + tlv_rates->header.len = + cpu_to_le16(params->supported_rates_len); + memcpy(tlv_rates->rates, params->supported_rates, + params->supported_rates_len); + config_len += sizeof(struct host_cmd_tlv_rates) + + params->supported_rates_len; + } + + if (params->ext_capab && params->ext_capab_len) { + extcap = (struct mwifiex_ie_types_extcap *)(pos + + config_len); + extcap->header.type = + cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + extcap->header.len = cpu_to_le16(params->ext_capab_len); + memcpy(extcap->ext_capab, params->ext_capab, + params->ext_capab_len); + config_len += sizeof(struct mwifiex_ie_types_extcap) + + params->ext_capab_len; + } + if (params->vht_capa) { + vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos + + config_len); + vht_capab->header.type = + cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy(&vht_capab->vht_cap, params->vht_capa, + sizeof(struct ieee80211_vht_cap)); + config_len += sizeof(struct mwifiex_ie_types_vhtcap); + } + if (params->aid) { + aid = (struct mwifiex_ie_types_aid *)(pos + config_len); + aid->header.type = cpu_to_le16(WLAN_EID_AID); + aid->header.len = cpu_to_le16(sizeof(params->aid)); + aid->aid = cpu_to_le16(params->aid); + config_len += sizeof(struct mwifiex_ie_types_aid); + } + + timeout = (void *)(pos + config_len); + timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); + timeout->header.len = cpu_to_le16(sizeof(timeout->value)); + timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT); + config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); + + break; + default: + dev_err(priv->adapter->dev, "Unknown TDLS operation\n"); + return -ENOTSUPP; + } + + le16_add_cpu(&cmd->size, config_len); + + return 0; +} /* * This function prepares the commands before sending them to the firmware. * @@ -1267,7 +1679,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); break; case HostCmd_CMD_CFG_DATA: - ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action); + ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_MAC_CONTROL: ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, @@ -1440,6 +1852,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, data_buf); break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf); + break; case HostCmd_CMD_MAC_REG_ACCESS: case HostCmd_CMD_BBP_REG_ACCESS: case HostCmd_CMD_RF_REG_ACCESS: @@ -1475,6 +1890,9 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, data_buf); break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd- %#x\n", cmd_no); @@ -1515,69 +1933,84 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (first_sta) { if (priv->adapter->iface_type == MWIFIEX_PCIE) { - ret = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_PCIE_DESC_DETAILS, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_PCIE_DESC_DETAILS, + HostCmd_ACT_GEN_SET, 0, NULL, + true); if (ret) return -1; } - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_FUNC_INIT, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT, + HostCmd_ACT_GEN_SET, 0, NULL, true); if (ret) return -1; - /* Download calibration data to firmware */ + /* Download calibration data to firmware. + * The cal-data can be read from device tree and/or + * a configuration file and downloaded to firmware. + */ + adapter->dt_node = + of_find_node_by_name(NULL, "marvell_cfgdata"); + if (adapter->dt_node) { + ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, + "marvell,caldata"); + if (ret) + return -1; + } + if (adapter->cal_data) { - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, NULL, + true); if (ret) return -1; } /* Read MAC address from HW */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_GET_HW_SPEC, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, NULL, true); if (ret) return -1; /* Reconfigure tx buf size */ - ret = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_RECONFIGURE_TX_BUFF, - HostCmd_ACT_GEN_SET, 0, - &priv->adapter->tx_buf_size); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, + &priv->adapter->tx_buf_size, true); if (ret) return -1; if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable IEEE PS by default */ priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - ret = mwifiex_send_cmd_sync( - priv, HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_STA_PS, NULL); + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_STA_PS, NULL, + true); if (ret) return -1; } } /* get tx rate */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, NULL, true); if (ret) return -1; priv->data_rate = 0; /* get tx power */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_RF_TX_PWR, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR, + HostCmd_ACT_GEN_GET, 0, NULL, true); if (ret) return -1; if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { /* set ibss coalescing_status */ - ret = mwifiex_send_cmd_sync( - priv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_SET, 0, &enable); + ret = mwifiex_send_cmd( + priv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_SET, 0, &enable, true); if (ret) return -1; } @@ -1585,16 +2018,16 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); amsdu_aggr_ctrl.enable = true; /* Send request to firmware */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, - HostCmd_ACT_GEN_SET, 0, - &amsdu_aggr_ctrl); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, + HostCmd_ACT_GEN_SET, 0, + &amsdu_aggr_ctrl, true); if (ret) return -1; /* MAC Control must be the last command in init_fw */ /* set MAC Control */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); if (ret) return -1; @@ -1603,10 +2036,9 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; - ret = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_AUTO_DS, - &auto_ds); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_AUTO_DS, + &auto_ds, true); if (ret) return -1; } @@ -1614,9 +2046,9 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Send cmd to FW to enable/disable 11D function */ state_11d = ENABLE_11D; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d, true); if (ret) dev_err(priv->adapter->dev, "11D: failed to enable 11D\n"); @@ -1629,8 +2061,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) * (Short GI, Channel BW, Green field support etc.) for transmit */ tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_11N_CFG, - HostCmd_ACT_GEN_SET, 0, &tx_cfg); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, + HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); ret = -EINPROGRESS; diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 58a6013712d..577f2979ed8 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -69,6 +69,7 @@ mwifiex_process_cmdresp_error(struct mwifiex_private *priv, break; case HostCmd_CMD_802_11_SCAN: + case HostCmd_CMD_802_11_SCAN_EXT: /* Cancel all pending scan command */ spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, @@ -157,8 +158,8 @@ static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, priv->subsc_evt_rssi_state = EVENT_HANDLED; - mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, subsc_evt); + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, subsc_evt, false); return 0; } @@ -274,17 +275,20 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; struct mwifiex_rate_scope *rate_scope; struct mwifiex_ie_types_header *head; - u16 tlv, tlv_buf_len; + u16 tlv, tlv_buf_len, tlv_buf_left; u8 *tlv_buf; u32 i; - tlv_buf = ((u8 *)rate_cfg) + - sizeof(struct host_cmd_ds_tx_rate_cfg); - tlv_buf_len = le16_to_cpu(*(__le16 *) (tlv_buf + sizeof(u16))); + tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); - while (tlv_buf && tlv_buf_len > 0) { - tlv = (*tlv_buf); - tlv = tlv | (*(tlv_buf + 1) << 8); + while (tlv_buf_left >= sizeof(*head)) { + head = (struct mwifiex_ie_types_header *)tlv_buf; + tlv = le16_to_cpu(head->type); + tlv_buf_len = le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; switch (tlv) { case TLV_TYPE_RATE_SCOPE: @@ -300,13 +304,21 @@ 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 */ } - head = (struct mwifiex_ie_types_header *) tlv_buf; - tlv_buf += le16_to_cpu(head->len) + sizeof(*head); - tlv_buf_len -= le16_to_cpu(head->len); + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); } priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); @@ -314,9 +326,8 @@ static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, if (priv->is_data_rate_auto) priv->data_rate = 0; else - return mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, false); return 0; } @@ -336,17 +347,20 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) if (!data_buf) return -1; - pg_tlv_hdr = (struct mwifiex_types_power_group *) - ((u8 *) data_buf + sizeof(struct host_cmd_ds_txpwr_cfg)); + pg_tlv_hdr = (struct mwifiex_types_power_group *)((u8 *)data_buf); pg = (struct mwifiex_power_group *) ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); - length = pg_tlv_hdr->length; - if (length > 0) { - max_power = pg->power_max; - min_power = pg->power_min; - length -= sizeof(struct mwifiex_power_group); - } - while (length) { + length = le16_to_cpu(pg_tlv_hdr->length); + + /* At least one structure required to update power */ + if (length < sizeof(struct mwifiex_power_group)) + return 0; + + max_power = pg->power_max; + min_power = pg->power_min; + length -= sizeof(struct mwifiex_power_group); + + while (length >= sizeof(struct mwifiex_power_group)) { pg++; if (max_power < pg->power_max) max_power = pg->power_max; @@ -356,10 +370,8 @@ static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) length -= sizeof(struct mwifiex_power_group); } - if (pg_tlv_hdr->length > 0) { - priv->min_tx_power_level = (u8) min_power; - priv->max_tx_power_level = (u8) max_power; - } + priv->min_tx_power_level = (u8) min_power; + priv->max_tx_power_level = (u8) max_power; return 0; } @@ -379,19 +391,25 @@ static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, struct mwifiex_types_power_group *pg_tlv_hdr; struct mwifiex_power_group *pg; u16 action = le16_to_cpu(txp_cfg->action); + u16 tlv_buf_left; - switch (action) { - case HostCmd_ACT_GEN_GET: - pg_tlv_hdr = (struct mwifiex_types_power_group *) - ((u8 *) txp_cfg + - sizeof(struct host_cmd_ds_txpwr_cfg)); + pg_tlv_hdr = (struct mwifiex_types_power_group *) + ((u8 *)txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); - pg = (struct mwifiex_power_group *) - ((u8 *) pg_tlv_hdr + - sizeof(struct mwifiex_types_power_group)); + pg = (struct mwifiex_power_group *) + ((u8 *)pg_tlv_hdr + + sizeof(struct mwifiex_types_power_group)); + + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); + if (tlv_buf_left < + le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) + return 0; + switch (action) { + case HostCmd_ACT_GEN_GET: if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) - mwifiex_get_power_level(priv, txp_cfg); + mwifiex_get_power_level(priv, pg_tlv_hdr); priv->tx_power_level = (u16) pg->power_min; break; @@ -400,14 +418,6 @@ static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, if (!le32_to_cpu(txp_cfg->mode)) break; - pg_tlv_hdr = (struct mwifiex_types_power_group *) - ((u8 *) txp_cfg + - sizeof(struct host_cmd_ds_txpwr_cfg)); - - pg = (struct mwifiex_power_group *) - ((u8 *) pg_tlv_hdr + - sizeof(struct mwifiex_types_power_group)); - if (pg->power_max == pg->power_min) priv->tx_power_level = (u16) pg->power_min; break; @@ -560,13 +570,13 @@ static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, } /* - * This function handles the command response of set/get key material. + * This function handles the command response of set/get v1 key material. * * Handling includes updating the driver parameters to reflect the * changes. */ -static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) +static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) { struct host_cmd_ds_802_11_key_material *key = &resp->params.key_material; @@ -589,6 +599,51 @@ static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, } /* + * This function handles the command response of set/get v2 key material. + * + * Handling includes updating the driver parameters to reflect the + * changes. + */ +static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_key_material_v2 *key_v2; + __le16 len; + + key_v2 = &resp->params.key_material_v2; + if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { + if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { + dev_dbg(priv->adapter->dev, "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + } + } + + if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) + return 0; + + memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, + WLAN_KEY_LEN_CCMP); + priv->aes_key_v2.key_param_set.key_params.aes.key_len = + key_v2->key_param_set.key_params.aes.key_len; + len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; + memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, + key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); + + return 0; +} + +/* Wrapper function for processing response of key material command */ +static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (priv->adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + return mwifiex_ret_802_11_key_material_v2(priv, resp); + else + return mwifiex_ret_802_11_key_material_v1(priv, resp); +} + +/* * This function handles the command response of get 11d domain information. */ static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, @@ -781,8 +836,7 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, } /* If BSSID is diff, modify current BSS parameters */ - if (memcmp(priv->curr_bss_params.bss_descriptor.mac_address, - ibss_coal_resp->bssid, ETH_ALEN)) { + if (!ether_addr_equal(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid)) { /* BSSID */ memcpy(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid, ETH_ALEN); @@ -800,7 +854,66 @@ static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, return 0; } +static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; + u16 reason = le16_to_cpu(cmd_tdls_oper->reason); + u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); + struct mwifiex_sta_node *node = + mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); + + switch (action) { + case ACT_TDLS_DELETE: + if (reason) { + if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) + dev_dbg(priv->adapter->dev, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + else + dev_err(priv->adapter->dev, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + } else { + dev_dbg(priv->adapter->dev, + "TDLS link delete for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; + case ACT_TDLS_CREATE: + if (reason) { + dev_err(priv->adapter->dev, + "TDLS link creation for %pM failed: reason %d", + cmd_tdls_oper->peer_mac, reason); + if (node && reason != TDLS_ERR_LINK_EXISTS) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + dev_dbg(priv->adapter->dev, + "TDLS link creation for %pM successful", + cmd_tdls_oper->peer_mac); + } + break; + case ACT_TDLS_CONFIG: + if (reason) { + dev_err(priv->adapter->dev, + "TDLS link config for %pM failed, reason %d\n", + cmd_tdls_oper->peer_mac, reason); + if (node) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + dev_dbg(priv->adapter->dev, + "TDLS link config for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; + default: + dev_err(priv->adapter->dev, + "Unknown TDLS command action respnse %d", action); + return -1; + } + return 0; +} /* * This function handles the command response for subscribe event command. */ @@ -871,6 +984,10 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, ret = mwifiex_ret_802_11_scan(priv, resp); adapter->curr_cmd->wait_q_enabled = false; break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_ret_802_11_scan_ext(priv); + adapter->curr_cmd->wait_q_enabled = false; + break; case HostCmd_CMD_802_11_BG_SCAN_QUERY: ret = mwifiex_ret_802_11_scan(priv, resp); dev_dbg(adapter->dev, @@ -999,6 +1116,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, break; case HostCmd_CMD_COALESCE_CFG: break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_ret_tdls_oper(priv, resp); + break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", resp->command); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 8c351f71f72..f6395ef11a7 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -54,6 +54,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) priv->scan_block = false; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + mwifiex_disable_all_tdls_links(priv); + /* Free Tx and Rx packets, report disconnect to upper layer */ mwifiex_clean_txrx(priv); @@ -112,7 +116,7 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) adapter->tx_lock_flag = false; adapter->pps_uapsd_mode = false; - if (adapter->num_cmd_timeout && adapter->curr_cmd) + if (adapter->is_cmd_timedout && adapter->curr_cmd) return; priv->media_connected = false; dev_dbg(adapter->dev, @@ -130,6 +134,46 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) netif_carrier_off(priv->netdev); } +static int mwifiex_parse_tdls_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_tdls_generic_event *tdls_evt = + (void *)event_skb->data + sizeof(adapter->event_cause); + + /* reserved 2 bytes are not mandatory in tdls event */ + if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) - + sizeof(u16) - sizeof(adapter->event_cause))) { + dev_err(adapter->dev, "Invalid event length!\n"); + return -1; + } + + sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac); + if (!sta_ptr) { + dev_err(adapter->dev, "cannot get sta entry!\n"); + return -1; + } + + switch (le16_to_cpu(tdls_evt->type)) { + case TDLS_EVENT_LINK_TEAR_DOWN: + cfg80211_tdls_oper_request(priv->netdev, + tdls_evt->peer_mac, + NL80211_TDLS_TEARDOWN, + le16_to_cpu(tdls_evt->u.reason_code), + GFP_KERNEL); + ret = mwifiex_tdls_oper(priv, tdls_evt->peer_mac, + MWIFIEX_TDLS_DISABLE_LINK); + queue_work(adapter->workqueue, &adapter->main_work); + break; + default: + break; + } + + return ret; +} + /* * This function handles events generated by firmware. * @@ -289,9 +333,8 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_HS_ACT_REQ: dev_dbg(adapter->dev, "event: HS_ACT_REQ\n"); - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_HS_CFG_ENH, - 0, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, + 0, 0, NULL, false); break; case EVENT_MIC_ERR_UNICAST: @@ -322,27 +365,34 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_BG_SCAN_REPORT: dev_dbg(adapter->dev, "event: BGS_REPORT\n"); - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_BG_SCAN_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, false); break; case EVENT_PORT_RELEASE: dev_dbg(adapter->dev, "event: PORT RELEASE\n"); break; + case EVENT_EXT_SCAN_REPORT: + dev_dbg(adapter->dev, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + ret = mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + + break; + case EVENT_WMM_STATUS_CHANGE: dev_dbg(adapter->dev, "event: WMM status changed\n"); - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_WMM_GET_STATUS, - 0, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, + 0, 0, NULL, false); break; case EVENT_RSSI_LOW: cfg80211_cqm_rssi_notify(priv->netdev, NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, GFP_KERNEL); - mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL); + mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, false); priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; dev_dbg(adapter->dev, "event: Beacon RSSI_LOW\n"); break; @@ -356,8 +406,8 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) cfg80211_cqm_rssi_notify(priv->netdev, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, GFP_KERNEL); - mwifiex_send_cmd_async(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL); + mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, false); priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; dev_dbg(adapter->dev, "event: Beacon RSSI_HIGH\n"); break; @@ -384,15 +434,15 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) break; case EVENT_IBSS_COALESCED: dev_dbg(adapter->dev, "event: IBSS_COALESCED\n"); - ret = mwifiex_send_cmd_async(priv, + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_GET, 0, NULL); + HostCmd_ACT_GEN_GET, 0, NULL, false); break; case EVENT_ADDBA: dev_dbg(adapter->dev, "event: ADDBA Request\n"); - mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP, - HostCmd_ACT_GEN_SET, 0, - adapter->event_body); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, + adapter->event_body, false); break; case EVENT_DELBA: dev_dbg(adapter->dev, "event: DELBA Request\n"); @@ -443,10 +493,14 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) priv->csa_expire_time = jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel; - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_DEAUTHENTICATE, + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, HostCmd_ACT_GEN_SET, 0, - priv->curr_bss_params.bss_descriptor.mac_address); + priv->curr_bss_params.bss_descriptor.mac_address, + false); + break; + + case EVENT_TDLS_GENERIC_EVENT: + ret = mwifiex_parse_tdls_event(priv, adapter->event_skb); break; default: diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index f084412eee0..536c14aa71f 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -60,10 +60,12 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, int status; /* Wait for completion */ - status = wait_event_interruptible(adapter->cmd_wait_q.wait, - *(cmd_queued->condition)); - if (status) { + status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, + *(cmd_queued->condition), + (12 * HZ)); + if (status <= 0) { dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status); + mwifiex_cancel_all_pending_cmd(adapter); return status; } @@ -108,19 +110,19 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, "info: Set multicast list=%d\n", mcast_list->num_multicast_addr); /* Send multicast addresses to firmware */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list); + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + mcast_list, false); } } dev_dbg(priv->adapter->dev, "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", old_pkt_filter, priv->curr_pkt_filter); if (old_pkt_filter != priv->curr_pkt_filter) { - ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, - 0, &priv->curr_pkt_filter); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, &priv->curr_pkt_filter, false); } return ret; @@ -184,6 +186,16 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); } +void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) +{ + if (priv->adapter->dt_node) { + char txpwr[] = {"marvell,00_txpwrlimit"}; + + memcpy(&txpwr[8], priv->adapter->country_code, 2); + mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); + } +} + static int mwifiex_process_country_ie(struct mwifiex_private *priv, struct cfg80211_bss *bss) { @@ -205,6 +217,14 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, return 0; } + if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { + rcu_read_unlock(); + wiphy_dbg(priv->wdev->wiphy, + "11D: skip setting domain info in FW\n"); + return 0; + } + memcpy(priv->adapter->country_code, &country_ie[2], 2); + domain_info->country_code[0] = country_ie[2]; domain_info->country_code[1] = country_ie[3]; domain_info->country_code[2] = ' '; @@ -219,13 +239,15 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, rcu_read_unlock(); - if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { wiphy_err(priv->adapter->wiphy, "11D: setting domain info in FW\n"); return -1; } + mwifiex_dnld_txpwr_table(priv); + return 0; } @@ -270,7 +292,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (mwifiex_band_to_radio_type(bss_desc->bss_band) == HostCmd_SCAN_RADIO_TYPE_BG) - config_bands = BAND_B | BAND_G | BAND_GN | BAND_GAC; + config_bands = BAND_B | BAND_G | BAND_GN; else config_bands = BAND_A | BAND_AN | BAND_AAC; @@ -319,8 +341,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, if (bss_desc && bss_desc->ssid.ssid_len && (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. ssid, &bss_desc->ssid))) { - kfree(bss_desc); - return 0; + ret = 0; + goto done; } /* Exit Adhoc mode first */ @@ -409,16 +431,13 @@ static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, status = -1; break; } - if (cmd_type == MWIFIEX_SYNC_CMD) - status = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_HS_CFG_ENH, - HostCmd_ACT_GEN_SET, 0, - &adapter->hs_cfg); - else - status = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_HS_CFG_ENH, - HostCmd_ACT_GEN_SET, 0, - &adapter->hs_cfg); + + status = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_HS_CFG_ENH, + HostCmd_ACT_GEN_SET, 0, + &adapter->hs_cfg, + cmd_type == MWIFIEX_SYNC_CMD); + if (hs_cfg->conditions == HS_CFG_CANCEL) /* Restore previous condition */ adapter->hs_cfg.conditions = @@ -491,6 +510,9 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); hscfg.is_invoke_hostcmd = true; + adapter->hs_enabling = true; + mwifiex_cancel_all_pending_cmd(adapter); + if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, @@ -499,8 +521,9 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter) return false; } - if (wait_event_interruptible(adapter->hs_activate_wait_q, - adapter->hs_activate_wait_q_woken)) { + if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken, + (10 * HZ)) <= 0) { dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); return false; } @@ -566,8 +589,8 @@ int mwifiex_disable_auto_ds(struct mwifiex_private *priv) auto_ds.auto_ds = DEEP_SLEEP_OFF; - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); } EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds); @@ -581,8 +604,8 @@ int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate) { int ret; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, true); if (!ret) { if (priv->is_data_rate_auto) @@ -638,8 +661,9 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv, txp_cfg->mode = cpu_to_le32(1); pg_tlv = (struct mwifiex_types_power_group *) (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); - pg_tlv->type = TLV_TYPE_POWER_GROUP; - pg_tlv->length = 4 * sizeof(struct mwifiex_power_group); + pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP); + pg_tlv->length = + cpu_to_le16(4 * sizeof(struct mwifiex_power_group)); pg = (struct mwifiex_power_group *) (buf + sizeof(struct host_cmd_ds_txpwr_cfg) + sizeof(struct mwifiex_types_power_group)); @@ -677,8 +701,8 @@ int mwifiex_set_tx_power(struct mwifiex_private *priv, pg->power_max = (s8) dbm; pg->ht_bandwidth = HT_BW_40; } - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_TXPWR_CFG, - HostCmd_ACT_GEN_SET, 0, buf); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, 0, buf, true); kfree(buf); return ret; @@ -701,12 +725,11 @@ int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) else adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - sub_cmd, BITMAP_STA_PS, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + sub_cmd, BITMAP_STA_PS, NULL, true); if ((!ret) && (sub_cmd == DIS_AUTO_PS)) - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_PS_MODE_ENH, - GET_PS, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + GET_PS, 0, NULL, false); return ret; } @@ -830,9 +853,9 @@ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, - encrypt_key); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, + encrypt_key, true); } /* @@ -844,6 +867,7 @@ static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, struct mwifiex_ds_encrypt_key *encrypt_key) { + struct mwifiex_adapter *adapter = priv->adapter; int ret; struct mwifiex_wep_key *wep_key; int index; @@ -858,10 +882,17 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, /* Copy the required key as the current key */ wep_key = &priv->wep_key[index]; if (!wep_key->key_length) { - dev_err(priv->adapter->dev, + dev_err(adapter->dev, "key not set, so cannot enable it\n"); return -1; } + + if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) { + memcpy(encrypt_key->key_material, + wep_key->key_material, wep_key->key_length); + encrypt_key->key_len = wep_key->key_length; + } + priv->wep_key_curr_index = (u16) index; priv->sec_info.wep_enabled = 1; } else { @@ -876,21 +907,32 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, priv->sec_info.wep_enabled = 1; } if (wep_key->key_length) { + void *enc_key; + + if (encrypt_key->key_disable) + memset(&priv->wep_key[index], 0, + sizeof(struct mwifiex_wep_key)); + + if (adapter->fw_key_api_major_ver == FW_KEY_API_VER_MAJOR_V2) + enc_key = encrypt_key; + else + enc_key = NULL; + /* Send request to firmware */ - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, 0, NULL); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, enc_key, false); if (ret) return ret; } + if (priv->sec_info.wep_enabled) priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; else priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); return ret; } @@ -925,10 +967,9 @@ static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, */ /* Send the key as PTK to firmware */ encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; - ret = mwifiex_send_cmd_async(priv, - HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - KEY_INFO_ENABLED, encrypt_key); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, false); if (ret) return ret; @@ -952,15 +993,13 @@ static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; if (remove_key) - ret = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - !KEY_INFO_ENABLED, encrypt_key); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + !KEY_INFO_ENABLED, encrypt_key, true); else - ret = mwifiex_send_cmd_sync(priv, - HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - KEY_INFO_ENABLED, encrypt_key); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, true); return ret; } @@ -1023,19 +1062,27 @@ int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); encrypt_key.key_len = key_len; + encrypt_key.key_index = key_index; if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) encrypt_key.is_igtk_key = true; if (!disable) { - encrypt_key.key_index = key_index; if (key_len) memcpy(encrypt_key.key_material, key, key_len); + else + encrypt_key.is_current_wep_key = true; + if (mac_addr) memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); - if (kp && kp->seq && kp->seq_len) + if (kp && kp->seq && kp->seq_len) { memcpy(encrypt_key.pn, kp->seq, kp->seq_len); + encrypt_key.pn_len = kp->seq_len; + encrypt_key.is_rx_seq_valid = true; + } } else { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return 0; encrypt_key.key_disable = true; if (mac_addr) memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); @@ -1056,8 +1103,8 @@ mwifiex_get_ver_ext(struct mwifiex_private *priv) struct mwifiex_ver_ext ver_ext; memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_VERSION_EXT, - HostCmd_ACT_GEN_GET, 0, &ver_ext)) + if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, + HostCmd_ACT_GEN_GET, 0, &ver_ext, true)) return -1; return 0; @@ -1082,8 +1129,8 @@ mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, ieee80211_frequency_to_channel(chan->center_freq); roc_cfg.duration = cpu_to_le32(duration); } - if (mwifiex_send_cmd_sync(priv, HostCmd_CMD_REMAIN_ON_CHAN, - action, 0, &roc_cfg)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN, + action, 0, &roc_cfg, true)) { dev_err(priv->adapter->dev, "failed to remain on channel\n"); return -1; } @@ -1115,8 +1162,8 @@ mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role) break; } - mwifiex_send_cmd_sync(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL); + mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true); return mwifiex_sta_init_cmd(priv, false); } @@ -1131,8 +1178,8 @@ int mwifiex_get_stats_info(struct mwifiex_private *priv, struct mwifiex_ds_get_stats *log) { - return mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_GET_LOG, - HostCmd_ACT_GEN_GET, 0, log); + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, 0, log, true); } /* @@ -1174,8 +1221,7 @@ static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, return -1; } - return mwifiex_send_cmd_sync(priv, cmd_no, action, 0, reg_rw); - + return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true); } /* @@ -1240,8 +1286,8 @@ mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, rd_eeprom.byte_count = cpu_to_le16((u16) bytes); /* Send request to firmware */ - ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, - HostCmd_ACT_GEN_GET, 0, &rd_eeprom); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, + HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true); if (!ret) memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); @@ -1370,7 +1416,7 @@ static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, * with requisite parameters and calls the IOCTL handler. */ int -mwifiex_set_gen_ie(struct mwifiex_private *priv, u8 *ie, int ie_len) +mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) { struct mwifiex_ds_misc_gen_ie gen_ie; diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index bb22664923e..8b639d7fe6d 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -36,12 +36,12 @@ mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv, struct sk_buff *skb) { const struct mwifiex_arp_eth_header *arp; - struct ethhdr *eth_hdr; + struct ethhdr *eth; struct ipv6hdr *ipv6; struct icmp6hdr *icmpv6; - eth_hdr = (struct ethhdr *)skb->data; - switch (ntohs(eth_hdr->h_proto)) { + eth = (struct ethhdr *)skb->data; + switch (ntohs(eth->h_proto)) { case ETH_P_ARP: arp = (void *)(skb->data + sizeof(struct ethhdr)); if (arp->hdr.ar_op == htons(ARPOP_REPLY) || @@ -87,16 +87,22 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, struct rx_packet_hdr *rx_pkt_hdr; struct rxpd *local_rx_pd; int hdr_chop; - struct ethhdr *eth_hdr; - u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + struct ethhdr *eth; + u16 rx_pkt_off, rx_pkt_len; + u8 *offset; local_rx_pd = (struct rxpd *) (skb->data); - rx_pkt_hdr = (void *)local_rx_pd + - le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; - if (!memcmp(&rx_pkt_hdr->rfc1042_hdr, - rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) { + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { /* * Replace the 803 header and rfc1042 header (llc/snap) with an * EthernetII header, keep the src/dst and snap_type @@ -106,7 +112,7 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, * To create the Ethernet II, just move the src, dst address * right before the snap_type. */ - eth_hdr = (struct ethhdr *) + eth = (struct ethhdr *) ((u8 *) &rx_pkt_hdr->eth803_hdr + sizeof(rx_pkt_hdr->eth803_hdr) + sizeof(rx_pkt_hdr->rfc1042_hdr) @@ -114,14 +120,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); - memcpy(eth_hdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, - sizeof(eth_hdr->h_source)); - memcpy(eth_hdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, - sizeof(eth_hdr->h_dest)); + memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(eth->h_source)); + memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(eth->h_dest)); /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header that was removed. */ - hdr_chop = (u8 *) eth_hdr - (u8 *) local_rx_pd; + hdr_chop = (u8 *) eth - (u8 *) local_rx_pd; } else { /* Chop off the rxpd */ hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - @@ -139,6 +145,12 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, return 0; } + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { + offset = (u8 *)local_rx_pd + rx_pkt_off; + mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); + } + priv->rxpd_rate = local_rx_pd->rx_rate; priv->rxpd_htinfo = local_rx_pd->ht_info; @@ -171,6 +183,7 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, struct rx_packet_hdr *rx_pkt_hdr; u8 ta[ETH_ALEN]; u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; + struct mwifiex_sta_node *sta_ptr; local_rx_pd = (struct rxpd *) (skb->data); rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); @@ -185,35 +198,11 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", skb->len, rx_pkt_offset, rx_pkt_length); priv->stats.rx_dropped++; - - if (adapter->if_ops.data_complete) - adapter->if_ops.data_complete(adapter, skb); - else - dev_kfree_skb_any(skb); - + dev_kfree_skb_any(skb); 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"); @@ -225,14 +214,25 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, * If the packet is not an unicast packet then send the packet * directly to os. Don't pass thru rx reordering */ - if (!IS_11N_ENABLED(priv) || - memcmp(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN)) { + if ((!IS_11N_ENABLED(priv) && + !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || + !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { mwifiex_process_rx_packet(priv, skb); return ret; } - if (mwifiex_queuing_ra_based(priv)) { + if (mwifiex_queuing_ra_based(priv) || + (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); + if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && + local_rx_pd->priority < MAX_NUM_TID) { + sta_ptr = mwifiex_get_sta_entry(priv, ta); + if (sta_ptr) + sta_ptr->rx_seq[local_rx_pd->priority] = + le16_to_cpu(local_rx_pd->seq_num); + } } else { if (rx_pkt_type != PKT_TYPE_BAR) priv->rx_seq[local_rx_pd->priority] = seq_num; @@ -244,12 +244,8 @@ int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, ta, (u8) rx_pkt_type, skb); - if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { - if (adapter->if_ops.data_complete) - adapter->if_ops.data_complete(adapter, skb); - else - dev_kfree_skb_any(skb); - } + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + dev_kfree_skb_any(skb); if (ret) priv->stats.rx_dropped++; diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 7b581af24f5..70eb863c724 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -95,6 +95,9 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, } } + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + /* Offset of actual data */ pkt_offset = sizeof(struct txpd) + pad; if (pkt_type == PKT_TYPE_MGMT) { @@ -125,6 +128,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) { struct mwifiex_adapter *adapter = priv->adapter; struct txpd *local_tx_pd; + struct mwifiex_tx_param tx_param; /* sizeof(struct txpd) + Interface specific header */ #define NULL_PACKET_HDR 64 u32 data_len = NULL_PACKET_HDR; @@ -146,8 +150,10 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) return -1; tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN); skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); skb_push(skb, sizeof(struct txpd)); @@ -164,8 +170,9 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) skb, NULL); } else { skb_push(skb, INTF_HEADER_LEN); + tx_param.next_pkt_len = 0; ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb, NULL); + skb, &tx_param); } switch (ret) { case -EBUSY: diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c new file mode 100644 index 00000000000..0e88364e0c6 --- /dev/null +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -0,0 +1,1103 @@ +/* Marvell Wireless LAN device driver: TDLS handling + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" +#include "11ac.h" + +#define TDLS_REQ_FIX_LEN 6 +#define TDLS_RESP_FIX_LEN 8 +#define TDLS_CONFIRM_FIX_LEN 6 + +static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, + const u8 *mac, u8 status) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *tid_list; + struct sk_buff *skb, *tmp; + struct mwifiex_txinfo *tx_info; + unsigned long flags; + u32 tid; + u8 tid_down; + + dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + + __skb_unlink(skb, &priv->tdls_txq); + tx_info = MWIFIEX_SKB_TXCB(skb); + tid = skb->priority; + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + + if (status == TDLS_SETUP_COMPLETE) { + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); + ra_list->tdls_link = true; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + } else { + tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(tid_list)) + ra_list = list_first_entry(tid_list, + struct mwifiex_ra_list_tbl, list); + else + ra_list = NULL; + tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; + } + + if (!ra_list) { + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + continue; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + tos_to_tid_inv[tid_down]); + + atomic_inc(&priv->wmm.tx_pkts_queued); + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, + const u8 *mac) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *ra_list_head; + struct sk_buff *skb, *tmp; + unsigned long flags; + int i; + + dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + list_for_each_entry(ra_list, ra_list_head, list) { + skb_queue_walk_safe(&ra_list->skb_head, skb, + tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + __skb_unlink(skb, &ra_list->skb_head); + atomic_dec(&priv->wmm.tx_pkts_queued); + ra_list->total_pkt_count--; + skb_queue_tail(&priv->tdls_txq, skb); + } + } + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; + u16 rates_size, supp_rates_size, ext_rates_size; + + memset(rates, 0, sizeof(rates)); + rates_size = mwifiex_get_supported_rates(priv, rates); + + supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); + + if (skb_tailroom(skb) < rates_size + 4) { + dev_err(priv->adapter->dev, + "Insuffient space while adding rates\n"); + return -ENOMEM; + } + + pos = skb_put(skb, supp_rates_size + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = supp_rates_size; + memcpy(pos, rates, supp_rates_size); + + if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { + ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; + pos = skb_put(skb, ext_rates_size + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = ext_rates_size; + memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, + ext_rates_size); + } + + return 0; +} + +static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_assoc_rsp *assoc_rsp; + u8 *pos; + + assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; + pos = (void *)skb_put(skb, 4); + *pos++ = WLAN_EID_AID; + *pos++ = 2; + *pos++ = le16_to_cpu(assoc_rsp->a_id); + + return; +} + +static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee80211_vht_cap vht_cap; + u8 *pos; + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); + *pos++ = WLAN_EID_VHT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_vht_cap); + + memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); + + mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); + memcpy(pos, &vht_cap, sizeof(vht_cap)); + + return 0; +} + +static int +mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, + u8 vht_enabled, struct sk_buff *skb) +{ + struct ieee80211_ht_operation *ht_oper; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_bssdescriptor *bss_desc = + &priv->curr_bss_params.bss_descriptor; + u8 *pos; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + dev_warn(priv->adapter->dev, + "TDLS peer station not found in list\n"); + return -1; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(struct ieee80211_ht_operation); + ht_oper = (void *)pos; + + ht_oper->primary_chan = bss_desc->channel; + + /* follow AP's channel bandwidth */ + if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && + bss_desc->bcn_ht_cap && + ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) + ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; + + if (vht_enabled) { + ht_oper->ht_param = + mwifiex_get_sec_chan_offset(bss_desc->channel); + ht_oper->ht_param |= BIT(2); + } + + memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, + sizeof(struct ieee80211_ht_operation)); + + return 0; +} + +static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, + const u8 *mac, struct sk_buff *skb) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set, peer_supp_chwd_set; + u8 *pos, ap_supp_chwd_set, chan_bw; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss; + u32 usr_vht_cap_info; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + dev_warn(adapter->dev, "TDLS peer station not found in list\n"); + return -1; + } + + if (!mwifiex_is_bss_in_11ac_mode(priv)) { + if (sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + dev_dbg(adapter->dev, + "TDLS peer doesn't support wider bandwitdh\n"); + return 0; + } + } else { + ap_vht_cap = bss_desc->bcn_vht_cap; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(struct ieee80211_vht_operation); + vht_oper = (struct ieee80211_vht_operation *)pos; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the minmum bandwith between AP/TDLS peers */ + vht_cap = &sta_ptr->tdls_cap.vhtcap; + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + peer_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); + + /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ + + if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + ap_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); + } + + switch (supp_chwd_set) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); + mcs_map_result = 0; + + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); + + if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min_t(u16, mcs_user, mcs_resp)); + } + + vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); + + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + default: + chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + vht_oper->center_freq_seg1_idx = + mwifiex_get_center_freq_index(priv, BAND_AAC, + bss_desc->channel, + chan_bw); + + return 0; +} + +static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_extcap *extcap; + + extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); + extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; + extcap->ieee_hdr.len = 8; + memset(extcap->ext_capab, 0, 8); + extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; + + if (priv->adapter->is_hw_11ac_capable) + extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; +} + +static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 3); + + *pos++ = WLAN_EID_QOS_CAPA; + *pos++ = 1; + *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; +} + +static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, + const u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_tdls_data *tf; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, priv->curr_addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + break; + + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + break; + + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } else { + ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } + break; + + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + dev_err(priv->adapter->dev, "Unknown TDLS frame type.\n"); + return -EINVAL; + } + + return 0; +} + +static void +mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - + sizeof(struct ieee_types_header); + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + struct timeval tv; + int ret; + u16 skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + 3 + /* Qos Info */ + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + extra_ies_len; + + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + + skb = dev_alloc_skb(skb_len); + if (!skb) { + dev_err(priv->adapter->dev, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, + priv->cfg_bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + break; + } + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb->priority = MWIFIEX_PRIO_BK; + break; + default: + skb->priority = MWIFIEX_PRIO_VI; + break; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +static int +mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, + const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); + + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + /* add address 4 */ + pos = skb_put(skb, ETH_ALEN); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(capab); + /* move back for addr4 */ + memmove(pos + ETH_ALEN, &mgmt->u.action.category, + sizeof(mgmt->u.action.u.tdls_discover_resp)); + /* init address 4 */ + memcpy(pos, bc_addr, ETH_ALEN); + + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + break; + default: + dev_err(priv->adapter->dev, "Unknown TDLS action frame type\n"); + return -EINVAL; + } + + return 0; +} + +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + struct timeval tv; + u8 *pos; + u32 pkt_type, tx_control; + u16 pkt_len, skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + extra_ies_len + + 3 + /* Qos Info */ + ETH_ALEN; /* Address4 */ + + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + + skb = dev_alloc_skb(skb_len); + if (!skb) { + dev_err(priv->adapter->dev, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = PKT_TYPE_MGMT; + tx_control = 0; + pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(pos, &pkt_type, sizeof(pkt_type)); + memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); + + if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + skb)) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last we are the responder */ + + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + + skb->priority = MWIFIEX_PRIO_VI; + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + + pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); + memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, + sizeof(pkt_len)); + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +/* This function process tdls action frame from peer. + * Peer capabilities are stored into station node structure. + */ +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len) +{ + struct mwifiex_sta_node *sta_ptr; + u8 *peer, *pos, *end; + u8 i, action, basic; + int ie_len = 0; + + if (len < (sizeof(struct ethhdr) + 3)) + return; + if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) + return; + if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) + return; + + peer = buf + ETH_ALEN; + action = *(buf + sizeof(struct ethhdr) + 2); + + /* just handle TDLS setup request/response/confirm */ + if (action > WLAN_TDLS_SETUP_CONFIRM) + return; + + dev_dbg(priv->adapter->dev, + "rx:tdls action: peer=%pM, action=%d\n", peer, action); + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return; + + switch (action) { + case WLAN_TDLS_SETUP_REQUEST: + if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) + return; + + pos = buf + sizeof(struct ethhdr) + 4; + /* payload 1+ category 1 + action 1 + dialog 1 */ + sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_RESPONSE: + if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) + return; + /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ + pos = buf + sizeof(struct ethhdr) + 6; + sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_CONFIRM: + if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) + return; + pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; + ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; + break; + default: + dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n"); + return; + } + + for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos + 2 + pos[1] > end) + break; + + switch (*pos) { + case WLAN_EID_SUPP_RATES: + sta_ptr->tdls_cap.rates_len = pos[1]; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[i] = pos[i + 2]; + break; + + case WLAN_EID_EXT_SUPP_RATES: + basic = sta_ptr->tdls_cap.rates_len; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; + sta_ptr->tdls_cap.rates_len += pos[1]; + break; + case WLAN_EID_HT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, + sizeof(struct ieee80211_ht_cap)); + sta_ptr->is_11n_enabled = 1; + break; + case WLAN_EID_HT_OPERATION: + memcpy(&sta_ptr->tdls_cap.ht_oper, pos, + sizeof(struct ieee80211_ht_operation)); + break; + case WLAN_EID_BSS_COEX_2040: + sta_ptr->tdls_cap.coex_2040 = pos[2]; + break; + case WLAN_EID_EXT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, + sizeof(struct ieee_types_header) + + min_t(u8, pos[1], 8)); + break; + case WLAN_EID_RSN: + memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, + sizeof(struct ieee_types_header) + pos[1]); + break; + case WLAN_EID_QOS_CAPA: + sta_ptr->tdls_cap.qos_info = pos[2]; + break; + case WLAN_EID_VHT_OPERATION: + if (priv->adapter->is_hw_11ac_capable) + memcpy(&sta_ptr->tdls_cap.vhtoper, pos, + sizeof(struct ieee80211_vht_operation)); + break; + case WLAN_EID_VHT_CAPABILITY: + if (priv->adapter->is_hw_11ac_capable) { + memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, + sizeof(struct ieee80211_vht_cap)); + sta_ptr->is_11ac_enabled = 1; + } + break; + case WLAN_EID_AID: + if (priv->adapter->is_hw_11ac_capable) + sta_ptr->tdls_cap.aid = + le16_to_cpu(*(__le16 *)(pos + 2)); + default: + break; + } + } + + return; +} + +static int +mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { + dev_err(priv->adapter->dev, + "link absent for peer %pM; cannot config\n", peer); + return -EINVAL; + } + + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { + dev_dbg(priv->adapter->dev, + "Setup already in progress for peer %pM\n", peer); + return 0; + } + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return -ENOMEM; + + sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; + mwifiex_hold_tdls_packets(priv, peer); + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr) { + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + mwifiex_del_sta_entry(priv, peer); + } + + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct ieee80211_mcs_info mcs; + unsigned long flags; + int i; + + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { + dev_dbg(priv->adapter->dev, + "tdls: enable link %pM success\n", peer); + + sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; + + mcs = sta_ptr->tdls_cap.ht_capb.mcs; + if (mcs.rx_mask[0] != 0xff) + sta_ptr->is_11n_enabled = true; + if (sta_ptr->is_11n_enabled) { + if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU) + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_4K; + + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + } else { + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + + memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); + } else { + dev_dbg(priv->adapter->dev, + "tdls: enable link %pM failed\n", peer); + if (sta_ptr) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_del_sta_entry(priv, peer); + } + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); + + return -1; + } + + return 0; +} + +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) +{ + switch (action) { + case MWIFIEX_TDLS_ENABLE_LINK: + return mwifiex_tdls_process_enable_link(priv, peer); + case MWIFIEX_TDLS_DISABLE_LINK: + return mwifiex_tdls_process_disable_link(priv, peer); + case MWIFIEX_TDLS_CREATE_LINK: + return mwifiex_tdls_process_create_link(priv, peer); + case MWIFIEX_TDLS_CONFIG_LINK: + return mwifiex_tdls_process_config_link(priv, peer); + } + return 0; +} + +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *sta_ptr; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (sta_ptr) + return sta_ptr->tdls_status; + + return TDLS_NOT_SETUP; +} + +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + if (list_empty(&priv->sta_list)) + return; + + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + + mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, + TDLS_LINK_TEARDOWN); + memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) + dev_warn(priv->adapter->dev, + "Disable link failed for TDLS peer %pM", + sta_ptr->mac_addr); + } + + mwifiex_del_all_sta_list(priv); +} diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 8f923d0d2ba..fd7e5b9b458 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -40,6 +40,7 @@ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); struct rxpd *local_rx_pd; struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); + int ret; local_rx_pd = (struct rxpd *) (skb->data); /* Get the BSS number from rxpd, get corresponding priv */ @@ -54,13 +55,20 @@ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, return -1; } + memset(rx_info, 0, sizeof(*rx_info)); rx_info->bss_num = priv->bss_num; rx_info->bss_type = priv->bss_type; if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - return mwifiex_process_uap_rx_packet(priv, skb); + ret = mwifiex_process_uap_rx_packet(priv, skb); + else + ret = mwifiex_process_sta_rx_packet(priv, skb); + + /* Decrement RX pending counter for each packet */ + if (adapter->if_ops.data_complete) + adapter->if_ops.data_complete(adapter); - return mwifiex_process_sta_rx_packet(priv, skb); + return ret; } EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); @@ -105,7 +113,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, switch (ret) { case -ENOSR: - dev_err(adapter->dev, "data: -ENOSR is returned\n"); + dev_dbg(adapter->dev, "data: -ENOSR is returned\n"); break; case -EBUSY: if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && @@ -168,7 +176,7 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, mwifiex_set_trans_start(priv->netdev); if (!status) { priv->stats.tx_packets++; - priv->stats.tx_bytes += skb->len; + priv->stats.tx_bytes += tx_info->pkt_len; if (priv->tx_timeout_cnt) priv->tx_timeout_cnt = 0; } else { diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 64424c81b44..32643555dd2 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,27 @@ 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 */ + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + break; + case MWIFIEX_RX_STBC12: /* fall through */ + case MWIFIEX_RX_STBC123: + /* HT_CAP 2X2 mode */ + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; + break; + default: + dev_warn(priv->adapter->dev, + "Unsupported RX-STBC, default to 2x2\n"); + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; + break; + } priv->ap_11n_enabled = 1; } else { memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); @@ -226,8 +248,8 @@ void mwifiex_set_vht_width(struct mwifiex_private *priv, if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80) vht_cfg.misc_config |= VHT_BW_80_160_80P80; - mwifiex_send_cmd_sync(priv, HostCmd_CMD_11AC_CFG, - HostCmd_ACT_GEN_SET, 0, &vht_cfg); + mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG, + HostCmd_ACT_GEN_SET, 0, &vht_cfg, true); return; } diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index 718066577c6..92e77a398ec 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -21,126 +21,8 @@ #include "main.h" #include "11n.h" -/* - * This function will return the pointer to station entry in station list - * table which matches specified mac address. - * This function should be called after acquiring RA list spinlock. - * NULL is returned if station entry is not found in associated STA list. - */ -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, u8 *mac) -{ - struct mwifiex_sta_node *node; - - if (!mac) - return NULL; - - list_for_each_entry(node, &priv->sta_list, list) { - if (!memcmp(node->mac_addr, mac, ETH_ALEN)) - return node; - } - - return NULL; -} - -/* - * This function will add a sta_node entry to associated station list - * table with the given mac address. - * If entry exist already, existing entry is returned. - * If received mac address is NULL, NULL is returned. - */ -static struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - if (!mac) - return NULL; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, mac); - if (node) - goto done; - - node = kzalloc(sizeof(struct mwifiex_sta_node), GFP_ATOMIC); - if (!node) - goto done; - - memcpy(node->mac_addr, mac, ETH_ALEN); - list_add_tail(&node->list, &priv->sta_list); - -done: - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return node; -} - -/* - * This function will search for HT IE in association request IEs - * and set station HT parameters accordingly. - */ -static void -mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, - int ies_len, struct mwifiex_sta_node *node) -{ - const struct ieee80211_ht_cap *ht_cap; - - if (!ies) - return; - ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); - if (ht_cap) { - node->is_11n_enabled = 1; - node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & - IEEE80211_HT_CAP_MAX_AMSDU ? - MWIFIEX_TX_DATA_BUF_SIZE_8K : - MWIFIEX_TX_DATA_BUF_SIZE_4K; - } else { - node->is_11n_enabled = 0; - } - return; -} - -/* - * This function will delete a station entry from station list - */ -static void mwifiex_del_sta_entry(struct mwifiex_private *priv, u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - node = mwifiex_get_sta_entry(priv, mac); - if (node) { - list_del(&node->list); - kfree(node); - } - - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} - -/* - * This function will delete all stations from associated station list. - */ -static void mwifiex_del_all_sta_list(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *node, *tmp; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { - list_del(&node->list); - kfree(node); - } - - INIT_LIST_HEAD(&priv->sta_list); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} /* * This function handles AP interface specific events generated by firmware. @@ -268,9 +150,9 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) case EVENT_ADDBA: dev_dbg(adapter->dev, "event: ADDBA Request\n"); if (priv->media_connected) - mwifiex_send_cmd_async(priv, HostCmd_CMD_11N_ADDBA_RSP, - HostCmd_ACT_GEN_SET, 0, - adapter->event_body); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, + adapter->event_body, false); break; case EVENT_DELBA: dev_dbg(adapter->dev, "event: DELBA Request\n"); @@ -284,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 1cfe5a738c4..b0601b91cc4 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -97,7 +97,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, struct mwifiex_txinfo *tx_info; int hdr_chop; struct timeval tv; - u8 rfc1042_eth_hdr[ETH_ALEN] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + struct ethhdr *p_ethhdr; uap_rx_pd = (struct uap_rxpd *)(skb->data); rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); @@ -111,17 +111,43 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, return; } - if (!memcmp(&rx_pkt_hdr->rfc1042_hdr, - rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr))) + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { + /* Replace the 803 header and rfc1042 header (llc/snap) with + * an Ethernet II header, keep the src/dst and snap_type + * (ethertype). + * + * The firmware only passes up SNAP frames converting all RX + * data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *)(&rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(p_ethhdr->h_dest)); /* Chop off the rxpd + the excess memory from * 802.2/llc/snap header that was removed. */ - hdr_chop = (u8 *)eth_hdr - (u8 *)uap_rx_pd; - else + hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd; + } else { /* Chop off the rxpd */ hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; + } - /* Chop off the leading header bytes so the it points + /* Chop off the leading header bytes so that it points * to the start of either the reconstructed EthII frame * or the 802.2/llc/snap frame. */ @@ -149,10 +175,24 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; + if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) { + /* Update bridge packet statistics as the + * packet is not going to kernel/upper layer. + */ + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + /* Sending bridge packet to TX queue, so save the packet + * length in TXCB to update statistics in TX complete. + */ + tx_info->pkt_len = skb->len; + } + do_gettimeofday(&tv); skb->tstamp = timeval_to_ktime(tv); mwifiex_wmm_add_buf_txqueue(priv, skb); @@ -241,36 +281,11 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), le16_to_cpu(uap_rx_pd->rx_pkt_length)); priv->stats.rx_dropped++; - - if (adapter->if_ops.data_complete) - adapter->if_ops.data_complete(adapter, skb); - else - dev_kfree_skb_any(skb); - + dev_kfree_skb_any(skb); 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"); @@ -300,12 +315,8 @@ int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, uap_rx_pd->priority, ta, pkt_type, skb); - if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { - if (adapter->if_ops.data_complete) - adapter->if_ops.data_complete(adapter, skb); - else - dev_kfree_skb_any(skb); - } + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + dev_kfree_skb_any(skb); if (ret) priv->stats.rx_dropped++; diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 1c70b8d0922..a8ce8130cfa 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -22,15 +22,19 @@ #define USB_VERSION "1.0" -static const char usbdriver_name[] = "usb8797"; - +static u8 user_rmmod; static struct mwifiex_if_ops usb_ops; static struct semaphore add_remove_card_sem; -static struct usb_card_rec *usb_card; static struct usb_device_id mwifiex_usb_table[] = { - {USB_DEVICE(USB8797_VID, USB8797_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8797_VID, USB8797_PID_2, + /* 8797 */ + {USB_DEVICE(USB8XXX_VID, USB8797_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8897 */ + {USB_DEVICE(USB8XXX_VID, USB8897_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8897_PID_2, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0xff)}, { } /* Terminating entry */ @@ -343,14 +347,23 @@ static int mwifiex_usb_probe(struct usb_interface *intf, id_vendor, id_product, bcd_device); /* PID_1 is used for firmware downloading only */ - if (id_product == USB8797_PID_1) - card->usb_boot_state = USB8797_FW_DNLD; - else - card->usb_boot_state = USB8797_FW_READY; + switch (id_product) { + case USB8797_PID_1: + case USB8897_PID_1: + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + case USB8797_PID_2: + case USB8897_PID_2: + card->usb_boot_state = USB8XXX_FW_READY; + break; + default: + pr_warning("unknown id_product %#x\n", id_product); + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + } card->udev = udev; card->intf = intf; - usb_card = card; pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, @@ -446,6 +459,7 @@ static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) * 'suspended' state and a 'disconnect' one. */ adapter->is_suspended = true; + adapter->hs_enabling = false; if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) usb_kill_urb(card->rx_cmd.urb); @@ -512,13 +526,6 @@ static int mwifiex_usb_resume(struct usb_interface *intf) MWIFIEX_BSS_ROLE_ANY), MWIFIEX_ASYNC_CMD); -#ifdef CONFIG_PM - /* Resume handler may be called due to remote wakeup, - * force to exit suspend anyway - */ - usb_disable_autosuspend(card->udev); -#endif /* CONFIG_PM */ - return 0; } @@ -536,6 +543,19 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) if (!adapter->priv_num) return; + if (user_rmmod) { +#ifdef CONFIG_PM + if (adapter->is_suspended) + mwifiex_usb_resume(intf); +#endif + + mwifiex_deauthenticate_all(adapter); + + mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_FUNC_SHUTDOWN); + } + mwifiex_usb_free(card); dev_dbg(adapter->dev, "%s: removing card\n", __func__); @@ -549,13 +569,13 @@ static void mwifiex_usb_disconnect(struct usb_interface *intf) } static struct usb_driver mwifiex_usb_driver = { - .name = usbdriver_name, + .name = "mwifiex_usb", .probe = mwifiex_usb_probe, .disconnect = mwifiex_usb_disconnect, .id_table = mwifiex_usb_table, .suspend = mwifiex_usb_suspend, .resume = mwifiex_usb_resume, - .supports_autosuspend = 1, + .soft_unbind = 1, }; static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) @@ -753,7 +773,20 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) card->adapter = adapter; adapter->dev = &card->udev->dev; - strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); + + switch (le16_to_cpu(card->udev->descriptor.idProduct)) { + case USB8897_PID_1: + case USB8897_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; + strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME); + break; + case USB8797_PID_1: + case USB8797_PID_2: + default: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); + break; + } return 0; } @@ -762,7 +795,7 @@ static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) { struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - usb_set_intfdata(card->intf, NULL); + card->adapter = NULL; } static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, @@ -770,7 +803,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, { int ret = 0; u8 *firmware = fw->fw_buf, *recv_buff; - u32 retries = USB8797_FW_MAX_RETRY, dlen; + u32 retries = USB8XXX_FW_MAX_RETRY, dlen; u32 fw_seqnum = 0, tlen = 0, dnld_cmd = 0; struct fw_data *fwdata; struct fw_sync_header sync_fw; @@ -872,7 +905,7 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, continue; } - retries = USB8797_FW_MAX_RETRY; + retries = USB8XXX_FW_MAX_RETRY; break; } fw_seqnum++; @@ -896,13 +929,13 @@ static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, int ret; struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - if (card->usb_boot_state == USB8797_FW_DNLD) { + if (card->usb_boot_state == USB8XXX_FW_DNLD) { ret = mwifiex_prog_fw_w_helper(adapter, fw); if (ret) return -1; /* Boot state changes after successful firmware download */ - if (card->usb_boot_state == USB8797_FW_DNLD) + if (card->usb_boot_state == USB8XXX_FW_DNLD) return -1; } @@ -935,11 +968,9 @@ static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, return 0; } -static int mwifiex_usb_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) +static int mwifiex_usb_data_complete(struct mwifiex_adapter *adapter) { atomic_dec(&adapter->rx_pending); - dev_kfree_skb_any(skb); return 0; } @@ -1004,29 +1035,8 @@ static void mwifiex_usb_cleanup_module(void) if (!down_interruptible(&add_remove_card_sem)) up(&add_remove_card_sem); - if (usb_card) { - struct mwifiex_adapter *adapter = usb_card->adapter; - int i; - - /* In case driver is removed when asynchronous FW downloading is - * in progress - */ - wait_for_completion(&adapter->fw_load); - -#ifdef CONFIG_PM - if (adapter->is_suspended) - mwifiex_usb_resume(usb_card->intf); -#endif - for (i = 0; i < adapter->priv_num; i++) - if ((GET_BSS_ROLE(adapter->priv[i]) == - MWIFIEX_BSS_ROLE_STA) && - adapter->priv[i]->media_connected) - mwifiex_deauthenticate(adapter->priv[i], NULL); - - mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - MWIFIEX_FUNC_SHUTDOWN); - } + /* set the flag as user is removing this module */ + user_rmmod = 1; usb_deregister(&mwifiex_usb_driver); } @@ -1038,4 +1048,5 @@ MODULE_AUTHOR("Marvell International Ltd."); MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION); MODULE_VERSION(USB_VERSION); MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE("mrvl/usb8797_uapsta.bin"); +MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index 98c4316cd1a..15b73d12e99 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -22,19 +22,23 @@ #include <linux/usb.h> -#define USB8797_VID 0x1286 +#define USB8XXX_VID 0x1286 + #define USB8797_PID_1 0x2043 #define USB8797_PID_2 0x2044 +#define USB8897_PID_1 0x2045 +#define USB8897_PID_2 0x2046 -#define USB8797_FW_DNLD 1 -#define USB8797_FW_READY 2 -#define USB8797_FW_MAX_RETRY 3 +#define USB8XXX_FW_DNLD 1 +#define USB8XXX_FW_READY 2 +#define USB8XXX_FW_MAX_RETRY 3 #define MWIFIEX_TX_DATA_URB 6 #define MWIFIEX_RX_DATA_URB 6 #define MWIFIEX_USB_TIMEOUT 100 #define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" +#define USB8897_DEFAULT_FW_NAME "mrvl/usb8897_uapsta.bin" #define FW_DNLD_TX_BUF_SIZE 620 #define FW_DNLD_RX_BUF_SIZE 2048 diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index 5d9e150f411..6da5abf52e6 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -72,7 +72,7 @@ int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, return -1; } - return mwifiex_send_cmd_sync(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL); + return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true); } EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); @@ -104,6 +104,7 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; info->is_hs_configured = adapter->is_hs_configured; info->hs_activated = adapter->hs_activated; + info->is_cmd_timedout = adapter->is_cmd_timedout; info->num_cmd_host_to_card_failure = adapter->dbg.num_cmd_host_to_card_failure; info->num_cmd_sleep_cfm_host_to_card_failure @@ -119,7 +120,6 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, info->num_cmd_assoc_failure = adapter->dbg.num_cmd_assoc_failure; info->num_tx_timeout = adapter->dbg.num_tx_timeout; - info->num_cmd_timeout = adapter->dbg.num_cmd_timeout; info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, @@ -191,6 +191,9 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) if (!skb) return -1; + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + skb->dev = priv->netdev; skb->protocol = eth_type_trans(skb, priv->netdev); skb->ip_summed = CHECKSUM_NONE; @@ -217,8 +220,6 @@ int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); - priv->stats.rx_bytes += skb->len; - priv->stats.rx_packets++; if (in_interrupt()) netif_rx(skb); else @@ -251,3 +252,117 @@ int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, return 0; } + +/* This function will return the pointer to station entry in station list + * table which matches specified mac address. + * This function should be called after acquiring RA list spinlock. + * NULL is returned if station entry is not found in associated STA list. + */ +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + + if (!mac) + return NULL; + + list_for_each_entry(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) + return node; + } + + return NULL; +} + +/* This function will add a sta_node entry to associated station list + * table with the given mac address. + * If entry exist already, existing entry is returned. + * If received mac address is NULL, NULL is returned. + */ +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + if (!mac) + return NULL; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, mac); + if (node) + goto done; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + goto done; + + memcpy(node->mac_addr, mac, ETH_ALEN); + list_add_tail(&node->list, &priv->sta_list); + +done: + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return node; +} + +/* This function will search for HT IE in association request IEs + * and set station HT parameters accordingly. + */ +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node) +{ + const struct ieee80211_ht_cap *ht_cap; + + if (!ies) + return; + + ht_cap = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, ies_len); + if (ht_cap) { + node->is_11n_enabled = 1; + node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU ? + MWIFIEX_TX_DATA_BUF_SIZE_8K : + MWIFIEX_TX_DATA_BUF_SIZE_4K; + } else { + node->is_11n_enabled = 0; + } + + return; +} + +/* This function will delete a station entry from station list */ +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + node = mwifiex_get_sta_entry(priv, mac); + if (node) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} + +/* This function will delete all stations from associated station list. */ +void mwifiex_del_all_sta_list(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *node, *tmp; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { + list_del(&node->list); + kfree(node); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index cb2d0582bd3..caadb3737b9 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -20,18 +20,57 @@ #ifndef _MWIFIEX_UTIL_H_ #define _MWIFIEX_UTIL_H_ +struct mwifiex_dma_mapping { + dma_addr_t addr; + size_t len; +}; + +struct mwifiex_cb { + struct mwifiex_dma_mapping dma_mapping; + union { + struct mwifiex_rxinfo rx_info; + struct mwifiex_txinfo tx_info; + }; +}; + static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) { - return (struct mwifiex_rxinfo *)(skb->cb + sizeof(dma_addr_t)); + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb)); + return &cb->rx_info; } static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) { - return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t)); + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + return &cb->tx_info; } -static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, dma_addr_t *buf_pa) +static inline void mwifiex_store_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) { - memcpy(buf_pa, skb->cb, sizeof(dma_addr_t)); + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); } + +static inline void mwifiex_get_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); +} + +static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) +{ + struct mwifiex_dma_mapping mapping; + + mwifiex_get_mapping(skb, &mapping); + + return mapping.addr; +} + #endif /* !_MWIFIEX_UTIL_H_ */ diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 95fa3599b40..d3671d009f6 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, @@ -64,21 +64,6 @@ static u8 tos_to_tid[] = { 0x07 /* 1 1 1 AC_VO */ }; -/* - * This table inverses the tos_to_tid operation to get a priority - * which is in sequential order, and can be compared. - * Use this to compare the priority of two different TIDs. - */ -static u8 tos_to_tid_inv[] = { - 0x02, /* from tos_to_tid[2] = 0 */ - 0x00, /* from tos_to_tid[0] = 1 */ - 0x01, /* from tos_to_tid[1] = 2 */ - 0x03, - 0x04, - 0x05, - 0x06, - 0x07}; - static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; /* @@ -107,7 +92,7 @@ mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) * The function also initializes the list with the provided RA. */ static struct mwifiex_ra_list_tbl * -mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, u8 *ra) +mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) { struct mwifiex_ra_list_tbl *ra_list; @@ -154,8 +139,7 @@ static u8 mwifiex_get_random_ba_threshold(void) * This function allocates and adds a RA list for all TIDs * with the given RA. */ -void -mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) { int i; struct mwifiex_ra_list_tbl *ra_list; @@ -175,8 +159,16 @@ mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra) break; ra_list->is_11n_enabled = 0; + ra_list->tdls_link = false; if (!mwifiex_queuing_ra_based(priv)) { - ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + if (mwifiex_get_tdls_link_status(priv, ra) == + TDLS_SETUP_COMPLETE) { + ra_list->tdls_link = true; + ra_list->is_11n_enabled = + mwifiex_tdls_peer_11n_enabled(priv, ra); + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + } } else { ra_list->is_11n_enabled = mwifiex_is_sta_11n_enabled(priv, node); @@ -213,8 +205,9 @@ static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) * This function map ACs to TIDs. */ static void -mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm) +mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) { + struct mwifiex_wmm_desc *wmm = &priv->wmm; u8 *queue_priority = wmm->queue_priority; int i; @@ -224,7 +217,7 @@ mwifiex_wmm_queue_priorities_tid(struct mwifiex_wmm_desc *wmm) } for (i = 0; i < MAX_NUM_TID; ++i) - tos_to_tid_inv[tos_to_tid[i]] = (u8)i; + priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); } @@ -285,7 +278,7 @@ mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, } } - mwifiex_wmm_queue_priorities_tid(&priv->wmm); + mwifiex_wmm_queue_priorities_tid(priv); } /* @@ -388,8 +381,7 @@ mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) * AP is disabled (due to call admission control (ACM bit). Mapping * of TID to AC is taken care of internally. */ -static u8 -mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) { enum mwifiex_wmm_ac_e ac, ac_down; u8 new_tid; @@ -421,20 +413,19 @@ mwifiex_wmm_init(struct mwifiex_adapter *adapter) continue; for (i = 0; i < MAX_NUM_TID; ++i) { - priv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; - priv->aggr_prio_tbl[i].ampdu_ap = tos_to_tid_inv[i]; - priv->aggr_prio_tbl[i].ampdu_user = 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 = + priv->tos_to_tid_inv[i]; } - priv->aggr_prio_tbl[6].amsdu - = priv->aggr_prio_tbl[6].ampdu_ap - = priv->aggr_prio_tbl[6].ampdu_user - = BA_STREAM_NOT_ALLOWED; - - priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap - = priv->aggr_prio_tbl[7].ampdu_user - = BA_STREAM_NOT_ALLOWED; - mwifiex_set_ba_params(priv); mwifiex_reset_11n_rx_seq_num(priv); @@ -546,6 +537,7 @@ void mwifiex_clean_txrx(struct mwifiex_private *priv) { unsigned long flags; + struct sk_buff *skb, *tmp; mwifiex_11n_cleanup_reorder_tbl(priv); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); @@ -559,9 +551,13 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) mwifiex_wmm_delete_all_ralist(priv); memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); - if (priv->adapter->if_ops.clean_pcie_ring) + if (priv->adapter->if_ops.clean_pcie_ring && + !priv->adapter->surprise_removed) priv->adapter->if_ops.clean_pcie_ring(priv->adapter); spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); } /* @@ -570,7 +566,7 @@ mwifiex_clean_txrx(struct mwifiex_private *priv) */ static struct mwifiex_ra_list_tbl * mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, - u8 *ra_addr) + const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; @@ -590,8 +586,9 @@ mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, * If no such node is found, a new node is added first and then * retrieved. */ -static struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, u8 *ra_addr) +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) { struct mwifiex_ra_list_tbl *ra_list; @@ -640,6 +637,21 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list; u8 ra[ETH_ALEN], tid_down; unsigned long flags; + struct list_head list_head; + int tdls_status = TDLS_NOT_SETUP; + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + + memcpy(ra, eth_hdr->h_dest, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { + if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) + dev_dbg(adapter->dev, + "TDLS setup packet for %pM. Don't block\n", ra); + else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) + tdls_status = mwifiex_get_tdls_link_status(priv, ra); + } if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); @@ -658,12 +670,27 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, have only 1 raptr for a tid in case of infra */ if (!mwifiex_queuing_ra_based(priv) && !mwifiex_is_skb_mgmt_frame(skb)) { - if (!list_empty(&priv->wmm.tid_tbl_ptr[tid_down].ra_list)) - ra_list = list_first_entry( - &priv->wmm.tid_tbl_ptr[tid_down].ra_list, - struct mwifiex_ra_list_tbl, list); - else - ra_list = NULL; + switch (tdls_status) { + case TDLS_SETUP_COMPLETE: + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, + ra); + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + break; + case TDLS_SETUP_INPROGRESS: + skb_queue_tail(&priv->tdls_txq, skb); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + return; + default: + list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(&list_head)) + ra_list = list_first_entry( + &list_head, struct mwifiex_ra_list_tbl, + list); + else + ra_list = NULL; + break; + } } else { memcpy(ra, skb->data, ETH_ALEN); if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) @@ -683,9 +710,9 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, ra_list->total_pkt_count++; if (atomic_read(&priv->wmm.highest_queued_prio) < - tos_to_tid_inv[tid_down]) + priv->tos_to_tid_inv[tid_down]) atomic_set(&priv->wmm.highest_queued_prio, - tos_to_tid_inv[tid_down]); + priv->tos_to_tid_inv[tid_down]); atomic_inc(&priv->wmm.tx_pkts_queued); @@ -708,7 +735,7 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, { u8 *curr = (u8 *) &resp->params.get_wmm_status; uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; - int valid = true; + bool valid = true; struct mwifiex_ie_types_data *tlv_hdr; struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; @@ -722,6 +749,9 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, tlv_hdr = (struct mwifiex_ie_types_data *) curr; tlv_len = le16_to_cpu(tlv_hdr->header.len); + if (resp_len < tlv_len + sizeof(tlv_hdr->header)) + break; + switch (le16_to_cpu(tlv_hdr->header.type)) { case TLV_TYPE_WMMQSTATUS: tlv_wmm_qstatus = @@ -1215,15 +1245,24 @@ 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, tid) && + if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && ptr->ba_pkt_count > ptr->ba_packet_thr) { if (mwifiex_space_avail_for_new_ba_stream(adapter)) { mwifiex_create_ba_tbl(priv, ptr->ra, tid, @@ -1236,7 +1275,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/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index 644d6e0c51c..eca56e371a5 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -34,6 +34,21 @@ enum ieee_types_wmm_ecw_bitmasks { static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; /* + * This table inverses the tos_to_tid operation to get a priority + * which is in sequential order, and can be compared. + * Use this to compare the priority of two different TIDs. + */ +static const u8 tos_to_tid_inv[] = { + 0x02, /* from tos_to_tid[2] = 0 */ + 0x00, /* from tos_to_tid[0] = 1 */ + 0x01, /* from tos_to_tid[1] = 2 */ + 0x03, + 0x04, + 0x05, + 0x06, + 0x07}; + +/* * This function retrieves the TID of the given RA list. */ static inline int @@ -83,11 +98,10 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) } void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb); -void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); + struct sk_buff *skb); +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); void mwifiex_rotate_priolists(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra, - int tid); + struct mwifiex_ra_list_tbl *ra, int tid); int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); @@ -95,21 +109,22 @@ int mwifiex_is_ralist_valid(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list, int tid); u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, - const struct sk_buff *skb); + const struct sk_buff *skb); void mwifiex_wmm_init(struct mwifiex_adapter *adapter); -extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, - u8 **assoc_buf, - struct ieee_types_wmm_parameter - *wmmie, - struct ieee80211_ht_cap - *htcap); +u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, + u8 **assoc_buf, + struct ieee_types_wmm_parameter *wmmie, + struct ieee80211_ht_cap *htcap); void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, - struct ieee_types_wmm_parameter - *wmm_ie); + struct ieee_types_wmm_parameter *wmm_ie); void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); -extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, - const struct host_cmd_ds_command *resp); +int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, + const struct host_cmd_ds_command *resp); +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr); +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); #endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a3707fd4ef6..3c0a0a86ba1 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -9,7 +9,6 @@ * warranty of any kind, whether express or implied. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/kernel.h> @@ -82,6 +81,9 @@ MODULE_PARM_DESC(ap_mode_default, */ #define MWL8K_HW_TIMER_REGISTER 0x0000a600 +#define BBU_RXRDY_CNT_REG 0x0000a860 +#define NOK_CCA_CNT_REG 0x0000a6a0 +#define BBU_AVG_NOISE_VAL 0x67 #define MWL8K_A2H_EVENTS (MWL8K_A2H_INT_DUMMY | \ MWL8K_A2H_INT_CHNL_SWITCHED | \ @@ -113,6 +115,8 @@ MODULE_PARM_DESC(ap_mode_default, */ #define MWL8K_NUM_AMPDU_STREAMS (TOTAL_HW_TX_QUEUES - 1) +#define MWL8K_NUM_CHANS 18 + struct rxd_ops { int rxd_size; void (*rxd_init)(void *rxd, dma_addr_t next_dma_addr); @@ -290,6 +294,12 @@ struct mwl8k_priv { /* bitmap of running BSSes */ u32 running_bsses; + + /* ACS related */ + bool sw_scan_start; + struct ieee80211_channel *acs_chan; + unsigned long channel_time; + struct survey_info survey[MWL8K_NUM_CHANS]; }; #define MAX_WEP_KEY_LEN 13 @@ -397,6 +407,7 @@ static const struct ieee80211_rate mwl8k_rates_50[] = { #define MWL8K_CMD_SET_HW_SPEC 0x0004 #define MWL8K_CMD_MAC_MULTICAST_ADR 0x0010 #define MWL8K_CMD_GET_STAT 0x0014 +#define MWL8K_CMD_BBP_REG_ACCESS 0x001a #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_TX_POWER 0x001f @@ -1258,7 +1269,7 @@ mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh) { return priv->capture_beacon && ieee80211_is_beacon(wh->frame_control) && - ether_addr_equal(wh->addr3, priv->capture_bssid); + ether_addr_equal_64bits(wh->addr3, priv->capture_bssid); } static inline void mwl8k_save_beacon(struct ieee80211_hw *hw, @@ -2988,6 +2999,47 @@ static int mwl8k_cmd_set_pre_scan(struct ieee80211_hw *hw) } /* + * CMD_BBP_REG_ACCESS. + */ +struct mwl8k_cmd_bbp_reg_access { + struct mwl8k_cmd_pkt header; + __le16 action; + __le16 offset; + u8 value; + u8 rsrv[3]; +} __packed; + +static int +mwl8k_cmd_bbp_reg_access(struct ieee80211_hw *hw, + u16 action, + u16 offset, + u8 *value) +{ + struct mwl8k_cmd_bbp_reg_access *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BBP_REG_ACCESS); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le16(action); + cmd->offset = cpu_to_le16(offset); + + rc = mwl8k_post_cmd(hw, &cmd->header); + + if (!rc) + *value = cmd->value; + else + *value = 0; + + kfree(cmd); + + return rc; +} + +/* * CMD_SET_POST_SCAN. */ struct mwl8k_cmd_set_post_scan { @@ -3017,6 +3069,64 @@ mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) return rc; } +static int freq_to_idx(struct mwl8k_priv *priv, int freq) +{ + struct ieee80211_supported_band *sband; + int band, ch, idx = 0; + + for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) { + sband = priv->hw->wiphy->bands[band]; + if (!sband) + continue; + + for (ch = 0; ch < sband->n_channels; ch++, idx++) + if (sband->channels[ch].center_freq == freq) + goto exit; + } + +exit: + return idx; +} + +static void mwl8k_update_survey(struct mwl8k_priv *priv, + struct ieee80211_channel *channel) +{ + u32 cca_cnt, rx_rdy; + s8 nf = 0, idx; + struct survey_info *survey; + + idx = freq_to_idx(priv, priv->acs_chan->center_freq); + if (idx >= MWL8K_NUM_CHANS) { + wiphy_err(priv->hw->wiphy, "Failed to update survey\n"); + return; + } + + survey = &priv->survey[idx]; + + cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); + cca_cnt /= 1000; /* uSecs to mSecs */ + survey->channel_time_busy = (u64) cca_cnt; + + rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); + rx_rdy /= 1000; /* uSecs to mSecs */ + survey->channel_time_rx = (u64) rx_rdy; + + priv->channel_time = jiffies - priv->channel_time; + survey->channel_time = jiffies_to_msecs(priv->channel_time); + + survey->channel = channel; + + mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &nf); + + /* Make sure sign is negative else ACS at hostapd fails */ + survey->noise = nf * -1; + + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_CHANNEL_TIME | + SURVEY_INFO_CHANNEL_TIME_BUSY | + SURVEY_INFO_CHANNEL_TIME_RX; +} + /* * CMD_SET_RF_CHANNEL. */ @@ -3034,6 +3144,7 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, enum nl80211_channel_type channel_type = cfg80211_get_chandef_type(&conf->chandef); struct mwl8k_cmd_set_rf_channel *cmd; + struct mwl8k_priv *priv = hw->priv; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -3050,13 +3161,29 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, else if (channel->band == IEEE80211_BAND_5GHZ) cmd->channel_flags |= cpu_to_le32(0x00000004); - if (channel_type == NL80211_CHAN_NO_HT || - channel_type == NL80211_CHAN_HT20) + if (!priv->sw_scan_start) { + if (channel_type == NL80211_CHAN_NO_HT || + channel_type == NL80211_CHAN_HT20) + cmd->channel_flags |= cpu_to_le32(0x00000080); + else if (channel_type == NL80211_CHAN_HT40MINUS) + cmd->channel_flags |= cpu_to_le32(0x000001900); + else if (channel_type == NL80211_CHAN_HT40PLUS) + cmd->channel_flags |= cpu_to_le32(0x000000900); + } else { cmd->channel_flags |= cpu_to_le32(0x00000080); - else if (channel_type == NL80211_CHAN_HT40MINUS) - cmd->channel_flags |= cpu_to_le32(0x000001900); - else if (channel_type == NL80211_CHAN_HT40PLUS) - cmd->channel_flags |= cpu_to_le32(0x000000900); + } + + if (priv->sw_scan_start) { + /* Store current channel stats + * before switching to newer one. + * This will be processed only for AP fw. + */ + if (priv->channel_time != 0) + mwl8k_update_survey(priv, priv->acs_chan); + + priv->channel_time = jiffies; + priv->acs_chan = channel; + } rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -5264,6 +5391,27 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx, { struct mwl8k_priv *priv = hw->priv; struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_supported_band *sband; + + if (priv->ap_fw) { + sband = hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + + if (sband && idx >= sband->n_channels) { + idx -= sband->n_channels; + sband = NULL; + } + + if (!sband) + sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ]; + + if (!sband || idx >= sband->n_channels) + return -ENOENT; + + memcpy(survey, &priv->survey[idx], sizeof(*survey)); + survey->channel = &sband->channels[idx]; + + return 0; + } if (idx != 0) return -ENOENT; @@ -5407,6 +5555,40 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return rc; } +static void mwl8k_sw_scan_start(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + u8 tmp; + + if (!priv->ap_fw) + return; + + /* clear all stats */ + priv->channel_time = 0; + ioread32(priv->regs + BBU_RXRDY_CNT_REG); + ioread32(priv->regs + NOK_CCA_CNT_REG); + mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); + + priv->sw_scan_start = true; +} + +static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + u8 tmp; + + if (!priv->ap_fw) + return; + + priv->sw_scan_start = false; + + /* clear all stats */ + priv->channel_time = 0; + ioread32(priv->regs + BBU_RXRDY_CNT_REG); + ioread32(priv->regs + NOK_CCA_CNT_REG); + mwl8k_cmd_bbp_reg_access(priv->hw, 0, BBU_AVG_NOISE_VAL, &tmp); +} + static const struct ieee80211_ops mwl8k_ops = { .tx = mwl8k_tx, .start = mwl8k_start, @@ -5425,6 +5607,8 @@ static const struct ieee80211_ops mwl8k_ops = { .get_stats = mwl8k_get_stats, .get_survey = mwl8k_get_survey, .ampdu_action = mwl8k_ampdu_action, + .sw_scan_start = mwl8k_sw_scan_start, + .sw_scan_complete = mwl8k_sw_scan_complete, }; static void mwl8k_finalize_join_worker(struct work_struct *work) @@ -5893,8 +6077,6 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) hw->extra_tx_headroom -= priv->ap_fw ? REDUCED_TX_HEADROOM : 0; - hw->channel_change_time = 10; - hw->queues = MWL8K_TX_WMM_QUEUES; /* Set rssi values to dBm */ @@ -6093,7 +6275,6 @@ err_iounmap: if (priv->sram != NULL) pci_iounmap(pdev, priv->sram); - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); err_free_reg: @@ -6147,7 +6328,6 @@ static void mwl8k_remove(struct pci_dev *pdev) unmap: pci_iounmap(pdev, priv->regs); pci_iounmap(pdev, priv->sram); - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index d01edd2c50c..a9e94b6db5b 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -59,7 +59,8 @@ int orinoco_wiphy_register(struct wiphy *wiphy) for (i = 0; i < NUM_CHANNELS; i++) { if (priv->channel_mask & (1 << i)) { priv->channels[i].center_freq = - ieee80211_dsss_chan_to_freq(i + 1); + ieee80211_channel_to_frequency(i + 1, + IEEE80211_BAND_2GHZ); channels++; } } @@ -177,7 +178,7 @@ static int orinoco_set_monitor_channel(struct wiphy *wiphy, if (chandef->chan->band != IEEE80211_BAND_2GHZ) return -EINVAL; - channel = ieee80211_freq_to_dsss_chan(chandef->chan->center_freq); + channel = ieee80211_frequency_to_channel(chandef->chan->center_freq); if ((channel < 1) || (channel > NUM_CHANNELS) || !(priv->channel_mask & (1 << (channel - 1)))) diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c index 75c15bc7b34..43790fbea0e 100644 --- a/drivers/net/wireless/orinoco/hermes.c +++ b/drivers/net/wireless/orinoco/hermes.c @@ -40,7 +40,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include "hermes.h" diff --git a/drivers/net/wireless/orinoco/hw.c b/drivers/net/wireless/orinoco/hw.c index c09c8437c0b..e27e32851f1 100644 --- a/drivers/net/wireless/orinoco/hw.c +++ b/drivers/net/wireless/orinoco/hw.c @@ -988,8 +988,8 @@ int __orinoco_hw_setup_enc(struct orinoco_private *priv) * tsc must be NULL or up to 8 bytes */ int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, u8 *key, u8 *rsc, size_t rsc_len, - u8 *tsc, size_t tsc_len) + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len) { struct { __le16 idx; @@ -1193,7 +1193,7 @@ int orinoco_hw_get_freq(struct orinoco_private *priv) goto out; } - freq = ieee80211_dsss_chan_to_freq(channel); + freq = ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); out: orinoco_unlock(priv, &flags); diff --git a/drivers/net/wireless/orinoco/hw.h b/drivers/net/wireless/orinoco/hw.h index 8f6831f4e32..466d1ede76f 100644 --- a/drivers/net/wireless/orinoco/hw.h +++ b/drivers/net/wireless/orinoco/hw.h @@ -38,8 +38,8 @@ int __orinoco_hw_set_wap(struct orinoco_private *priv); int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv); int __orinoco_hw_setup_enc(struct orinoco_private *priv); int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx, - int set_tx, u8 *key, u8 *rsc, size_t rsc_len, - u8 *tsc, size_t tsc_len); + int set_tx, const u8 *key, const u8 *rsc, + size_t rsc_len, const u8 *tsc, size_t tsc_len); int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx); int __orinoco_hw_set_multicast_list(struct orinoco_private *priv, struct net_device *dev, diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h index 3bb936b9558..eebd2be21ee 100644 --- a/drivers/net/wireless/orinoco/orinoco.h +++ b/drivers/net/wireless/orinoco/orinoco.h @@ -182,23 +182,20 @@ extern int orinoco_debug; /* Exported prototypes */ /********************************************************************/ -extern struct orinoco_private *alloc_orinocodev( - int sizeof_card, struct device *device, - int (*hard_reset)(struct orinoco_private *), - int (*stop_fw)(struct orinoco_private *, int)); -extern void free_orinocodev(struct orinoco_private *priv); -extern int orinoco_init(struct orinoco_private *priv); -extern int orinoco_if_add(struct orinoco_private *priv, - unsigned long base_addr, - unsigned int irq, - const struct net_device_ops *ops); -extern void orinoco_if_del(struct orinoco_private *priv); -extern int orinoco_up(struct orinoco_private *priv); -extern void orinoco_down(struct orinoco_private *priv); -extern irqreturn_t orinoco_interrupt(int irq, void *dev_id); - -extern void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); -extern void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); +struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device, + int (*hard_reset)(struct orinoco_private *), + int (*stop_fw)(struct orinoco_private *, int)); +void free_orinocodev(struct orinoco_private *priv); +int orinoco_init(struct orinoco_private *priv); +int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, + unsigned int irq, const struct net_device_ops *ops); +void orinoco_if_del(struct orinoco_private *priv); +int orinoco_up(struct orinoco_private *priv); +void orinoco_down(struct orinoco_private *priv); +irqreturn_t orinoco_interrupt(int irq, void *dev_id); + +void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); +void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); int orinoco_process_xmit_skb(struct sk_buff *skb, struct net_device *dev, diff --git a/drivers/net/wireless/orinoco/orinoco_cs.c b/drivers/net/wireless/orinoco/orinoco_cs.c index d21d9593931..c0a27377d9e 100644 --- a/drivers/net/wireless/orinoco/orinoco_cs.c +++ b/drivers/net/wireless/orinoco/orinoco_cs.c @@ -15,7 +15,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c index d73fdf6185a..ffb2469eb67 100644 --- a/drivers/net/wireless/orinoco/orinoco_nortel.c +++ b/drivers/net/wireless/orinoco/orinoco_nortel.c @@ -234,7 +234,6 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -265,7 +264,6 @@ static void orinoco_nortel_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c index 677bf14eca8..5ae1191d253 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.c +++ b/drivers/net/wireless/orinoco/orinoco_pci.c @@ -184,7 +184,6 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -205,7 +204,6 @@ static void orinoco_pci_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_release_regions(pdev); diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c index 2559dbd6184..bbd36d1676f 100644 --- a/drivers/net/wireless/orinoco/orinoco_plx.c +++ b/drivers/net/wireless/orinoco/orinoco_plx.c @@ -273,7 +273,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -301,7 +300,6 @@ static void orinoco_plx_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c index 42afeeea2c4..04b08de5fd5 100644 --- a/drivers/net/wireless/orinoco/orinoco_tmd.c +++ b/drivers/net/wireless/orinoco/orinoco_tmd.c @@ -170,7 +170,6 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -195,7 +194,6 @@ static void orinoco_tmd_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->bridge_io); diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c index bdfe637953f..c90939ced0e 100644 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -52,7 +52,6 @@ #include <linux/signal.h> #include <linux/errno.h> #include <linux/poll.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/fcntl.h> #include <linux/spinlock.h> @@ -101,25 +100,11 @@ static struct ez_usb_fw firmware = { .code = NULL, }; -#ifdef CONFIG_USB_DEBUG -static int debug = 1; -#else -static int debug; -#endif - /* Debugging macros */ -#undef dbg -#define dbg(format, arg...) \ - do { if (debug) printk(KERN_DEBUG PFX "%s: " format "\n", \ - __func__ , ## arg); } while (0) #undef err #define err(format, arg...) \ do { printk(KERN_ERR PFX format "\n", ## arg); } while (0) -/* Module paramaters */ -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug enabled or not"); - MODULE_FIRMWARE("orinoco_ezusb_fw"); /* @@ -342,7 +327,7 @@ static void ezusb_request_timerfn(u_long _ctx) ctx->state = EZUSB_CTX_REQ_TIMEOUT; } else { ctx->state = EZUSB_CTX_RESP_TIMEOUT; - dbg("couldn't unlink"); + dev_dbg(&ctx->outurb->dev->dev, "couldn't unlink\n"); atomic_inc(&ctx->refcount); ctx->killed = 1; ezusb_ctx_complete(ctx); @@ -635,9 +620,9 @@ static void ezusb_request_in_callback(struct ezusb_priv *upriv, ctx = c; break; } - dbg("Skipped (0x%x/0x%x) (%d/%d)", - le16_to_cpu(ans->hermes_rid), - c->in_rid, ans->ans_reply_count, reply_count); + netdev_dbg(upriv->dev, "Skipped (0x%x/0x%x) (%d/%d)\n", + le16_to_cpu(ans->hermes_rid), c->in_rid, + ans->ans_reply_count, reply_count); } } @@ -769,7 +754,7 @@ static int ezusb_submit_in_urb(struct ezusb_priv *upriv) void *cur_buf = upriv->read_urb->transfer_buffer; if (upriv->read_urb->status == -EINPROGRESS) { - dbg("urb busy, not resubmiting"); + netdev_dbg(upriv->dev, "urb busy, not resubmiting\n"); retval = -EBUSY; goto exit; } @@ -839,8 +824,9 @@ static int ezusb_firmware_download(struct ezusb_priv *upriv, memcpy(fw_buffer, &fw->code[addr], FW_BUF_SIZE); if (variant_offset >= addr && variant_offset < addr + FW_BUF_SIZE) { - dbg("Patching card_variant byte at 0x%04X", - variant_offset); + netdev_dbg(upriv->dev, + "Patching card_variant byte at 0x%04X\n", + variant_offset); fw_buffer[variant_offset - addr] = FW_VAR_VALUE; } retval = usb_control_msg(upriv->udev, @@ -880,7 +866,6 @@ static int ezusb_access_ltv(struct ezusb_priv *upriv, BUG_ON(in_irq()); if (!upriv->udev) { - dbg("Device disconnected"); retval = -ENODEV; goto exit; } @@ -1024,8 +1009,9 @@ static int ezusb_doicmd_wait(struct hermes *hw, u16 cmd, u16 parm0, u16 parm1, cpu_to_le16(parm1), cpu_to_le16(parm2), }; - dbg("0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X", - cmd, parm0, parm1, parm2); + netdev_dbg(upriv->dev, + "0x%04X, parm0 0x%04X, parm1 0x%04X, parm2 0x%04X\n", cmd, + parm0, parm1, parm2); ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; @@ -1046,7 +1032,7 @@ static int ezusb_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0, 0, 0, }; - dbg("0x%04X, parm0 0x%04X", cmd, parm0); + netdev_dbg(upriv->dev, "0x%04X, parm0 0x%04X\n", cmd, parm0); ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_DOCMD, EZUSB_RID_ACK); if (!ctx) return -ENOMEM; @@ -1333,7 +1319,7 @@ static int ezusb_hard_reset(struct orinoco_private *priv) return retval; } - dbg("sending control message"); + netdev_dbg(upriv->dev, "sending control message\n"); retval = usb_control_msg(upriv->udev, usb_sndctrlpipe(upriv->udev, 0), EZUSB_REQUEST_TRIGER, @@ -1402,10 +1388,8 @@ static void ezusb_bulk_in_callback(struct urb *urb) u16 crc; u16 hermes_rid; - if (upriv->udev == NULL) { - dbg("disconnected"); + if (upriv->udev == NULL) return; - } if (urb->status == -ETIMEDOUT) { /* When a device gets unplugged we get this every time @@ -1422,12 +1406,13 @@ static void ezusb_bulk_in_callback(struct urb *urb) if ((urb->status == -EILSEQ) || (urb->status == -ENOENT) || (urb->status == -ECONNRESET)) { - dbg("status %d, not resubmiting", urb->status); + netdev_dbg(upriv->dev, "status %d, not resubmiting\n", + urb->status); return; } if (urb->status) - dbg("status: %d length: %d", - urb->status, urb->actual_length); + netdev_dbg(upriv->dev, "status: %d length: %d\n", + urb->status, urb->actual_length); if (urb->actual_length < sizeof(*ans)) { err("%s: short read, ignoring", __func__); goto resubmit; @@ -1688,7 +1673,7 @@ static int ezusb_probe(struct usb_interface *interface, firmware.code = fw_entry->data; } if (firmware.size && firmware.code) { - if (ezusb_firmware_download(upriv, &firmware)) + if (ezusb_firmware_download(upriv, &firmware) < 0) goto error; } else { err("No firmware to download"); diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index e8c5714bfd1..e175b9b8561 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -110,7 +110,8 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv, break; } - freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); + freq = ieee80211_channel_to_frequency( + le16_to_cpu(bss->a.channel), IEEE80211_BAND_2GHZ); channel = ieee80211_get_channel(wiphy, freq); if (!channel) { printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", @@ -146,7 +147,7 @@ void orinoco_add_extscan_result(struct orinoco_private *priv, ie_len = len - sizeof(*bss); ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len); chan = ie ? ie[2] : 0; - freq = ieee80211_dsss_chan_to_freq(chan); + freq = ieee80211_channel_to_frequency(chan, IEEE80211_BAND_2GHZ); channel = ieee80211_get_channel(wiphy, freq); timestamp = le64_to_cpu(bss->timestamp); diff --git a/drivers/net/wireless/orinoco/spectrum_cs.c b/drivers/net/wireless/orinoco/spectrum_cs.c index e2264bc12eb..b60048c95e0 100644 --- a/drivers/net/wireless/orinoco/spectrum_cs.c +++ b/drivers/net/wireless/orinoco/spectrum_cs.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/delay.h> #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index 3b5508f982e..6abdaf0aa05 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -52,9 +52,9 @@ static int orinoco_set_key(struct orinoco_private *priv, int index, priv->keys[index].seq_len = seq_len; if (key_len) - memcpy(priv->keys[index].key, key, key_len); + memcpy((void *)priv->keys[index].key, key, key_len); if (seq_len) - memcpy(priv->keys[index].seq, seq, seq_len); + memcpy((void *)priv->keys[index].seq, seq, seq_len); switch (alg) { case ORINOCO_ALG_TKIP: @@ -444,7 +444,7 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, for (i = 0; i < (6 - frq->e); i++) denom *= 10; - chan = ieee80211_freq_to_dsss_chan(frq->m / denom); + chan = ieee80211_frequency_to_channel(frq->m / denom); } if ((chan < 1) || (chan > NUM_CHANNELS) || diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index d43e3740e45..0fe67d2da20 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -16,7 +16,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/firmware.h> #include <linux/etherdevice.h> #include <linux/sort.h> diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c index b3879fbf536..bc065e8e348 100644 --- a/drivers/net/wireless/p54/fwio.c +++ b/drivers/net/wireless/p54/fwio.c @@ -16,7 +16,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/firmware.h> #include <linux/etherdevice.h> diff --git a/drivers/net/wireless/p54/led.c b/drivers/net/wireless/p54/led.c index 3837e1eec5f..1f6fd5ff553 100644 --- a/drivers/net/wireless/p54/led.c +++ b/drivers/net/wireless/p54/led.c @@ -16,7 +16,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/firmware.h> #include <linux/etherdevice.h> diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 067e6f2fd05..7be3a483964 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -16,7 +16,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/slab.h> #include <linux/firmware.h> #include <linux/etherdevice.h> @@ -670,7 +669,8 @@ static unsigned int p54_flush_count(struct p54_common *priv) return total; } -static void p54_flush(struct ieee80211_hw *dev, u32 queues, bool drop) +static void p54_flush(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct p54_common *priv = dev->priv; unsigned int total, i; @@ -757,7 +757,6 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT); - dev->channel_change_time = 1000; /* TODO: find actual value */ priv->beacon_req_id = cpu_to_le32(0); priv->tx_stats[P54_QUEUE_BEACON].limit = 1; priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; diff --git a/drivers/net/wireless/p54/net2280.h b/drivers/net/wireless/p54/net2280.h index e3ed893b5aa..aedfaf24f38 100644 --- a/drivers/net/wireless/p54/net2280.h +++ b/drivers/net/wireless/p54/net2280.h @@ -20,8 +20,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ /*-------------------------------------------------------------------------*/ diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 57e3af8ebb4..d411de40905 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -13,7 +13,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/pci.h> #include <linux/slab.h> #include <linux/firmware.h> @@ -631,7 +630,6 @@ static int p54p_probe(struct pci_dev *pdev, iounmap(priv->map); err_free_dev: - pci_set_drvdata(pdev, NULL); p54_free_common(dev); err_free_reg: diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 7fc46f26cf2..de15171e2cd 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -636,7 +636,7 @@ static int p54spi_probe(struct spi_device *spi) gpio_direction_input(p54spi_gpio_irq); ret = request_irq(gpio_to_irq(p54spi_gpio_irq), - p54spi_interrupt, IRQF_DISABLED, "p54spi", + p54spi_interrupt, 0, "p54spi", priv->spi); if (ret < 0) { dev_err(&priv->spi->dev, "request_irq() failed"); diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index e328d3058c4..043bd1c23c1 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -12,7 +12,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/usb.h> #include <linux/pci.h> #include <linux/slab.h> @@ -514,7 +513,7 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev) if (!buf) return -ENOMEM; - left = block_size = min((size_t)P54U_FW_BLOCK, priv->fw->size); + left = block_size = min_t(size_t, P54U_FW_BLOCK, priv->fw->size); strcpy(buf, p54u_firmware_upload_3887); left -= strlen(p54u_firmware_upload_3887); tmp += strlen(p54u_firmware_upload_3887); @@ -1054,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/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index f95de0d1621..153c61539ec 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -17,7 +17,6 @@ */ #include <linux/export.h> -#include <linux/init.h> #include <linux/firmware.h> #include <linux/etherdevice.h> #include <asm/div64.h> @@ -308,7 +307,7 @@ static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb) return; /* only consider beacons from the associated BSSID */ - if (!ether_addr_equal(hdr->addr3, priv->bssid)) + if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid)) return; tim = p54_find_ie(skb, WLAN_EID_TIM); @@ -587,7 +586,7 @@ static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) chan = priv->curchan; if (chan) { struct survey_info *survey = &priv->survey[chan->hw_value]; - survey->noise = clamp_t(s8, priv->noise, -128, 127); + survey->noise = clamp(priv->noise, -128, 127); survey->channel_time = priv->survey_raw.active; survey->channel_time_tx = priv->survey_raw.tx; survey->channel_time_busy = priv->survey_raw.tx + diff --git a/drivers/net/wireless/prism54/isl_38xx.c b/drivers/net/wireless/prism54/isl_38xx.c index 02fc67bccbd..333c1a2f882 100644 --- a/drivers/net/wireless/prism54/isl_38xx.c +++ b/drivers/net/wireless/prism54/isl_38xx.c @@ -12,8 +12,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/isl_38xx.h b/drivers/net/wireless/prism54/isl_38xx.h index 19c33d31373..547ab885610 100644 --- a/drivers/net/wireless/prism54/isl_38xx.h +++ b/drivers/net/wireless/prism54/isl_38xx.h @@ -11,8 +11,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 1c22b81e6ef..ecbb0546cf3 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ @@ -25,6 +24,7 @@ #include <linux/if_arp.h> #include <linux/slab.h> #include <linux/pci.h> +#include <linux/etherdevice.h> #include <asm/uaccess.h> @@ -183,7 +183,7 @@ prism54_update_stats(struct work_struct *work) data = r.ptr; /* copy this MAC to the bss */ - memcpy(bss.address, data, 6); + memcpy(bss.address, data, ETH_ALEN); kfree(data); /* now ask for the corresponding bss */ @@ -531,7 +531,7 @@ prism54_set_wap(struct net_device *ndev, struct iw_request_info *info, return -EINVAL; /* prepare the structure for the set object */ - memcpy(&bssid[0], awrq->sa_data, 6); + memcpy(&bssid[0], awrq->sa_data, ETH_ALEN); /* set the bssid -- does this make sense when in AP mode? */ rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid); @@ -550,7 +550,7 @@ prism54_get_wap(struct net_device *ndev, struct iw_request_info *info, int rvalue; rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r); - memcpy(awrq->sa_data, r.ptr, 6); + memcpy(awrq->sa_data, r.ptr, ETH_ALEN); awrq->sa_family = ARPHRD_ETHER; kfree(r.ptr); @@ -582,7 +582,7 @@ prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, size_t wpa_ie_len; /* The first entry must be the MAC address */ - memcpy(iwe.u.ap_addr.sa_data, bss->address, 6); + memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN); iwe.u.ap_addr.sa_family = ARPHRD_ETHER; iwe.cmd = SIOCGIWAP; current_ev = iwe_stream_add_event(info, current_ev, end_buf, @@ -644,7 +644,7 @@ prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, wpa_ie_len = prism54_wpa_bss_ie_get(priv, bss->address, wpa_ie); if (wpa_ie_len > 0) { iwe.cmd = IWEVGENIE; - iwe.u.data.length = min(wpa_ie_len, (size_t)MAX_WPA_IE_LEN); + iwe.u.data.length = min_t(size_t, wpa_ie_len, MAX_WPA_IE_LEN); current_ev = iwe_stream_add_point(info, current_ev, end_buf, &iwe, wpa_ie); } @@ -1861,7 +1861,7 @@ prism54_del_mac(struct net_device *ndev, struct iw_request_info *info, if (mutex_lock_interruptible(&acl->lock)) return -ERESTARTSYS; list_for_each_entry(entry, &acl->mac_list, _list) { - if (memcmp(entry->addr, addr->sa_data, ETH_ALEN) == 0) { + if (ether_addr_equal(entry->addr, addr->sa_data)) { list_del(&entry->_list); acl->size--; kfree(entry); @@ -2489,7 +2489,7 @@ prism54_set_mac_address(struct net_device *ndev, void *addr) &((struct sockaddr *) addr)->sa_data); if (!ret) memcpy(priv->ndev->dev_addr, - &((struct sockaddr *) addr)->sa_data, 6); + &((struct sockaddr *) addr)->sa_data, ETH_ALEN); return ret; } diff --git a/drivers/net/wireless/prism54/isl_ioctl.h b/drivers/net/wireless/prism54/isl_ioctl.h index a34bceb6e3c..842a2549fac 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.h +++ b/drivers/net/wireless/prism54/isl_ioctl.h @@ -13,8 +13,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/isl_oid.h b/drivers/net/wireless/prism54/isl_oid.h index 59e31258d45..83fec557997 100644 --- a/drivers/net/wireless/prism54/isl_oid.h +++ b/drivers/net/wireless/prism54/isl_oid.h @@ -13,8 +13,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index 5970ff6f40c..931cf440ff1 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -13,8 +13,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ @@ -811,6 +810,10 @@ static const struct net_device_ops islpci_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; +static struct device_type wlan_type = { + .name = "wlan", +}; + struct net_device * islpci_setup(struct pci_dev *pdev) { @@ -821,9 +824,8 @@ islpci_setup(struct pci_dev *pdev) return ndev; pci_set_drvdata(pdev, ndev); -#if defined(SET_NETDEV_DEV) SET_NETDEV_DEV(ndev, &pdev->dev); -#endif + SET_NETDEV_DEVTYPE(ndev, &wlan_type); /* setup the structure members */ ndev->base_addr = pci_resource_start(pdev, 0); @@ -837,7 +839,7 @@ islpci_setup(struct pci_dev *pdev) /* ndev->set_multicast_list = &islpci_set_multicast_list; */ ndev->addr_len = ETH_ALEN; /* Get a non-zero dummy MAC address for nameif. Jean II */ - memcpy(ndev->dev_addr, dummy_mac, 6); + memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN); ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT; @@ -911,7 +913,6 @@ islpci_setup(struct pci_dev *pdev) do_islpci_free_memory: islpci_free_memory(priv); do_free_netdev: - pci_set_drvdata(pdev, NULL); free_netdev(ndev); priv = NULL; return NULL; diff --git a/drivers/net/wireless/prism54/islpci_dev.h b/drivers/net/wireless/prism54/islpci_dev.h index c40403877f9..f6f088e05fe 100644 --- a/drivers/net/wireless/prism54/islpci_dev.h +++ b/drivers/net/wireless/prism54/islpci_dev.h @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index 799e148d037..674658f2e6e 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -11,8 +11,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/islpci_eth.h b/drivers/net/wireless/prism54/islpci_eth.h index 6ca30a5b7bf..80f50f1bc6f 100644 --- a/drivers/net/wireless/prism54/islpci_eth.h +++ b/drivers/net/wireless/prism54/islpci_eth.h @@ -11,8 +11,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/islpci_hotplug.c b/drivers/net/wireless/prism54/islpci_hotplug.c index 9e68e0cb718..1105a12dbde 100644 --- a/drivers/net/wireless/prism54/islpci_hotplug.c +++ b/drivers/net/wireless/prism54/islpci_hotplug.c @@ -12,8 +12,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ @@ -199,7 +198,6 @@ prism54_probe(struct pci_dev *pdev, const struct pci_device_id *id) do_unregister_netdev: unregister_netdev(ndev); islpci_free_memory(priv); - pci_set_drvdata(pdev, NULL); free_netdev(ndev); priv = NULL; do_pci_clear_mwi: @@ -247,7 +245,6 @@ prism54_remove(struct pci_dev *pdev) /* free the PCI memory and unmap the remapped page */ islpci_free_memory(priv); - pci_set_drvdata(pdev, NULL); free_netdev(ndev); priv = NULL; diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c index 9f19cceab48..0de14dfa68c 100644 --- a/drivers/net/wireless/prism54/islpci_mgt.c +++ b/drivers/net/wireless/prism54/islpci_mgt.c @@ -12,8 +12,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/islpci_mgt.h b/drivers/net/wireless/prism54/islpci_mgt.h index 0db93db9b67..700c434c880 100644 --- a/drivers/net/wireless/prism54/islpci_mgt.h +++ b/drivers/net/wireless/prism54/islpci_mgt.h @@ -12,8 +12,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/oid_mgt.c b/drivers/net/wireless/prism54/oid_mgt.c index a01606b36e0..47b34bfe890 100644 --- a/drivers/net/wireless/prism54/oid_mgt.c +++ b/drivers/net/wireless/prism54/oid_mgt.c @@ -11,8 +11,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ @@ -682,7 +681,7 @@ mgt_update_addr(islpci_private *priv) isl_oid[GEN_OID_MACADDRESS].size, &res); if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR)) - memcpy(priv->ndev->dev_addr, res->data, 6); + memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN); else ret = -EIO; if (res) diff --git a/drivers/net/wireless/prism54/oid_mgt.h b/drivers/net/wireless/prism54/oid_mgt.h index 92c8a2d4acd..cf5141df847 100644 --- a/drivers/net/wireless/prism54/oid_mgt.h +++ b/drivers/net/wireless/prism54/oid_mgt.h @@ -11,8 +11,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/prism54/prismcompat.h b/drivers/net/wireless/prism54/prismcompat.h index aa1d1747784..bc1401eb4b9 100644 --- a/drivers/net/wireless/prism54/prismcompat.h +++ b/drivers/net/wireless/prism54/prismcompat.h @@ -11,8 +11,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * */ diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 9b557a1bb7f..8330fa33e50 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -17,8 +17,7 @@ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * * Changes: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000 @@ -344,7 +343,7 @@ static void ray_detach(struct pcmcia_device *link) ray_release(link); local = netdev_priv(dev); - del_timer(&local->timer); + del_timer_sync(&local->timer); if (link->priv) { unregister_netdev(dev); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 8169a85c449..d2a9a08210b 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -15,8 +15,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. * * Portions of this file are based on NDISwrapper project, * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani @@ -27,7 +26,6 @@ // #define VERBOSE // more; success messages #include <linux/module.h> -#include <linux/init.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -519,7 +517,7 @@ static int rndis_set_default_key(struct wiphy *wiphy, struct net_device *netdev, u8 key_index, bool unicast, bool multicast); static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo); + const u8 *mac, struct station_info *sinfo); static int rndis_dump_station(struct wiphy *wiphy, struct net_device *dev, int idx, u8 *mac, struct station_info *sinfo); @@ -1292,7 +1290,8 @@ static int set_channel(struct usbnet *usbdev, int channel) if (is_associated(usbdev)) return 0; - dsconfig = ieee80211_dsss_chan_to_freq(channel) * 1000; + dsconfig = 1000 * + ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ); len = sizeof(config); ret = rndis_query_oid(usbdev, @@ -2491,7 +2490,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, } static int rndis_get_station(struct wiphy *wiphy, struct net_device *dev, - u8 *mac, struct station_info *sinfo) + const u8 *mac, struct station_info *sinfo) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; @@ -2837,7 +2836,9 @@ static void rndis_wlan_do_link_up_work(struct usbnet *usbdev) bssid, req_ie, req_ie_len, resp_ie, resp_ie_len, GFP_KERNEL); } else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC) - cfg80211_ibss_joined(usbdev->net, bssid, GFP_KERNEL); + cfg80211_ibss_joined(usbdev->net, bssid, + get_current_channel(usbdev, NULL), + GFP_KERNEL); kfree(info); 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..cf61d6e3eaa --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_core.c @@ -0,0 +1,344 @@ +/** + * 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 = 0, 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: + 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; + } + } + + if (q_num < NUM_EDCA_QUEUES) + 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..c466246a323 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c @@ -0,0 +1,336 @@ +/** + * 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); + if (!dev_dbgfs) + return -ENOMEM; + + adapter->dfsentry = dev_dbgfs; + + snprintf(devdir, sizeof(devdir), "%s", + wiphy_name(adapter->hw->wiphy)); + + dev_dbgfs->subdir = debugfs_create_dir(devdir, NULL); + + if (!dev_dbgfs->subdir) { + kfree(dev_dbgfs); + return -ENOMEM; + } + + 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..54aaeb09deb --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -0,0 +1,1009 @@ +/** + * 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); + status = 0; + 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..2eefbf159bc --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c @@ -0,0 +1,1307 @@ +/** + * 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 int 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__); + + 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; + } + } + + 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(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__); + dev_kfree_skb(skb); + 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, + RSI_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, + RSI_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]); + int ret; + + 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); + + ret = rsi_load_bootup_params(common); + if (ret) + return ret; + 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..46e7af446f0 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c @@ -0,0 +1,849 @@ +/** + * 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); + } 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; + } + host->ios.timing = MMC_TIMING_SD_HS; + host->ops->set_ios(host, &host->ios); + } + } + + /* Set clock */ + if (mmc_card_hs(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; + + if (!adapter) + return; + + dev = (struct rsi_91x_sdiodev *)adapter->rsi_dev; + + 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..20d11ccfffe --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -0,0 +1,564 @@ +/** + * 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 -ENOMEM; + } + + 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); + +fail: + kfree(common->rx_data_pkt); + return status; +} + +/** + * 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..4c46e5631e2 --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_91x_usb.c @@ -0,0 +1,587 @@ +/** + * 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 *buf; + int status = -ENOMEM; + + buf = kmalloc(0x04, GFP_KERNEL); + if (!buf) + return status; + + status = usb_control_msg(usbdev, + usb_rcvctrlpipe(usbdev, 0), + USB_VENDOR_REGISTER_READ, + USB_TYPE_VENDOR, + ((reg & 0xffff0000) >> 16), (reg & 0xffff), + (void *)buf, + len, + HZ * 5); + + *value = (buf[0] | (buf[1] << 8)); + if (status < 0) { + rsi_dbg(ERR_ZONE, + "%s: Reg read failed with error code :%d\n", + __func__, status); + } + kfree(buf); + + 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; + int status = -ENOMEM; + + usb_reg_buf = kmalloc(0x04, GFP_KERNEL); + if (!usb_reg_buf) + return status; + + 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); + } + kfree(usb_reg_buf); + + 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 = (u8)(min_t(u32, 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..d3fbe33d232 --- /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, "%s", 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..225215a3b8b --- /dev/null +++ b/drivers/net/wireless/rsi/rsi_mgmt.h @@ -0,0 +1,286 @@ +/** + * 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 RSI_IFTYPE_STATION 0 + +#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/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 68dbbb9c6d1..006b8bcb2e3 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -58,11 +58,11 @@ config RT61PCI config RT2800PCI tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" - depends on PCI || SOC_RT288X || SOC_RT305X + depends on PCI select RT2800_LIB + select RT2800_LIB_MMIO select RT2X00_LIB_MMIO - select RT2X00_LIB_PCI if PCI - select RT2X00_LIB_SOC if SOC_RT288X || SOC_RT305X + select RT2X00_LIB_PCI select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_CCITT @@ -199,9 +199,30 @@ config RT2800USB_UNKNOWN endif +config RT2800SOC + tristate "Ralink WiSoC support" + depends on SOC_RT288X || SOC_RT305X + select RT2X00_LIB_SOC + select RT2X00_LIB_MMIO + select RT2X00_LIB_CRYPTO + select RT2X00_LIB_FIRMWARE + select RT2800_LIB + select RT2800_LIB_MMIO + ---help--- + This adds support for Ralink WiSoC devices. + Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352. + + When compiled as a module, this driver will be called rt2800soc. + + config RT2800_LIB tristate +config RT2800_LIB_MMIO + tristate + select RT2X00_LIB_MMIO + select RT2800_LIB + config RT2X00_LIB_MMIO tristate @@ -219,6 +240,7 @@ config RT2X00_LIB_USB config RT2X00_LIB tristate + select AVERAGE config RT2X00_LIB_FIRMWARE boolean diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile index f069d8bc5b6..24a66015a49 100644 --- a/drivers/net/wireless/rt2x00/Makefile +++ b/drivers/net/wireless/rt2x00/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o obj-$(CONFIG_RT2800_LIB) += rt2800lib.o +obj-$(CONFIG_RT2800_LIB_MMIO) += rt2800mmio.o obj-$(CONFIG_RT2400PCI) += rt2400pci.o obj-$(CONFIG_RT2500PCI) += rt2500pci.o obj-$(CONFIG_RT61PCI) += rt61pci.o @@ -21,3 +22,4 @@ obj-$(CONFIG_RT2800PCI) += rt2800pci.o obj-$(CONFIG_RT2500USB) += rt2500usb.o obj-$(CONFIG_RT73USB) += rt73usb.o obj-$(CONFIG_RT2800USB) += rt2800usb.o +obj-$(CONFIG_RT2800SOC) += rt2800soc.o diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 3d53a09da5a..4ccfef5094e 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,7 +24,6 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -1261,7 +1258,7 @@ static void rt2400pci_fill_rxdone(struct queue_entry *entry, */ rxdesc->timestamp = ((u64)rx_high << 32) | rx_low; rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08; - rxdesc->rssi = rt2x00_get_field32(word2, RXD_W3_RSSI) - + rxdesc->rssi = rt2x00_get_field32(word3, RXD_W3_RSSI) - entry->queue->rt2x00dev->rssi_offset; rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); diff --git a/drivers/net/wireless/rt2x00/rt2400pci.h b/drivers/net/wireless/rt2x00/rt2400pci.h index e4b07f0aa3c..0fd3a9d01a6 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.h +++ b/drivers/net/wireless/rt2x00/rt2400pci.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 0ac5c589ddc..a511cccc9f0 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,7 +24,6 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -1684,8 +1681,13 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) { __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + /* + * On this device RFKILL initialized during probe does not work. + */ + __set_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags); + } /* * Check if the BBP tuning should be enabled. @@ -1880,6 +1882,11 @@ static int rt2500pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) EEPROM_MAC_ADDR_0)); /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + /* * Initialize hw_mode information. */ spec->supported_bands = SUPPORT_BAND_2GHZ; diff --git a/drivers/net/wireless/rt2x00/rt2500pci.h b/drivers/net/wireless/rt2x00/rt2500pci.h index 9c10068e498..573e87bcc55 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.h +++ b/drivers/net/wireless/rt2x00/rt2500pci.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2500usb.c b/drivers/net/wireless/rt2x00/rt2500usb.c index 85acc79f68b..d849d590de2 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.c +++ b/drivers/net/wireless/rt2x00/rt2500usb.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -26,7 +24,6 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -1709,6 +1706,11 @@ static int rt2500usb_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_PS_NULLFUNC_STACK; + /* + * Disable powersaving as default. + */ + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev); SET_IEEE80211_PERM_ADDR(rt2x00dev->hw, rt2x00_eeprom_addr(rt2x00dev, diff --git a/drivers/net/wireless/rt2x00/rt2500usb.h b/drivers/net/wireless/rt2x00/rt2500usb.h index 1b91a4cef96..afba0739c3b 100644 --- a/drivers/net/wireless/rt2x00/rt2500usb.h +++ b/drivers/net/wireless/rt2x00/rt2500usb.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index fa33b5edf93..a394a9a9591 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -21,9 +21,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -52,6 +50,7 @@ * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) * RF5592 2.4G/5G 2T2R + * RF3070 2.4G 1T1R * RF5360 2.4G 1T1R * RF5370 2.4G 1T1R * RF5390 2.4G 1T1R @@ -70,6 +69,7 @@ #define RF3322 0x000c #define RF3053 0x000d #define RF5592 0x000f +#define RF3070 0x3070 #define RF3290 0x3290 #define RF5360 0x5360 #define RF5370 0x5370 @@ -122,7 +122,7 @@ /* * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. */ -#define MAC_CSR0_3290 0x0000 +#define MAC_CSR0_3290 0x0000 /* * E2PROM_CSR: PCI EEPROM control register. @@ -211,17 +211,17 @@ /* * COEX_CFG_0 */ -#define COEX_CFG0 0x0040 +#define COEX_CFG0 0x0040 #define COEX_CFG_ANT FIELD32(0xff000000) /* * COEX_CFG_1 */ -#define COEX_CFG1 0x0044 +#define COEX_CFG1 0x0044 /* * COEX_CFG_2 */ -#define COEX_CFG2 0x0048 +#define COEX_CFG2 0x0048 #define BT_COEX_CFG1 FIELD32(0xff000000) #define BT_COEX_CFG0 FIELD32(0x00ff0000) #define WL_COEX_CFG1 FIELD32(0x0000ff00) @@ -235,8 +235,8 @@ #define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) #define PLL_CONTROL FIELD32(0x00070000) #define PLL_LPF_R1 FIELD32(0x00080000) -#define PLL_LPF_C1_CTRL FIELD32(0x00300000) -#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) +#define PLL_LPF_C1_CTRL FIELD32(0x00300000) +#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) #define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) #define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) #define PLL_LOCK_CTRL FIELD32(0x70000000) @@ -2164,7 +2164,7 @@ struct mac_iveiv_entry { */ #define RFCSR6_R1 FIELD8(0x03) #define RFCSR6_R2 FIELD8(0x40) -#define RFCSR6_TXDIV FIELD8(0x0c) +#define RFCSR6_TXDIV FIELD8(0x0c) /* bits for RF3053 */ #define RFCSR6_VCO_IC FIELD8(0xc0) @@ -2202,13 +2202,13 @@ struct mac_iveiv_entry { * RFCSR 12: */ #define RFCSR12_TX_POWER FIELD8(0x1f) -#define RFCSR12_DR0 FIELD8(0xe0) +#define RFCSR12_DR0 FIELD8(0xe0) /* * RFCSR 13: */ #define RFCSR13_TX_POWER FIELD8(0x1f) -#define RFCSR13_DR0 FIELD8(0xe0) +#define RFCSR13_DR0 FIELD8(0xe0) /* * RFCSR 15: @@ -2226,7 +2226,7 @@ struct mac_iveiv_entry { #define RFCSR17_TXMIXER_GAIN FIELD8(0x07) #define RFCSR17_TX_LO1_EN FIELD8(0x08) #define RFCSR17_R FIELD8(0x20) -#define RFCSR17_CODE FIELD8(0x7f) +#define RFCSR17_CODE FIELD8(0x7f) /* RFCSR 18 */ #define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) @@ -2449,7 +2449,7 @@ enum rt2800_eeprom_word { */ #define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) #define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) -#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) +#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) /* * EEPROM NIC Configuration 1 @@ -2471,18 +2471,18 @@ enum rt2800_eeprom_word { * DAC_TEST: 0: disable, 1: enable */ #define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) -#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) -#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) -#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) +#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) #define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010) #define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020) #define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040) #define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080) #define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100) #define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200) -#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) +#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) #define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800) -#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) +#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) #define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000) #define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000) @@ -2521,9 +2521,9 @@ enum rt2800_eeprom_word { * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved */ -#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) -#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) -#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) +#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) +#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) +#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) /* * EEPROM LNA @@ -2790,7 +2790,7 @@ enum rt2800_eeprom_word { #define MCU_CURRENT 0x36 #define MCU_LED 0x50 #define MCU_LED_STRENGTH 0x51 -#define MCU_LED_AG_CONF 0x52 +#define MCU_LED_AG_CONF 0x52 #define MCU_LED_ACT_CONF 0x53 #define MCU_LED_LED_POLARITY 0x54 #define MCU_RADAR 0x60 @@ -2799,7 +2799,7 @@ enum rt2800_eeprom_word { #define MCU_FREQ_OFFSET 0x74 #define MCU_BBP_SIGNAL 0x80 #define MCU_POWER_SAVE 0x83 -#define MCU_BAND_SELECT 0x91 +#define MCU_BAND_SELECT 0x91 /* * MCU mailbox tokens diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 88ce656f96c..c17fcf27272 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -24,9 +24,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -278,12 +276,9 @@ static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = { [EEPROM_LNA] = 0x0026, [EEPROM_EXT_LNA2] = 0x0027, [EEPROM_RSSI_BG] = 0x0028, - [EEPROM_TXPOWER_DELTA] = 0x0028, /* Overlaps with RSSI_BG */ [EEPROM_RSSI_BG2] = 0x0029, - [EEPROM_TXMIXER_GAIN_BG] = 0x0029, /* Overlaps with RSSI_BG2 */ [EEPROM_RSSI_A] = 0x002a, [EEPROM_RSSI_A2] = 0x002b, - [EEPROM_TXMIXER_GAIN_A] = 0x002b, /* Overlaps with RSSI_A2 */ [EEPROM_TXPOWER_BG1] = 0x0030, [EEPROM_TXPOWER_BG2] = 0x0037, [EEPROM_EXT_TXPOWER_BG3] = 0x003e, @@ -1010,10 +1005,9 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc) entry->skb->len + padding_len); /* - * Enable beaconing again. + * Restore beaconing state. */ - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); /* * Clean up beacon skb. @@ -1044,13 +1038,14 @@ static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev, void rt2800_clear_beacon(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 reg; + u32 orig_reg, reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ - rt2800_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &orig_reg); + reg = orig_reg; rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); @@ -1060,10 +1055,9 @@ void rt2800_clear_beacon(struct queue_entry *entry) rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx); /* - * Enabled beaconing again. + * Restore beaconing state. */ - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); + rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg); } EXPORT_SYMBOL_GPL(rt2800_clear_beacon); @@ -1783,7 +1777,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) rt2800_bbp_read(rt2x00dev, 3, &r3); if (rt2x00_rt(rt2x00dev, RT3572) && - test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + rt2x00_has_cap_bt_coexist(rt2x00dev)) rt2800_config_3572bt_ant(rt2x00dev); /* @@ -1795,7 +1789,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) break; case 2: if (rt2x00_rt(rt2x00dev, RT3572) && - test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + rt2x00_has_cap_bt_coexist(rt2x00dev)) rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1); else rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); @@ -1825,7 +1819,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) break; case 2: if (rt2x00_rt(rt2x00dev, RT3572) && - test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + rt2x00_has_cap_bt_coexist(rt2x00dev)) { rt2x00_set_field8(&r3, BBP3_RX_ADC, 1); rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); @@ -2029,13 +2023,6 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, rt2x00dev->default_ant.tx_chain_num <= 2); rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - msleep(1); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); @@ -2141,7 +2128,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { if (rf->channel <= 14) { rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); @@ -2650,7 +2637,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, if (rt2x00_rt(rt2x00dev, RT5392)) { rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr); - if (info->default_power1 > POWER_BOUND) + if (info->default_power2 > POWER_BOUND) rt2x00_set_field8(&rfcsr, RFCSR50_TX, POWER_BOUND); else rt2x00_set_field8(&rfcsr, RFCSR50_TX, @@ -2674,7 +2661,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, if (rf->channel <= 14) { int idx = rf->channel-1; - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { /* r55/r59 value array of channel 1~14 */ static const char r55_bt_rev[] = {0x83, 0x83, @@ -3152,6 +3139,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF3322: rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info); break; + case RF3070: case RF5360: case RF5370: case RF5372: @@ -3166,7 +3154,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); } - if (rt2x00_rf(rt2x00dev, RF3290) || + if (rt2x00_rf(rt2x00dev, RF3070) || + rt2x00_rf(rt2x00dev, RF3290) || rt2x00_rf(rt2x00dev, RF3322) || rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || @@ -3218,8 +3207,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rf->channel <= 14) { if (!rt2x00_rt(rt2x00dev, RT5390) && !rt2x00_rt(rt2x00dev, RT5392)) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, - &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 75, 0x46); } else { @@ -3244,7 +3232,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rt2x00_rt(rt2x00dev, RT3593)) rt2800_bbp_write(rt2x00dev, 83, 0x9a); - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) rt2800_bbp_write(rt2x00dev, 75, 0x46); else rt2800_bbp_write(rt2x00dev, 75, 0x50); @@ -3280,7 +3268,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, /* Turn on primary PAs */ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14); - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); else rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, @@ -3311,33 +3299,50 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - if (rt2x00_rt(rt2x00dev, RT3572)) + if (rt2x00_rt(rt2x00dev, RT3572)) { rt2800_rfcsr_write(rt2x00dev, 8, 0x80); + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + (2 * rt2x00dev->lna_gain); + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + } + if (rt2x00_rt(rt2x00dev, RT3593)) { - if (rt2x00_is_usb(rt2x00dev)) { - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - /* Band selection. GPIO #8 controls all paths */ + /* Band selection */ + if (rt2x00_is_usb(rt2x00dev) || + rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #8 controls all paths */ rt2x00_set_field32(®, GPIO_CTRL_DIR8, 0); if (rf->channel <= 14) rt2x00_set_field32(®, GPIO_CTRL_VAL8, 1); else rt2x00_set_field32(®, GPIO_CTRL_VAL8, 0); + } + /* LNA PE control. */ + if (rt2x00_is_usb(rt2x00dev)) { + /* GPIO #4 controls PE0 and PE1, + * GPIO #7 controls PE2 + */ rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); - /* LNA PE control. - * GPIO #4 controls PE0 and PE1, - * GPIO #7 controls PE2 - */ rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); - - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + } else if (rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #4 controls PE0, PE1 and PE2 */ + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); } + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + /* AGC init */ if (rf->channel <= 14) reg = 0x1c + 2 * rt2x00dev->lna_gain; @@ -3565,7 +3570,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev, { int delta; - if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_power_limit(rt2x00dev)) return 0; /* @@ -3594,7 +3599,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, if (rt2x00_rt(rt2x00dev, RT3593)) return min_t(u8, txpower, 0xc); - if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_power_limit(rt2x00dev)) { /* * Check if eirp txpower exceed txpower_limit. * We use OFDM 6M as criterion and its eirp txpower @@ -4264,6 +4269,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); break; case RF3053: + case RF3070: case RF3290: case RF5360: case RF5370: @@ -4405,6 +4411,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || rt2x00_rt(rt2x00dev, RT5592)) @@ -4412,8 +4419,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) else vgc = 0x2e + rt2x00dev->lna_gain; } else { /* 5GHZ band */ - if (rt2x00_rt(rt2x00dev, RT3572)) - vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3; + if (rt2x00_rt(rt2x00dev, RT3593)) + vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3; else if (rt2x00_rt(rt2x00dev, RT5592)) vgc = 0x24 + (2 * rt2x00dev->lna_gain); else { @@ -4431,11 +4438,17 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level != vgc_level) { - if (rt2x00_rt(rt2x00dev, RT5592)) { + if (rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, + vgc_level); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a); rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level); - } else + } else { rt2800_bbp_write(rt2x00dev, 66, vgc_level); + } + qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } @@ -4454,17 +4467,35 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) return; - /* - * When RSSI is better then -80 increase VGC level with 0x10, except - * for rt5592 chip. + + /* When RSSI is better than a certain threshold, increase VGC + * with a chip specific value in order to improve the balance + * between sensibility and noise isolation. */ vgc = rt2800_get_default_vgc(rt2x00dev); - if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65) - vgc += 0x20; - else if (qual->rssi > -80) - vgc += 0x10; + switch (rt2x00dev->chip.rt) { + case RT3572: + case RT3593: + if (qual->rssi > -65) { + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) + vgc += 0x20; + else + vgc += 0x10; + } + break; + + case RT5592: + if (qual->rssi > -65) + vgc += 0x20; + break; + + default: + if (qual->rssi > -80) + vgc += 0x10; + break; + } rt2800_set_vgc(rt2x00dev, qual, vgc); } @@ -5489,7 +5520,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) ant = (div_mode == 3) ? 1 : 0; /* check if this is a Bluetooth combo card */ - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { u32 reg; rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); @@ -5798,7 +5829,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { - if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_external_lna_bg(rt2x00dev)) rt2x00_set_field8(&rfcsr, RFCSR17_R, 1); } @@ -5985,7 +6016,7 @@ static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 20, 0xba); rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); rt2800_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 25, 0x03); rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { @@ -6419,7 +6450,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 7, 0x00); rt2800_rfcsr_write(rt2x00dev, 10, 0x53); rt2800_rfcsr_write(rt2x00dev, 11, 0x4a); - rt2800_rfcsr_write(rt2x00dev, 12, 0xc6); + rt2800_rfcsr_write(rt2x00dev, 12, 0x46); rt2800_rfcsr_write(rt2x00dev, 13, 0x9f); rt2800_rfcsr_write(rt2x00dev, 14, 0x00); rt2800_rfcsr_write(rt2x00dev, 15, 0x00); @@ -6432,7 +6463,8 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 22, 0x20); rt2800_rfcsr_write(rt2x00dev, 23, 0x00); rt2800_rfcsr_write(rt2x00dev, 24, 0x00); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + if (rt2x00_is_usb(rt2x00dev) && + rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) rt2800_rfcsr_write(rt2x00dev, 25, 0x80); else rt2800_rfcsr_write(rt2x00dev, 25, 0xc0); @@ -6441,7 +6473,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 28, 0x00); rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); rt2800_rfcsr_write(rt2x00dev, 31, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 33, 0x00); @@ -6452,10 +6484,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 38, 0x85); rt2800_rfcsr_write(rt2x00dev, 39, 0x1b); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); - else - rt2800_rfcsr_write(rt2x00dev, 40, 0x4b); + rt2800_rfcsr_write(rt2x00dev, 40, 0x0b); rt2800_rfcsr_write(rt2x00dev, 41, 0xbb); rt2800_rfcsr_write(rt2x00dev, 42, 0xd2); rt2800_rfcsr_write(rt2x00dev, 43, 0x9a); @@ -6476,16 +6505,26 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 53, 0x84); rt2800_rfcsr_write(rt2x00dev, 54, 0x78); rt2800_rfcsr_write(rt2x00dev, 55, 0x44); - rt2800_rfcsr_write(rt2x00dev, 56, 0x22); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) + rt2800_rfcsr_write(rt2x00dev, 56, 0x42); + else + rt2800_rfcsr_write(rt2x00dev, 56, 0x22); rt2800_rfcsr_write(rt2x00dev, 57, 0x80); rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 59, 0x63); + rt2800_rfcsr_write(rt2x00dev, 59, 0x8f); rt2800_rfcsr_write(rt2x00dev, 60, 0x45); - if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) - rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); - else - rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); + if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { + if (rt2x00_is_usb(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xd1); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xd5); + } else { + if (rt2x00_is_usb(rt2x00dev)) + rt2800_rfcsr_write(rt2x00dev, 61, 0xdd); + else + rt2800_rfcsr_write(rt2x00dev, 61, 0xb5); + } rt2800_rfcsr_write(rt2x00dev, 62, 0x00); rt2800_rfcsr_write(rt2x00dev, 63, 0x00); @@ -6499,7 +6538,6 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev) rt2800_rf_init_calibration(rt2x00dev, 2); rt2800_rfcsr_write(rt2x00dev, 1, 0x17); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); rt2800_rfcsr_write(rt2x00dev, 3, 0x88); rt2800_rfcsr_write(rt2x00dev, 5, 0x10); rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); @@ -6569,7 +6607,6 @@ static void rt2800_init_rfcsr_5592(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 1, 0x3F); rt2800_rfcsr_write(rt2x00dev, 3, 0x08); - rt2800_rfcsr_write(rt2x00dev, 3, 0x08); rt2800_rfcsr_write(rt2x00dev, 5, 0x10); rt2800_rfcsr_write(rt2x00dev, 6, 0xE4); rt2800_rfcsr_write(rt2x00dev, 7, 0x00); @@ -6653,17 +6690,20 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) u16 word; /* - * Initialize all registers. + * Initialize MAC registers. */ if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_registers(rt2x00dev))) return -EIO; + /* + * Wait BBP/RF to wake up. + */ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) return -EIO; /* - * Send signal to firmware during boot time. + * Send signal during boot time to initialize firmware. */ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); @@ -6672,9 +6712,15 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); msleep(1); + /* + * Make sure BBP is up and running. + */ if (unlikely(rt2800_wait_bbp_ready(rt2x00dev))) return -EIO; + /* + * Initialize BBP/RF registers. + */ rt2800_init_bbp(rt2x00dev); rt2800_init_rfcsr(rt2x00dev); @@ -7021,6 +7067,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF3022: case RF3052: case RF3053: + case RF3070: case RF3290: case RF3320: case RF3322: @@ -7203,7 +7250,7 @@ static const struct rf_channel rf_vals[] = { /* * RF value list for rt3xxx - * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052) + * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053) */ static const struct rf_channel rf_vals_3x[] = { {1, 241, 2, 2 }, @@ -7399,72 +7446,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = { {196, 83, 0, 12, 1}, }; -static const struct rf_channel rf_vals_3053[] = { - /* Channel, N, R, K */ - {1, 241, 2, 2}, - {2, 241, 2, 7}, - {3, 242, 2, 2}, - {4, 242, 2, 7}, - {5, 243, 2, 2}, - {6, 243, 2, 7}, - {7, 244, 2, 2}, - {8, 244, 2, 7}, - {9, 245, 2, 2}, - {10, 245, 2, 7}, - {11, 246, 2, 2}, - {12, 246, 2, 7}, - {13, 247, 2, 2}, - {14, 248, 2, 4}, - - {36, 0x56, 0, 4}, - {38, 0x56, 0, 6}, - {40, 0x56, 0, 8}, - {44, 0x57, 0, 0}, - {46, 0x57, 0, 2}, - {48, 0x57, 0, 4}, - {52, 0x57, 0, 8}, - {54, 0x57, 0, 10}, - {56, 0x58, 0, 0}, - {60, 0x58, 0, 4}, - {62, 0x58, 0, 6}, - {64, 0x58, 0, 8}, - - {100, 0x5B, 0, 8}, - {102, 0x5B, 0, 10}, - {104, 0x5C, 0, 0}, - {108, 0x5C, 0, 4}, - {110, 0x5C, 0, 6}, - {112, 0x5C, 0, 8}, - - /* NOTE: Channel 114 has been removed intentionally. - * The EEPROM contains no TX power values for that, - * and it is disabled in the vendor driver as well. - */ - - {116, 0x5D, 0, 0}, - {118, 0x5D, 0, 2}, - {120, 0x5D, 0, 4}, - {124, 0x5D, 0, 8}, - {126, 0x5D, 0, 10}, - {128, 0x5E, 0, 0}, - {132, 0x5E, 0, 4}, - {134, 0x5E, 0, 6}, - {136, 0x5E, 0, 8}, - {140, 0x5F, 0, 0}, - - {149, 0x5F, 0, 9}, - {151, 0x5F, 0, 11}, - {153, 0x60, 0, 1}, - {157, 0x60, 0, 5}, - {159, 0x60, 0, 7}, - {161, 0x60, 0, 9}, - {165, 0x61, 0, 1}, - {167, 0x61, 0, 3}, - {169, 0x61, 0, 5}, - {171, 0x61, 0, 7}, - {173, 0x61, 0, 9}, -}; - static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; @@ -7473,14 +7454,12 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) char *default_power2; char *default_power3; unsigned int i; - u16 eeprom; u32 reg; /* - * Disable powersaving as default on PCI devices. + * Disable powersaving as default. */ - if (rt2x00_is_pci(rt2x00dev) || rt2x00_is_soc(rt2x00dev)) - rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + rt2x00dev->hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; /* * Initialize all hw fields. @@ -7522,48 +7501,48 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->max_report_rates = 7; rt2x00dev->hw->max_rate_tries = 1; - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - /* * Initialize hw_mode information. */ - spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(rt2x00dev, RF2820) || - rt2x00_rf(rt2x00dev, RF2720)) { + switch (rt2x00dev->chip.rf) { + case RF2720: + case RF2820: spec->num_channels = 14; spec->channels = rf_vals; - } else if (rt2x00_rf(rt2x00dev, RF2850) || - rt2x00_rf(rt2x00dev, RF2750)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; + break; + + case RF2750: + case RF2850: spec->num_channels = ARRAY_SIZE(rf_vals); spec->channels = rf_vals; - } else if (rt2x00_rf(rt2x00dev, RF3020) || - rt2x00_rf(rt2x00dev, RF2020) || - rt2x00_rf(rt2x00dev, RF3021) || - rt2x00_rf(rt2x00dev, RF3022) || - rt2x00_rf(rt2x00dev, RF3290) || - rt2x00_rf(rt2x00dev, RF3320) || - rt2x00_rf(rt2x00dev, RF3322) || - rt2x00_rf(rt2x00dev, RF5360) || - rt2x00_rf(rt2x00dev, RF5370) || - rt2x00_rf(rt2x00dev, RF5372) || - rt2x00_rf(rt2x00dev, RF5390) || - rt2x00_rf(rt2x00dev, RF5392)) { + break; + + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3070: + case RF3290: + case RF3320: + case RF3322: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: spec->num_channels = 14; spec->channels = rf_vals_3x; - } else if (rt2x00_rf(rt2x00dev, RF3052)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; + break; + + case RF3052: + case RF3053: spec->num_channels = ARRAY_SIZE(rf_vals_3x); spec->channels = rf_vals_3x; - } else if (rt2x00_rf(rt2x00dev, RF3053)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_3053); - spec->channels = rf_vals_3053; - } else if (rt2x00_rf(rt2x00dev, RF5592)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; + break; + case RF5592: rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, ®); if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) { spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40); @@ -7572,11 +7551,16 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20); spec->channels = rf_vals_5592_xtal20; } + break; } if (WARN_ON_ONCE(!spec->channels)) return -ENODEV; + spec->supported_bands = SUPPORT_BAND_2GHZ; + if (spec->num_channels > 14) + spec->supported_bands |= SUPPORT_BAND_5GHZ; + /* * Initialize HT information. */ @@ -7591,22 +7575,21 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40; - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) >= 2) + if (rt2x00dev->default_ant.tx_chain_num >= 2) spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; - spec->ht.cap |= - rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) << - IEEE80211_HT_CAP_RX_STBC_SHIFT; + spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num << + IEEE80211_HT_CAP_RX_STBC_SHIFT; spec->ht.ampdu_factor = 3; spec->ht.ampdu_density = 4; spec->ht.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED | IEEE80211_HT_MCS_TX_RX_DIFF | - ((rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + ((rt2x00dev->default_ant.tx_chain_num - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - switch (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH)) { + switch (rt2x00dev->default_ant.rx_chain_num) { case 3: spec->ht.mcs.rx_mask[2] = 0xff; case 2: @@ -7671,6 +7654,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3320: case RF3052: case RF3053: + case RF3070: case RF3290: case RF5360: case RF5370: diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index a94ba447e63..3019db637a4 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -14,9 +14,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef RT2800LIB_H diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c new file mode 100644 index 00000000000..de4790b41be --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800mmio.c @@ -0,0 +1,871 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + * <http://rt2x00.serialmonkey.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Module: rt2800mmio + * Abstract: rt2800 MMIO device routines. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/export.h> + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* + * TX descriptor initialization + */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) +{ + return (__le32 *) entry->skb->data; +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); + +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + const unsigned int txwi_size = entry->queue->winfo_size; + + /* + * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 + * must contains a TXWI structure + 802.11 header + padding + 802.11 + * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and + * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 + * data. It means that LAST_SEC0 is always 0. + */ + + /* + * Initialize TX descriptor + */ + word = 0; + rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); + rt2x00_desc_write(txd, 0, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, + !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); + rt2x00_desc_write(txd, 1, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W2_SD_PTR1, + skbdesc->skb_dma + txwi_size); + rt2x00_desc_write(txd, 2, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W3_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W3_QSEL, 2); + rt2x00_desc_write(txd, 3, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} +EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); + +/* + * RX control handlers + */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *rxd = entry_priv->desc; + u32 word; + + rt2x00_desc_read(rxd, 3, &word); + + if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + /* + * Unfortunately we don't know the cipher type used during + * decryption. This prevents us from correct providing + * correct statistics through debugfs. + */ + rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); + + if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(word, RXD_W3_L2PAD)) + rxdesc->dev_flags |= RXDONE_L2PAD; + + /* + * Process the RXWI structure that is at the start of the buffer. + */ + rt2800_process_rxwi(entry, rxdesc); +} +EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); + +/* + * Interrupt functions. + */ +static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_conf conf = { .flags = 0 }; + struct rt2x00lib_conf libconf = { .conf = &conf }; + + rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); +} + +static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) +{ + __le32 *txwi; + u32 word; + int wcid, tx_wcid; + + wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); + + txwi = rt2800_drv_get_txwi(entry); + rt2x00_desc_read(txwi, 1, &word); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + + return (tx_wcid == wcid); +} + +static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * rt2800pci hardware might reorder frames when exchanging traffic + * with multiple BA enabled STAs. + * + * For example, a tx queue + * [ STA1 | STA2 | STA1 | STA2 ] + * can result in tx status reports + * [ STA1 | STA1 | STA2 | STA2 ] + * when the hw decides to aggregate the frames for STA1 into one AMPDU. + * + * To mitigate this effect, associate the tx status to the first frame + * in the tx queue with a matching wcid. + */ + if (rt2800mmio_txdone_entry_check(entry, status) && + !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} + +static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * Find the first frame without tx status and assign this status to it + * regardless if it matches or not. + */ + if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} +static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, + void *data) +{ + if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + rt2800_txdone_entry(entry, entry->status, + rt2800mmio_get_txwi(entry)); + return false; + } + + /* No more frames to release */ + return true; +} + +static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + u32 status; + u8 qid; + int max_tx_done = 16; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { + qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); + if (unlikely(qid >= QID_RX)) { + /* + * Unknown queue, this shouldn't happen. Just drop + * this tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", + qid); + break; + } + + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + if (unlikely(queue == NULL)) { + /* + * The queue is NULL, this shouldn't happen. Stop + * processing here and drop the tx status + */ + rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", + qid); + break; + } + + if (unlikely(rt2x00queue_empty(queue))) { + /* + * The queue is empty. Stop processing here + * and drop the tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + /* + * Let's associate this tx status with the first + * matching frame. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_find_entry)) { + /* + * We cannot match the tx status to any frame, so just + * use the first one. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_match_first)) { + rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", + qid); + break; + } + } + + /* + * Release all frames with a valid tx status. + */ + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, NULL, + rt2800mmio_txdone_release_entries); + + if (--max_tx_done == 0) + break; + } + + return !max_tx_done; +} + +static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 1); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +void rt2800mmio_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2800mmio_txdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + + /* + * No need to enable the tx status interrupt here as we always + * leave it enabled to minimize the possibility of a tx status + * register overflow. See comment in interrupt handler. + */ +} +EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); + +void rt2800mmio_pretbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_pretbtt(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); + +void rt2800mmio_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + + rt2x00lib_beacondone(rt2x00dev); + + if (rt2x00dev->intf_ap_count) { + /* + * The rt2800pci hardware tbtt timer is off by 1us per tbtt + * causing beacon skew and as a result causing problems with + * some powersaving clients over time. Shorten the beacon + * interval every 64 beacons by 64us to mitigate this effect. + */ + if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16) - 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16)); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + drv_data->tbtt_tick++; + drv_data->tbtt_tick %= BCN_TBTT_OFFSET; + } + + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); + +void rt2800mmio_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); +} +EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); + +void rt2800mmio_autowake_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2800mmio_wakeup(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, + INT_MASK_CSR_AUTO_WAKEUP); +} +EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); + +static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) +{ + u32 status; + int i; + + /* + * The TX_FIFO_STATUS interrupt needs special care. We should + * read TX_STA_FIFO but we should do it immediately as otherwise + * the register can overflow and we would lose status reports. + * + * Hence, read the TX_STA_FIFO register and copy all tx status + * reports into a kernel FIFO which is handled in the txstatus + * tasklet. We use a tasklet to process the tx status reports + * because we can schedule the tasklet multiple times (when the + * interrupt fires again during tx status processing). + * + * Furthermore we don't disable the TX_FIFO_STATUS + * interrupt here but leave it enabled so that the TX_STA_FIFO + * can also be read while the tx status tasklet gets executed. + * + * Since we have only one producer and one consumer we don't + * need to lock the kfifo. + */ + for (i = 0; i < rt2x00dev->tx->limit; i++) { + rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); + + if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) + break; + + if (!kfifo_put(&rt2x00dev->txstatus_fifo, status)) { + rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); + break; + } + } + + /* Schedule the tasklet for processing the tx status. */ + tasklet_schedule(&rt2x00dev->txstatus_tasklet); +} + +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg, mask; + + /* Read status and ACK all interrupts */ + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + /* + * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits + * for interrupts and interrupt masks we can just use the value of + * INT_SOURCE_CSR to create the interrupt mask. + */ + mask = ~reg; + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { + rt2800mmio_txstatus_interrupt(rt2x00dev); + /* + * Never disable the TX_FIFO_STATUS interrupt. + */ + rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); + } + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) + tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) + tasklet_schedule(&rt2x00dev->autowake_tasklet); + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + reg &= mask; + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock(&rt2x00dev->irqmask_lock); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(rt2800mmio_interrupt); + +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + } + + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + reg = 0; + if (state == STATE_RADIO_IRQ_ON) { + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); + } + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Wait for possibly running tasklets to finish. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); + +/* + * Queue handlers. + */ +void rt2800mmio_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_start_queue); + +void rt2800mmio_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry *entry; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), + entry->entry_idx); + break; + case QID_MGMT: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), + entry->entry_idx); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); + +void rt2800mmio_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + + /* + * Wait for current invocation to finish. The tasklet + * won't be scheduled anymore afterwards since we disabled + * the TBTT and PRE TBTT timer. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); + +void rt2800mmio_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 64; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_queue_init); + +/* + * Initialization functions. + */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); + +void rt2800mmio_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 0, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); + rt2x00_desc_write(entry_priv->desc, 1, word); + + /* + * Set RX IDX in register to inform hardware that we have + * handled this entry and it is available for reuse again. + */ + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + entry->entry_idx); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); + rt2x00_desc_write(entry_priv->desc, 1, word); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); + +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + + /* + * Initialize registers. + */ + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, + rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, + rt2x00dev->tx[1].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); + + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, + rt2x00dev->tx[2].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); + + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, + rt2x00dev->tx[3].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, + rt2x00dev->rx[0].limit); + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + rt2x00dev->rx[0].limit - 1); + rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); + + rt2800_disable_wpdma(rt2x00dev); + + rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_queues); + +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Reset DMA indexes + */ + rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); + rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); + + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + + if (rt2x00_is_pcie(rt2x00dev) && + (rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592))) { + rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); + rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); + rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); + rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); + } + + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + reg = 0; + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_registers); + +/* + * Device state switch handlers. + */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* Wait for DMA, ignore error until we initialize queues. */ + rt2800_wait_wpdma_ready(rt2x00dev); + + if (unlikely(rt2800mmio_init_queues(rt2x00dev))) + return -EIO; + + return rt2800_enable_radio(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2800 MMIO library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h new file mode 100644 index 00000000000..b63312ce3f2 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800mmio.h @@ -0,0 +1,163 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + * <http://rt2x00.serialmonkey.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Module: rt2800mmio + * Abstract: forward declarations for the rt2800mmio module. + */ + +#ifndef RT2800MMIO_H +#define RT2800MMIO_H + +/* + * Queue register offset macros + */ +#define TX_QUEUE_REG_OFFSET 0x10 +#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE (4 * sizeof(__le32)) +#define RXD_DESC_SIZE (4 * sizeof(__le32)) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) +#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) +#define TXD_W1_BURST FIELD32(0x00008000) +#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) +#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) +#define TXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) + +/* + * Word3 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + */ +#define TXD_W3_WIV FIELD32(0x01000000) +#define TXD_W3_QSEL FIELD32(0x06000000) +#define TXD_W3_TCO FIELD32(0x20000000) +#define TXD_W3_UCO FIELD32(0x40000000) +#define TXD_W3_ICO FIELD32(0x80000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_SDP0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define RXD_W1_SDL1 FIELD32(0x00003fff) +#define RXD_W1_SDL0 FIELD32(0x3fff0000) +#define RXD_W1_LS0 FIELD32(0x40000000) +#define RXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define RXD_W2_SDP1 FIELD32(0xffffffff) + +/* + * Word3 + * AMSDU: RX with 802.3 header, not 802.11 header. + * DECRYPTED: This frame is being decrypted. + */ +#define RXD_W3_BA FIELD32(0x00000001) +#define RXD_W3_DATA FIELD32(0x00000002) +#define RXD_W3_NULLDATA FIELD32(0x00000004) +#define RXD_W3_FRAG FIELD32(0x00000008) +#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W3_MULTICAST FIELD32(0x00000020) +#define RXD_W3_BROADCAST FIELD32(0x00000040) +#define RXD_W3_MY_BSS FIELD32(0x00000080) +#define RXD_W3_CRC_ERROR FIELD32(0x00000100) +#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W3_AMSDU FIELD32(0x00000800) +#define RXD_W3_HTC FIELD32(0x00001000) +#define RXD_W3_RSSI FIELD32(0x00002000) +#define RXD_W3_L2PAD FIELD32(0x00004000) +#define RXD_W3_AMPDU FIELD32(0x00008000) +#define RXD_W3_DECRYPTED FIELD32(0x00010000) +#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) +#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) + +/* TX descriptor initialization */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry); +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc); + +/* RX control handlers */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc); + +/* Interrupt functions */ +void rt2800mmio_txstatus_tasklet(unsigned long data); +void rt2800mmio_pretbtt_tasklet(unsigned long data); +void rt2800mmio_tbtt_tasklet(unsigned long data); +void rt2800mmio_rxdone_tasklet(unsigned long data); +void rt2800mmio_autowake_tasklet(unsigned long data); +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance); +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state); + +/* Queue handlers */ +void rt2800mmio_start_queue(struct data_queue *queue); +void rt2800mmio_kick_queue(struct data_queue *queue); +void rt2800mmio_stop_queue(struct data_queue *queue); +void rt2800mmio_queue_init(struct data_queue *queue); + +/* Initialization functions */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry); +void rt2800mmio_clear_entry(struct queue_entry *entry); +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev); +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev); + +/* Device state switch handlers. */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev); + +#endif /* RT2800MMIO_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index f8f2abbfbb6..a5b32ca2cf0 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -20,9 +20,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -37,14 +35,13 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/platform_device.h> #include <linux/eeprom_93cx6.h> #include "rt2x00.h" #include "rt2x00mmio.h" #include "rt2x00pci.h" -#include "rt2x00soc.h" #include "rt2800lib.h" +#include "rt2800mmio.h" #include "rt2800.h" #include "rt2800pci.h" @@ -90,27 +87,6 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); } -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) -static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) -{ - void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); - - if (!base_addr) - return -ENOMEM; - - memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); - - iounmap(base_addr); - return 0; -} -#else -static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) -{ - return -ENOMEM; -} -#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */ - -#ifdef CONFIG_PCI static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; @@ -183,112 +159,6 @@ static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { return rt2800_read_eeprom_efuse(rt2x00dev); } -#else -static inline int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) -{ - return -EOPNOTSUPP; -} - -static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) -{ - return 0; -} - -static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) -{ - return -EOPNOTSUPP; -} -#endif /* CONFIG_PCI */ - -/* - * Queue handlers. - */ -static void rt2800pci_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); - rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); - rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); - break; - default: - break; - } -} - -static void rt2800pci_kick_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - struct queue_entry *entry; - - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - entry = rt2x00queue_get_entry(queue, Q_INDEX); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), - entry->entry_idx); - break; - case QID_MGMT: - entry = rt2x00queue_get_entry(queue, Q_INDEX); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), - entry->entry_idx); - break; - default: - break; - } -} - -static void rt2800pci_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); - rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); - rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); - - /* - * Wait for current invocation to finish. The tasklet - * won't be scheduled anymore afterwards since we disabled - * the TBTT and PRE TBTT timer. - */ - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - - break; - default: - break; - } -} /* * Firmware functions @@ -332,217 +202,13 @@ static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, } /* - * Initialization functions. - */ -static bool rt2800pci_get_entry_state(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 1, &word); - - return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); - } else { - rt2x00_desc_read(entry_priv->desc, 1, &word); - - return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); - } -} - -static void rt2800pci_clear_entry(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); - rt2x00_desc_write(entry_priv->desc, 0, word); - - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); - rt2x00_desc_write(entry_priv->desc, 1, word); - - /* - * Set RX IDX in register to inform hardware that we have - * handled this entry and it is available for reuse again. - */ - rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, - entry->entry_idx); - } else { - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); - rt2x00_desc_write(entry_priv->desc, 1, word); - } -} - -static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct queue_entry_priv_mmio *entry_priv; - - /* - * Initialize registers. - */ - entry_priv = rt2x00dev->tx[0].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, - rt2x00dev->tx[0].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); - - entry_priv = rt2x00dev->tx[1].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, - rt2x00dev->tx[1].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); - - entry_priv = rt2x00dev->tx[2].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, - rt2x00dev->tx[2].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); - - entry_priv = rt2x00dev->tx[3].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, - rt2x00dev->tx[3].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); - - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); - - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); - - entry_priv = rt2x00dev->rx->entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, - rt2x00dev->rx[0].limit); - rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, - rt2x00dev->rx[0].limit - 1); - rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); - - rt2800_disable_wpdma(rt2x00dev); - - rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); - - return 0; -} - -/* * Device state switch handlers. */ -static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - unsigned long flags; - - /* - * When interrupts are being enabled, the interrupt registers - * should clear the register to assure a clean state. - */ - if (state == STATE_RADIO_IRQ_ON) { - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - } - - spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - reg = 0; - if (state == STATE_RADIO_IRQ_ON) { - rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); - rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); - rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); - rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); - rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); - } - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); - - if (state == STATE_RADIO_IRQ_OFF) { - /* - * Wait for possibly running tasklets to finish. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->autowake_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - } -} - -static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - /* - * Reset DMA indexes - */ - rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); - rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); - - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); - - if (rt2x00_is_pcie(rt2x00dev) && - (rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT3593) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592))) { - rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); - rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); - rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); - rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); - } - - rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); - - reg = 0; - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - return 0; -} - static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) { int retval; - /* Wait for DMA, ignore error until we initialize queues. */ - rt2800_wait_wpdma_ready(rt2x00dev); - - if (unlikely(rt2800pci_init_queues(rt2x00dev))) - return -EIO; - - retval = rt2800_enable_radio(rt2x00dev); + retval = rt2800mmio_enable_radio(rt2x00dev); if (retval) return retval; @@ -559,15 +225,6 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) return retval; } -static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - if (rt2x00_is_soc(rt2x00dev)) { - rt2800_disable_radio(rt2x00dev); - rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); - } -} - static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { @@ -601,12 +258,11 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, * After the radio has been disabled, the device should * be put to sleep for powersaving. */ - rt2800pci_disable_radio(rt2x00dev); rt2800pci_set_state(rt2x00dev, STATE_SLEEP); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: - rt2800pci_toggle_irq(rt2x00dev, state); + rt2800mmio_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -627,479 +283,13 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, } /* - * TX descriptor initialization - */ -static __le32 *rt2800pci_get_txwi(struct queue_entry *entry) -{ - return (__le32 *) entry->skb->data; -} - -static void rt2800pci_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *txd = entry_priv->desc; - u32 word; - const unsigned int txwi_size = entry->queue->winfo_size; - - /* - * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 - * must contains a TXWI structure + 802.11 header + padding + 802.11 - * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and - * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 - * data. It means that LAST_SEC0 is always 0. - */ - - /* - * Initialize TX descriptor - */ - word = 0; - rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); - rt2x00_desc_write(txd, 0, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); - rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, - !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_BURST, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); - rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); - rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); - rt2x00_desc_write(txd, 1, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W2_SD_PTR1, - skbdesc->skb_dma + txwi_size); - rt2x00_desc_write(txd, 2, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W3_WIV, - !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W3_QSEL, 2); - rt2x00_desc_write(txd, 3, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} - -/* - * RX control handlers - */ -static void rt2800pci_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *rxd = entry_priv->desc; - u32 word; - - rt2x00_desc_read(rxd, 3, &word); - - if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - - /* - * Unfortunately we don't know the cipher type used during - * decryption. This prevents us from correct providing - * correct statistics through debugfs. - */ - rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); - - if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { - /* - * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. Unfortunately the descriptor doesn't contain - * any fields with the EIV/IV data either, so they can't - * be restored by rt2x00lib. - */ - rxdesc->flags |= RX_FLAG_IV_STRIPPED; - - /* - * The hardware has already checked the Michael Mic and has - * stripped it from the frame. Signal this to mac80211. - */ - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; - - if (rt2x00_get_field32(word, RXD_W3_L2PAD)) - rxdesc->dev_flags |= RXDONE_L2PAD; - - /* - * Process the RXWI structure that is at the start of the buffer. - */ - rt2800_process_rxwi(entry, rxdesc); -} - -/* - * Interrupt functions. - */ -static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev) -{ - struct ieee80211_conf conf = { .flags = 0 }; - struct rt2x00lib_conf libconf = { .conf = &conf }; - - rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); -} - -static bool rt2800pci_txdone_entry_check(struct queue_entry *entry, u32 status) -{ - __le32 *txwi; - u32 word; - int wcid, tx_wcid; - - wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); - - txwi = rt2800_drv_get_txwi(entry); - rt2x00_desc_read(txwi, 1, &word); - tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); - - return (tx_wcid == wcid); -} - -static bool rt2800pci_txdone_find_entry(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * rt2800pci hardware might reorder frames when exchanging traffic - * with multiple BA enabled STAs. - * - * For example, a tx queue - * [ STA1 | STA2 | STA1 | STA2 ] - * can result in tx status reports - * [ STA1 | STA1 | STA2 | STA2 ] - * when the hw decides to aggregate the frames for STA1 into one AMPDU. - * - * To mitigate this effect, associate the tx status to the first frame - * in the tx queue with a matching wcid. - */ - if (rt2800pci_txdone_entry_check(entry, status) && - !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} - -static bool rt2800pci_txdone_match_first(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * Find the first frame without tx status and assign this status to it - * regardless if it matches or not. - */ - if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} -static bool rt2800pci_txdone_release_entries(struct queue_entry *entry, - void *data) -{ - if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - rt2800_txdone_entry(entry, entry->status, - rt2800pci_get_txwi(entry)); - return false; - } - - /* No more frames to release */ - return true; -} - -static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - u32 status; - u8 qid; - int max_tx_done = 16; - - while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { - qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); - if (unlikely(qid >= QID_RX)) { - /* - * Unknown queue, this shouldn't happen. Just drop - * this tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", - qid); - break; - } - - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - if (unlikely(queue == NULL)) { - /* - * The queue is NULL, this shouldn't happen. Stop - * processing here and drop the tx status - */ - rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", - qid); - break; - } - - if (unlikely(rt2x00queue_empty(queue))) { - /* - * The queue is empty. Stop processing here - * and drop the tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); - break; - } - - /* - * Let's associate this tx status with the first - * matching frame. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800pci_txdone_find_entry)) { - /* - * We cannot match the tx status to any frame, so just - * use the first one. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800pci_txdone_match_first)) { - rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", - qid); - break; - } - } - - /* - * Release all frames with a valid tx status. - */ - rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, NULL, - rt2800pci_txdone_release_entries); - - if (--max_tx_done == 0) - break; - } - - return !max_tx_done; -} - -static inline void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, irq_field, 1); - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -static void rt2800pci_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2800pci_txdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - - /* - * No need to enable the tx status interrupt here as we always - * leave it enabled to minimize the possibility of a tx status - * register overflow. See comment in interrupt handler. - */ -} - -static void rt2800pci_pretbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2x00lib_pretbtt(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); -} - -static void rt2800pci_tbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u32 reg; - - rt2x00lib_beacondone(rt2x00dev); - - if (rt2x00dev->intf_ap_count) { - /* - * The rt2800pci hardware tbtt timer is off by 1us per tbtt - * causing beacon skew and as a result causing problems with - * some powersaving clients over time. Shorten the beacon - * interval every 64 beacons by 64us to mitigate this effect. - */ - if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - (rt2x00dev->beacon_int * 16) - 1); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - (rt2x00dev->beacon_int * 16)); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } - drv_data->tbtt_tick++; - drv_data->tbtt_tick %= BCN_TBTT_OFFSET; - } - - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); -} - -static void rt2800pci_rxdone_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2x00mmio_rxdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); -} - -static void rt2800pci_autowake_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2800pci_wakeup(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP); -} - -static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) -{ - u32 status; - int i; - - /* - * The TX_FIFO_STATUS interrupt needs special care. We should - * read TX_STA_FIFO but we should do it immediately as otherwise - * the register can overflow and we would lose status reports. - * - * Hence, read the TX_STA_FIFO register and copy all tx status - * reports into a kernel FIFO which is handled in the txstatus - * tasklet. We use a tasklet to process the tx status reports - * because we can schedule the tasklet multiple times (when the - * interrupt fires again during tx status processing). - * - * Furthermore we don't disable the TX_FIFO_STATUS - * interrupt here but leave it enabled so that the TX_STA_FIFO - * can also be read while the tx status tasklet gets executed. - * - * Since we have only one producer and one consumer we don't - * need to lock the kfifo. - */ - for (i = 0; i < rt2x00dev->tx->limit; i++) { - rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); - - if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) - break; - - if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { - rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); - break; - } - } - - /* Schedule the tasklet for processing the tx status. */ - tasklet_schedule(&rt2x00dev->txstatus_tasklet); -} - -static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) -{ - struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg, mask; - - /* Read status and ACK all interrupts */ - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - - if (!reg) - return IRQ_NONE; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return IRQ_HANDLED; - - /* - * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits - * for interrupts and interrupt masks we can just use the value of - * INT_SOURCE_CSR to create the interrupt mask. - */ - mask = ~reg; - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { - rt2800pci_txstatus_interrupt(rt2x00dev); - /* - * Never disable the TX_FIFO_STATUS interrupt. - */ - rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); - } - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) - tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) - tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) - tasklet_schedule(&rt2x00dev->autowake_tasklet); - - /* - * Disable all interrupts for which a tasklet was scheduled right now, - * the tasklet will reenable the appropriate interrupts. - */ - spin_lock(&rt2x00dev->irqmask_lock); - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - reg &= mask; - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock(&rt2x00dev->irqmask_lock); - - return IRQ_HANDLED; -} - -/* * Device probe functions. */ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) { int retval; - if (rt2x00_is_soc(rt2x00dev)) - retval = rt2800pci_read_eeprom_soc(rt2x00dev); - else if (rt2800pci_efuse_detect(rt2x00dev)) + if (rt2800pci_efuse_detect(rt2x00dev)) retval = rt2800pci_read_eeprom_efuse(rt2x00dev); else retval = rt2800pci_read_eeprom_pci(rt2x00dev); @@ -1145,25 +335,25 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = { .read_eeprom = rt2800pci_read_eeprom, .hwcrypt_disabled = rt2800pci_hwcrypt_disabled, .drv_write_firmware = rt2800pci_write_firmware, - .drv_init_registers = rt2800pci_init_registers, - .drv_get_txwi = rt2800pci_get_txwi, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, }; static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { - .irq_handler = rt2800pci_interrupt, - .txstatus_tasklet = rt2800pci_txstatus_tasklet, - .pretbtt_tasklet = rt2800pci_pretbtt_tasklet, - .tbtt_tasklet = rt2800pci_tbtt_tasklet, - .rxdone_tasklet = rt2800pci_rxdone_tasklet, - .autowake_tasklet = rt2800pci_autowake_tasklet, + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, .probe_hw = rt2800_probe_hw, .get_firmware_name = rt2800pci_get_firmware_name, .check_firmware = rt2800_check_firmware, .load_firmware = rt2800_load_firmware, .initialize = rt2x00mmio_initialize, .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt2800pci_get_entry_state, - .clear_entry = rt2800pci_clear_entry, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, .set_device_state = rt2800pci_set_device_state, .rfkill_poll = rt2800_rfkill_poll, .link_stats = rt2800_link_stats, @@ -1171,15 +361,15 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .link_tuner = rt2800_link_tuner, .gain_calibration = rt2800_gain_calibration, .vco_calibration = rt2800_vco_calibration, - .start_queue = rt2800pci_start_queue, - .kick_queue = rt2800pci_kick_queue, - .stop_queue = rt2800pci_stop_queue, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt2800pci_write_tx_desc, + .write_tx_desc = rt2800mmio_write_tx_desc, .write_tx_data = rt2800_write_tx_data, .write_beacon = rt2800_write_beacon, .clear_beacon = rt2800_clear_beacon, - .fill_rxdone = rt2800pci_fill_rxdone, + .fill_rxdone = rt2800mmio_fill_rxdone, .config_shared_key = rt2800_config_shared_key, .config_pairwise_key = rt2800_config_pairwise_key, .config_filter = rt2800_config_filter, @@ -1191,49 +381,6 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .sta_remove = rt2800_sta_remove, }; -static void rt2800pci_queue_init(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - unsigned short txwi_size, rxwi_size; - - rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); - - switch (queue->qid) { - case QID_RX: - queue->limit = 128; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->winfo_size = rxwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 64; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_BEACON: - queue->limit = 8; - queue->data_size = 0; /* No DMA required for beacons */ - queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_ATIM: - /* fallthrough */ - default: - BUG(); - break; - } -} - static const struct rt2x00_ops rt2800pci_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), @@ -1241,7 +388,7 @@ static const struct rt2x00_ops rt2800pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2800pci_queue_init, + .queue_init = rt2800mmio_queue_init, .lib = &rt2800pci_rt2x00_ops, .drv = &rt2800pci_rt2800_ops, .hw = &rt2800pci_mac80211_ops, @@ -1253,7 +400,6 @@ static const struct rt2x00_ops rt2800pci_ops = { /* * RT2800pci module information. */ -#ifdef CONFIG_PCI static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { { PCI_DEVICE(0x1814, 0x0601) }, { PCI_DEVICE(0x1814, 0x0681) }, @@ -1298,38 +444,15 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { #endif { 0, } }; -#endif /* CONFIG_PCI */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); -#ifdef CONFIG_PCI MODULE_FIRMWARE(FIRMWARE_RT2860); MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); -#endif /* CONFIG_PCI */ MODULE_LICENSE("GPL"); -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) -static int rt2800soc_probe(struct platform_device *pdev) -{ - return rt2x00soc_probe(pdev, &rt2800pci_ops); -} - -static struct platform_driver rt2800soc_driver = { - .driver = { - .name = "rt2800_wmac", - .owner = THIS_MODULE, - .mod_name = KBUILD_MODNAME, - }, - .probe = rt2800soc_probe, - .remove = rt2x00soc_remove, - .suspend = rt2x00soc_suspend, - .resume = rt2x00soc_resume, -}; -#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */ - -#ifdef CONFIG_PCI static int rt2800pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { @@ -1344,39 +467,5 @@ static struct pci_driver rt2800pci_driver = { .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; -#endif /* CONFIG_PCI */ - -static int __init rt2800pci_init(void) -{ - int ret = 0; - -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) - ret = platform_driver_register(&rt2800soc_driver); - if (ret) - return ret; -#endif -#ifdef CONFIG_PCI - ret = pci_register_driver(&rt2800pci_driver); - if (ret) { -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) - platform_driver_unregister(&rt2800soc_driver); -#endif - return ret; - } -#endif - - return ret; -} - -static void __exit rt2800pci_exit(void) -{ -#ifdef CONFIG_PCI - pci_unregister_driver(&rt2800pci_driver); -#endif -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) - platform_driver_unregister(&rt2800soc_driver); -#endif -} -module_init(rt2800pci_init); -module_exit(rt2800pci_exit); +module_pci_driver(rt2800pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h index ab22a087c50..9dfef4607d6 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.h +++ b/drivers/net/wireless/rt2x00/rt2800pci.h @@ -20,9 +20,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -35,107 +33,10 @@ #define RT2800PCI_H /* - * Queue register offset macros - */ -#define TX_QUEUE_REG_OFFSET 0x10 -#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) - -/* * 8051 firmware image. */ #define FIRMWARE_RT2860 "rt2860.bin" #define FIRMWARE_RT3290 "rt3290.bin" #define FIRMWARE_IMAGE_BASE 0x2000 -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE (4 * sizeof(__le32)) -#define RXD_DESC_SIZE (4 * sizeof(__le32)) - -/* - * TX descriptor format for TX, PRIO and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) -#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) -#define TXD_W1_BURST FIELD32(0x00008000) -#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) -#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) -#define TXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) - -/* - * Word3 - * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI - * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. - * 0:MGMT, 1:HCCA 2:EDCA - */ -#define TXD_W3_WIV FIELD32(0x01000000) -#define TXD_W3_QSEL FIELD32(0x06000000) -#define TXD_W3_TCO FIELD32(0x20000000) -#define TXD_W3_UCO FIELD32(0x40000000) -#define TXD_W3_ICO FIELD32(0x80000000) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - */ -#define RXD_W0_SDP0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define RXD_W1_SDL1 FIELD32(0x00003fff) -#define RXD_W1_SDL0 FIELD32(0x3fff0000) -#define RXD_W1_LS0 FIELD32(0x40000000) -#define RXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define RXD_W2_SDP1 FIELD32(0xffffffff) - -/* - * Word3 - * AMSDU: RX with 802.3 header, not 802.11 header. - * DECRYPTED: This frame is being decrypted. - */ -#define RXD_W3_BA FIELD32(0x00000001) -#define RXD_W3_DATA FIELD32(0x00000002) -#define RXD_W3_NULLDATA FIELD32(0x00000004) -#define RXD_W3_FRAG FIELD32(0x00000008) -#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) -#define RXD_W3_MULTICAST FIELD32(0x00000020) -#define RXD_W3_BROADCAST FIELD32(0x00000040) -#define RXD_W3_MY_BSS FIELD32(0x00000080) -#define RXD_W3_CRC_ERROR FIELD32(0x00000100) -#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) -#define RXD_W3_AMSDU FIELD32(0x00000800) -#define RXD_W3_HTC FIELD32(0x00001000) -#define RXD_W3_RSSI FIELD32(0x00002000) -#define RXD_W3_L2PAD FIELD32(0x00004000) -#define RXD_W3_AMPDU FIELD32(0x00008000) -#define RXD_W3_DECRYPTED FIELD32(0x00010000) -#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) -#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) - #endif /* RT2800PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c new file mode 100644 index 00000000000..f6d1bf5be00 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800soc.c @@ -0,0 +1,261 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn <IvDoorn@gmail.com> + * Copyright (C) 2009 Alban Browaeys <prahal@yahoo.com> + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Luis Correia <luis.f.correia@gmail.com> + * Copyright (C) 2009 Mattias Nissler <mattias.nissler@gmx.de> + * Copyright (C) 2009 Mark Asselstine <asselsm@gmail.com> + * Copyright (C) 2009 Xose Vazquez Perez <xose.vazquez@gmail.com> + * Copyright (C) 2009 Bart Zolnierkiewicz <bzolnier@gmail.com> + * <http://rt2x00.serialmonkey.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Module: rt2800soc + * Abstract: rt2800 WiSoC specific routines. + */ + +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00soc.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* Allow hardware encryption to be disabled. */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2800_disable_radio(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); +} + +static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2800mmio_enable_radio(rt2x00dev); + break; + + case STATE_RADIO_OFF: + rt2800soc_disable_radio(rt2x00dev); + break; + + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800mmio_toggle_irq(rt2x00dev, state); + break; + + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + /* These states are not supported, but don't report an error */ + retval = 0; + break; + + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); + + if (!base_addr) + return -ENOMEM; + + memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); + + iounmap(base_addr); + return 0; +} + +/* Firmware functions */ +static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + WARN_ON_ONCE(1); + return NULL; +} + +static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static const struct ieee80211_ops rt2800soc_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_tkip_seq = rt2800_get_tkip_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800soc_rt2800_ops = { + .register_read = rt2x00mmio_register_read, + .register_read_lock = rt2x00mmio_register_read, /* same for SoCs */ + .register_write = rt2x00mmio_register_write, + .register_write_lock = rt2x00mmio_register_write, /* same for SoCs */ + .register_multiread = rt2x00mmio_register_multiread, + .register_multiwrite = rt2x00mmio_register_multiwrite, + .regbusy_read = rt2x00mmio_regbusy_read, + .read_eeprom = rt2800soc_read_eeprom, + .hwcrypt_disabled = rt2800soc_hwcrypt_disabled, + .drv_write_firmware = rt2800soc_write_firmware, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = { + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, + .probe_hw = rt2800_probe_hw, + .get_firmware_name = rt2800soc_get_firmware_name, + .check_firmware = rt2800soc_check_firmware, + .load_firmware = rt2800soc_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, + .set_device_state = rt2800soc_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2800mmio_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .fill_rxdone = rt2800mmio_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static const struct rt2x00_ops rt2800soc_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800mmio_queue_init, + .lib = &rt2800soc_rt2x00_ops, + .drv = &rt2800soc_rt2800_ops, + .hw = &rt2800soc_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +static int rt2800soc_probe(struct platform_device *pdev) +{ + return rt2x00soc_probe(pdev, &rt2800soc_ops); +} + +static struct platform_driver rt2800soc_driver = { + .driver = { + .name = "rt2800_wmac", + .owner = THIS_MODULE, + .mod_name = KBUILD_MODNAME, + }, + .probe = rt2800soc_probe, + .remove = rt2x00soc_remove, + .suspend = rt2x00soc_suspend, + .resume = rt2x00soc_resume, +}; + +module_platform_driver(rt2800soc_driver); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 96961b9a395..832006b5aab 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -18,9 +18,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -31,7 +29,6 @@ #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/usb.h> @@ -128,9 +125,9 @@ static inline bool rt2800usb_entry_txstatus_timeout(struct queue_entry *entry) tout = time_after(jiffies, entry->last_action + msecs_to_jiffies(100)); if (unlikely(tout)) - rt2x00_warn(entry->queue->rt2x00dev, - "TX status timeout for entry %d in queue %d\n", - entry->entry_idx, entry->queue->qid); + rt2x00_dbg(entry->queue->rt2x00dev, + "TX status timeout for entry %d in queue %d\n", + entry->entry_idx, entry->queue->qid); return tout; } @@ -148,6 +145,8 @@ static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) return false; } +#define TXSTATUS_READ_INTERVAL 1000000 + static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, int urb_status, u32 tx_status) { @@ -162,7 +161,7 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, valid = rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID); if (valid) { - if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status)) + if (!kfifo_put(&rt2x00dev->txstatus_fifo, tx_status)) rt2x00_warn(rt2x00dev, "TX status FIFO overrun\n"); queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); @@ -176,8 +175,9 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); if (rt2800usb_txstatus_pending(rt2x00dev)) { - /* Read register after 250 us */ - hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 250000), + /* Read register after 1 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, TXSTATUS_READ_INTERVAL), HRTIMER_MODE_REL); return false; } @@ -202,8 +202,9 @@ static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev) if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) return; - /* Read TX_STA_FIFO register after 500 us */ - hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 500000), + /* Read TX_STA_FIFO register after 2 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, 2*TXSTATUS_READ_INTERVAL), HRTIMER_MODE_REL); } @@ -228,6 +229,31 @@ static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) /* * Firmware functions */ +static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) +{ + __le32 *reg; + u32 fw_mode; + + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (reg == NULL) + return -ENOMEM; + /* cannot use rt2x00usb_register_read here as it uses different + * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the + * magic value USB_MODE_AUTORUN (0x11) to the device, thus the + * returned value would be invalid. + */ + rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN, + reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE); + fw_mode = le32_to_cpu(*reg); + kfree(reg); + + if ((fw_mode & 0x00000003) == 2) + return 1; + + return 0; +} + static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) { return FIRMWARE_RT2870; @@ -239,6 +265,7 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, int status; u32 offset; u32 length; + int retval; /* * Check which section of the firmware we need. @@ -256,8 +283,16 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, /* * Write firmware to device. */ - rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, - data + offset, length); + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) { + rt2x00_info(rt2x00dev, + "Firmware loading not required - NIC in AutoRun mode\n"); + } else { + rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data + offset, length); + } rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); @@ -565,8 +600,8 @@ static void rt2800usb_txdone(struct rt2x00_dev *rt2x00dev) queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); if (unlikely(rt2x00queue_empty(queue))) { - rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); + rt2x00_dbg(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); break; } @@ -734,11 +769,26 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, /* * Device probe functions. */ +static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + int retval; + + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) + return 1; + return rt2800_efuse_detect(rt2x00dev); +} + static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) { int retval; - if (rt2800_efuse_detect(rt2x00dev)) + retval = rt2800usb_efuse_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) retval = rt2800_read_eeprom_efuse(rt2x00dev); else retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, @@ -763,7 +813,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Overwrite TX done handler */ - PREPARE_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); + INIT_WORK(&rt2x00dev->txdone_work, rt2800usb_work_txdone); return 0; } @@ -988,6 +1038,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x07d1, 0x3c15) }, { USB_DEVICE(0x07d1, 0x3c16) }, { USB_DEVICE(0x07d1, 0x3c17) }, + { USB_DEVICE(0x2001, 0x3317) }, { USB_DEVICE(0x2001, 0x3c1b) }, /* Draytek */ { USB_DEVICE(0x07fa, 0x7712) }, @@ -1176,6 +1227,8 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Linksys */ { USB_DEVICE(0x13b1, 0x002f) }, { USB_DEVICE(0x1737, 0x0079) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0170) }, /* Ralink */ { USB_DEVICE(0x148f, 0x3572) }, /* Sitecom */ @@ -1199,6 +1252,8 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x050d, 0x1103) }, /* Cameo */ { USB_DEVICE(0x148f, 0xf301) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c1f) }, /* Edimax */ { USB_DEVICE(0x7392, 0x7733) }, /* Hawking */ @@ -1212,6 +1267,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0789, 0x016b) }, /* NETGEAR */ { USB_DEVICE(0x0846, 0x9012) }, + { USB_DEVICE(0x0846, 0x9013) }, { USB_DEVICE(0x0846, 0x9019) }, /* Planex */ { USB_DEVICE(0x2019, 0xed19) }, @@ -1220,6 +1276,7 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Sitecom */ { USB_DEVICE(0x0df6, 0x0067) }, { USB_DEVICE(0x0df6, 0x006a) }, + { USB_DEVICE(0x0df6, 0x006e) }, /* ZyXEL */ { USB_DEVICE(0x0586, 0x3421) }, #endif @@ -1236,6 +1293,9 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x2001, 0x3c1c) }, { USB_DEVICE(0x2001, 0x3c1d) }, { USB_DEVICE(0x2001, 0x3c1e) }, + { USB_DEVICE(0x2001, 0x3c20) }, + { USB_DEVICE(0x2001, 0x3c22) }, + { USB_DEVICE(0x2001, 0x3c23) }, /* LG innotek */ { USB_DEVICE(0x043e, 0x7a22) }, { USB_DEVICE(0x043e, 0x7a42) }, @@ -1258,12 +1318,17 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x043e, 0x7a32) }, /* AVM GmbH */ { USB_DEVICE(0x057c, 0x8501) }, - /* D-Link DWA-160-B2 */ + /* Buffalo */ + { USB_DEVICE(0x0411, 0x0241) }, + /* D-Link */ { USB_DEVICE(0x2001, 0x3c1a) }, + { USB_DEVICE(0x2001, 0x3c21) }, /* Proware */ { USB_DEVICE(0x043e, 0x7a13) }, /* Ralink */ { USB_DEVICE(0x148f, 0x5572) }, + /* TRENDnet */ + { USB_DEVICE(0x20f4, 0x724a) }, #endif #ifdef CONFIG_RT2800USB_UNKNOWN /* @@ -1333,6 +1398,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x1d4d, 0x0010) }, /* Planex */ { USB_DEVICE(0x2019, 0xab24) }, + { USB_DEVICE(0x2019, 0xab29) }, /* Qcom */ { USB_DEVICE(0x18e8, 0x6259) }, /* RadioShack */ diff --git a/drivers/net/wireless/rt2x00/rt2800usb.h b/drivers/net/wireless/rt2x00/rt2800usb.h index 671ea359261..ea7cac09599 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.h +++ b/drivers/net/wireless/rt2x00/rt2800usb.h @@ -17,9 +17,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index fe4c572db52..d13f25cd70d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -15,9 +15,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -39,6 +37,7 @@ #include <linux/input-polldev.h> #include <linux/kfifo.h> #include <linux/hrtimer.h> +#include <linux/average.h> #include <net/mac80211.h> @@ -138,17 +137,6 @@ #define SHORT_EIFS ( SIFS + SHORT_DIFS + \ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) -/* - * Structure for average calculation - * The avg field contains the actual average value, - * but avg_weight is internally used during calculations - * to prevent rounding errors. - */ -struct avg_val { - int avg; - int avg_weight; -}; - enum rt2x00_chip_intf { RT2X00_CHIP_INTF_PCI, RT2X00_CHIP_INTF_PCIE, @@ -297,7 +285,7 @@ struct link_ant { * Similar to the avg_rssi in the link_qual structure * this value is updated by using the walking average. */ - struct avg_val rssi_ant; + struct ewma rssi_ant; }; /* @@ -326,7 +314,7 @@ struct link { /* * Currently active average RSSI value */ - struct avg_val avg_rssi; + struct ewma avg_rssi; /* * Work structure for scheduling periodic link tuning. @@ -705,6 +693,7 @@ enum rt2x00_capability_flags { REQUIRE_SW_SEQNO, REQUIRE_HT_TX_DESC, REQUIRE_PS_AUTOWAKE, + REQUIRE_DELAYED_RFKILL, /* * Capabilities @@ -1179,6 +1168,93 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); } +/* Helpers for capability flags */ + +static inline bool +rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev, + enum rt2x00_capability_flags cap_flag) +{ + return test_bit(cap_flag, &rt2x00dev->cap_flags); +} + +static inline bool +rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO); +} + +static inline bool +rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT); +} + +static inline bool +rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS); +} + +static inline bool +rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL); +} + +static inline bool +rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT); +} + +static inline bool +rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING); +} + +static inline bool +rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE); +} + +static inline bool +rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE); +} + +static inline bool +rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A); +} + +static inline bool +rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG); +} + +static inline bool +rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA); +} + +static inline bool +rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST); +} + +static inline bool +rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION); +} + /** * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. * @entry: Pointer to &struct queue_entry @@ -1373,7 +1449,8 @@ int rt2x00mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 queue, const struct ieee80211_tx_queue_params *params); void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw); -void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop); +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant); int rt2x00mac_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); void rt2x00mac_get_ringparam(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 8cb43f8f3ef..1122dc44c9f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c index 1ca4c7ffc18..a2fd05ba25c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -52,7 +50,7 @@ void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; - if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key) + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) return; __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); @@ -80,7 +78,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, struct ieee80211_key_conf *key = tx_info->control.hw_key; unsigned int overhead = 0; - if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key) + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) return overhead; /* diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index fe7a7f63a9e..90fdb02b55e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -288,7 +286,7 @@ static ssize_t rt2x00debug_read_queue_dump(struct file *file, if (retval) return retval; - status = min((size_t)skb->len, length); + status = min_t(size_t, skb->len, length); if (copy_to_user(buf, skb->data, status)) { status = -EFAULT; goto exit; @@ -750,7 +748,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) intf, &rt2x00debug_fop_queue_stats); #ifdef CONFIG_RT2X00_LIB_CRYPTO - if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) intf->crypto_stats_entry = debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, intf, &rt2x00debug_fop_crypto_stats); diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.h b/drivers/net/wireless/rt2x00/rt2x00debug.h index e11d39bdfef..e65712c235b 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.h +++ b/drivers/net/wireless/rt2x00/rt2x00debug.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 712eea9d398..4fa43a2eeb7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -14,9 +14,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -88,7 +86,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) rt2x00queue_start_queues(rt2x00dev); rt2x00link_start_tuner(rt2x00dev); rt2x00link_start_agc(rt2x00dev); - if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) rt2x00link_start_vcocal(rt2x00dev); /* @@ -113,7 +111,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) * Stop all queues */ rt2x00link_stop_agc(rt2x00dev); - if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) rt2x00link_stop_vcocal(rt2x00dev); rt2x00link_stop_tuner(rt2x00dev); rt2x00queue_stop_queues(rt2x00dev); @@ -181,6 +179,7 @@ static void rt2x00lib_autowakeup(struct work_struct *work) static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { + struct ieee80211_tx_control control = {}; struct rt2x00_dev *rt2x00dev = data; struct sk_buff *skb; @@ -195,7 +194,7 @@ static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac, */ skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); while (skb) { - rt2x00mac_tx(rt2x00dev->hw, NULL, skb); + rt2x00mac_tx(rt2x00dev->hw, &control, skb); skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif); } } @@ -234,7 +233,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) * here as they will fetch the next beacon directly prior to * transmission. */ - if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev)) return; /* fetch next beacon */ @@ -358,7 +357,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, * mac80211 will expect the same data to be present it the * frame as it was passed to us. */ - if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) rt2x00crypto_tx_insert_iv(entry->skb, header_length); /* @@ -566,10 +565,10 @@ static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev, #undef TID_CHECK - if (!ether_addr_equal(ba->ra, entry->ta)) + if (!ether_addr_equal_64bits(ba->ra, entry->ta)) continue; - if (!ether_addr_equal(ba->ta, entry->ra)) + if (!ether_addr_equal_64bits(ba->ta, entry->ra)) continue; /* Mark BAR since we received the according BA */ @@ -1127,9 +1126,10 @@ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) return; /* - * Unregister extra components. + * Stop rfkill polling. */ - rt2x00rfkill_unregister(rt2x00dev); + if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_unregister(rt2x00dev); /* * Allow the HW to uninitialize. @@ -1167,6 +1167,12 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); + /* + * Start rfkill polling. + */ + if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_register(rt2x00dev); + return 0; } @@ -1376,7 +1382,12 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00link_register(rt2x00dev); rt2x00leds_register(rt2x00dev); rt2x00debug_register(rt2x00dev); - rt2x00rfkill_register(rt2x00dev); + + /* + * Start rfkill polling. + */ + if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_register(rt2x00dev); return 0; @@ -1392,6 +1403,12 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); /* + * Stop rfkill polling. + */ + if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_unregister(rt2x00dev); + + /* * Disable radio. */ rt2x00lib_disable_radio(rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2x00dump.h b/drivers/net/wireless/rt2x00/rt2x00dump.h index 063ebcce97f..4c0e01b5d51 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dump.h +++ b/drivers/net/wireless/rt2x00/rt2x00dump.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c index 1b4254b4272..fbae2799e3e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00firmware.c +++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c @@ -14,9 +14,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.c b/drivers/net/wireless/rt2x00/rt2x00leds.c index 997a6c89e66..c681d04b506 100644 --- a/drivers/net/wireless/rt2x00/rt2x00leds.c +++ b/drivers/net/wireless/rt2x00/rt2x00leds.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00leds.h b/drivers/net/wireless/rt2x00/rt2x00leds.h index 3b46f0c3332..b2c5269570d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00leds.h +++ b/drivers/net/wireless/rt2x00/rt2x00leds.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00lib.h b/drivers/net/wireless/rt2x00/rt2x00lib.h index a0935987fa3..fb7c349ccc9 100644 --- a/drivers/net/wireless/rt2x00/rt2x00lib.h +++ b/drivers/net/wireless/rt2x00/rt2x00lib.h @@ -14,9 +14,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -146,7 +144,7 @@ void rt2x00queue_remove_l2pad(struct sk_buff *skb, unsigned int header_length); * @local: frame is not from mac80211 */ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, - bool local); + struct ieee80211_sta *sta, bool local); /** * rt2x00queue_update_beacon - Send new beacon from mac80211 diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c index 8368aab86f2..9b941c0c126 100644 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -35,50 +33,28 @@ */ #define DEFAULT_RSSI -128 -/* - * Helper struct and macro to work with moving/walking averages. - * When adding a value to the average value the following calculation - * is needed: - * - * avg_rssi = ((avg_rssi * 7) + rssi) / 8; - * - * The advantage of this approach is that we only need 1 variable - * to store the average in (No need for a count and a total). - * But more importantly, normal average values will over time - * move less and less towards newly added values this results - * that with link tuning, the device can have a very good RSSI - * for a few minutes but when the device is moved away from the AP - * the average will not decrease fast enough to compensate. - * The walking average compensates this and will move towards - * the new values correctly allowing a effective link tuning, - * the speed of the average moving towards other values depends - * on the value for the number of samples. The higher the number - * of samples, the slower the average will move. - * We use two variables to keep track of the average value to - * compensate for the rounding errors. This can be a significant - * error (>5dBm) if the factor is too low. - */ -#define AVG_SAMPLES 8 -#define AVG_FACTOR 1000 -#define MOVING_AVERAGE(__avg, __val) \ -({ \ - struct avg_val __new; \ - __new.avg_weight = \ - (__avg).avg_weight ? \ - ((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \ - ((__val) * (AVG_FACTOR))) / \ - (AVG_SAMPLES)) : \ - ((__val) * (AVG_FACTOR)); \ - __new.avg = __new.avg_weight / (AVG_FACTOR); \ - __new; \ -}) +/* Constants for EWMA calculations. */ +#define RT2X00_EWMA_FACTOR 1024 +#define RT2X00_EWMA_WEIGHT 8 + +static inline int rt2x00link_get_avg_rssi(struct ewma *ewma) +{ + unsigned long avg; + + avg = ewma_read(ewma); + if (avg) + return -avg; + + return DEFAULT_RSSI; +} static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; - if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success) - return ant->rssi_ant.avg; + if (rt2x00dev->link.qual.rx_success) + return rt2x00link_get_avg_rssi(&ant->rssi_ant); + return DEFAULT_RSSI; } @@ -100,8 +76,8 @@ static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) { - rt2x00dev->link.ant.rssi_ant.avg = 0; - rt2x00dev->link.ant.rssi_ant.avg_weight = 0; + ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR, + RT2X00_EWMA_WEIGHT); } static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) @@ -249,12 +225,12 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, /* * Update global RSSI */ - link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi); + ewma_add(&link->avg_rssi, -rxdesc->rssi); /* * Update antenna RSSI */ - ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi); + ewma_add(&ant->rssi_ant, -rxdesc->rssi); } void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) @@ -309,6 +285,8 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) */ rt2x00dev->link.count = 0; memset(qual, 0, sizeof(*qual)); + ewma_init(&rt2x00dev->link.avg_rssi, RT2X00_EWMA_FACTOR, + RT2X00_EWMA_WEIGHT); /* * Restore the VGC level as stored in the registers, @@ -363,17 +341,17 @@ static void rt2x00link_tuner(struct work_struct *work) * collect the RSSI data we could use this. Otherwise we * must fallback to the default RSSI value. */ - if (!link->avg_rssi.avg || !qual->rx_success) + if (!qual->rx_success) qual->rssi = DEFAULT_RSSI; else - qual->rssi = link->avg_rssi.avg; + qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi); /* * Check if link tuning is supported by the hardware, some hardware * do not support link tuning at all, while other devices can disable * the feature from the EEPROM. */ - if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_link_tuning(rt2x00dev)) rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); /* @@ -513,7 +491,7 @@ static void rt2x00link_vcocal(struct work_struct *work) void rt2x00link_register(struct rt2x00_dev *rt2x00dev) { INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); - if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal); INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index f883802f350..004dff9b962 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -90,7 +88,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, frag_skb->data, data_length, tx_info, (struct ieee80211_rts *)(skb->data)); - retval = rt2x00queue_write_tx_frame(queue, skb, true); + retval = rt2x00queue_write_tx_frame(queue, skb, NULL, true); if (retval) { dev_kfree_skb_any(skb); rt2x00_warn(rt2x00dev, "Failed to send RTS/CTS frame\n"); @@ -151,7 +149,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, goto exit_fail; } - if (unlikely(rt2x00queue_write_tx_frame(queue, skb, false))) + if (unlikely(rt2x00queue_write_tx_frame(queue, skb, control->sta, false))) goto exit_fail; /* @@ -382,11 +380,11 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, * of different types, but has no a separate filter for PS Poll frames, * FIF_CONTROL flag implies FIF_PSPOLL. */ - if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_control_filters(rt2x00dev)) { if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) *total_flags |= FIF_CONTROL | FIF_PSPOLL; } - if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) { if (*total_flags & FIF_CONTROL) *total_flags |= FIF_PSPOLL; } @@ -469,7 +467,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; - if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_hw_crypto(rt2x00dev)) return -EOPNOTSUPP; /* @@ -489,6 +487,8 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, crypto.cipher = rt2x00crypto_key_to_cipher(key); if (crypto.cipher == CIPHER_NONE) return -EOPNOTSUPP; + if (crypto.cipher == CIPHER_TKIP && rt2x00_is_usb(rt2x00dev)) + return -EOPNOTSUPP; crypto.cmd = cmd; @@ -623,20 +623,18 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid); /* - * Update the beacon. This is only required on USB devices. PCI - * devices fetch beacons periodically. - */ - if (changes & BSS_CHANGED_BEACON && rt2x00_is_usb(rt2x00dev)) - rt2x00queue_update_beacon(rt2x00dev, vif); - - /* * Start/stop beaconing. */ if (changes & BSS_CHANGED_BEACON_ENABLED) { if (!bss_conf->enable_beacon && intf->enable_beacon) { - rt2x00queue_clear_beacon(rt2x00dev, vif); rt2x00dev->intf_beaconing--; intf->enable_beacon = false; + /* + * Clear beacon in the H/W for this vif. This is needed + * to disable beaconing on this particular interface + * and keep it running on other interfaces. + */ + rt2x00queue_clear_beacon(rt2x00dev, vif); if (rt2x00dev->intf_beaconing == 0) { /* @@ -647,11 +645,15 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, rt2x00queue_stop_queue(rt2x00dev->bcn); mutex_unlock(&intf->beacon_skb_mutex); } - - } else if (bss_conf->enable_beacon && !intf->enable_beacon) { rt2x00dev->intf_beaconing++; intf->enable_beacon = true; + /* + * Upload beacon to the H/W. This is only required on + * USB devices. PCI devices fetch beacons periodically. + */ + if (rt2x00_is_usb(rt2x00dev)) + rt2x00queue_update_beacon(rt2x00dev, vif); if (rt2x00dev->intf_beaconing == 1) { /* @@ -749,11 +751,15 @@ void rt2x00mac_rfkill_poll(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(rt2x00mac_rfkill_poll); -void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +void rt2x00mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + tx_queue_for_each(rt2x00dev, queue) rt2x00queue_flush_queue(queue, drop); } diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/rt2x00/rt2x00mmio.c index 64b06c6abe5..6f236ea180a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mmio.c +++ b/drivers/net/wireless/rt2x00/rt2x00mmio.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.h b/drivers/net/wireless/rt2x00/rt2x00mmio.h index cda3dbcf7ea..701c3127efb 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mmio.h +++ b/drivers/net/wireless/rt2x00/rt2x00mmio.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index dc49e525ae5..d93db4b0371 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -119,7 +117,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) rt2x00dev->ops = ops; rt2x00dev->hw = hw; rt2x00dev->irq = pci_dev->irq; - rt2x00dev->name = pci_name(pci_dev); + rt2x00dev->name = ops->name; if (pci_is_pcie(pci_dev)) rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); @@ -156,8 +154,6 @@ exit_release_regions: exit_disable_device: pci_disable_device(pci_dev); - pci_set_drvdata(pci_dev, NULL); - return retval; } EXPORT_SYMBOL_GPL(rt2x00pci_probe); @@ -177,7 +173,6 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) /* * Free the PCI device data. */ - pci_set_drvdata(pci_dev, NULL); pci_disable_device(pci_dev); pci_release_regions(pci_dev); } diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h index 60d90b20f8b..bc0ca5f58f3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.h +++ b/drivers/net/wireless/rt2x00/rt2x00pci.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 6c8a33b6ee2..5642ccceca7 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -15,9 +15,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -61,7 +59,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) * at least 8 bytes bytes available in headroom for IV/EIV * and 8 bytes for ICV data as tailroon. */ - if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) { head_size += 8; tail_size += 8; } @@ -635,7 +633,7 @@ static void rt2x00queue_bar_check(struct queue_entry *entry) } int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, - bool local) + struct ieee80211_sta *sta, bool local) { struct ieee80211_tx_info *tx_info; struct queue_entry *entry; @@ -649,7 +647,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, * after that we are free to use the skb->cb array * for our information. */ - rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, NULL); + rt2x00queue_create_tx_descriptor(queue->rt2x00dev, skb, &txdesc, sta); /* * All information is retrieved from the skb->cb array, @@ -1033,38 +1031,21 @@ EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue); void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) { - bool started; bool tx_queue = (queue->qid == QID_AC_VO) || (queue->qid == QID_AC_VI) || (queue->qid == QID_AC_BE) || (queue->qid == QID_AC_BK); - mutex_lock(&queue->status_lock); /* - * If the queue has been started, we must stop it temporarily - * to prevent any new frames to be queued on the device. If - * we are not dropping the pending frames, the queue must - * only be stopped in the software and not the hardware, - * otherwise the queue will never become empty on its own. + * If we are not supposed to drop any pending + * frames, this means we must force a start (=kick) + * to the queue to make sure the hardware will + * start transmitting. */ - started = test_bit(QUEUE_STARTED, &queue->flags); - if (started) { - /* - * Pause the queue - */ - rt2x00queue_pause_queue(queue); - - /* - * If we are not supposed to drop any pending - * frames, this means we must force a start (=kick) - * to the queue to make sure the hardware will - * start transmitting. - */ - if (!drop && tx_queue) - queue->rt2x00dev->ops->lib->kick_queue(queue); - } + if (!drop && tx_queue) + queue->rt2x00dev->ops->lib->kick_queue(queue); /* * Check if driver supports flushing, if that is the case we can @@ -1080,14 +1061,6 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) if (unlikely(!rt2x00queue_empty(queue))) rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n", queue->qid); - - /* - * Restore the queue to the previous status - */ - if (started) - rt2x00queue_unpause_queue(queue); - - mutex_unlock(&queue->status_lock); } EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h index ebe11722497..c48125be0e3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.h +++ b/drivers/net/wireless/rt2x00/rt2x00queue.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00reg.h b/drivers/net/wireless/rt2x00/rt2x00reg.h index 6f867eec49c..3cc541d13d6 100644 --- a/drivers/net/wireless/rt2x00/rt2x00reg.h +++ b/drivers/net/wireless/rt2x00/rt2x00reg.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.c b/drivers/net/wireless/rt2x00/rt2x00soc.c index 9271a5fce0a..69a0cdadb07 100644 --- a/drivers/net/wireless/rt2x00/rt2x00soc.c +++ b/drivers/net/wireless/rt2x00/rt2x00soc.c @@ -14,9 +14,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00soc.h b/drivers/net/wireless/rt2x00/rt2x00soc.h index 474cbfc1efc..9948d355e9a 100644 --- a/drivers/net/wireless/rt2x00/rt2x00soc.h +++ b/drivers/net/wireless/rt2x00/rt2x00soc.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 88289873c0c..86c43d112a4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -14,9 +14,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -70,6 +68,12 @@ int rt2x00usb_vendor_request(struct rt2x00_dev *rt2x00dev, } } + /* If the port is powered down, we get a -EPROTO error, and this + * leads to a endless loop. So just say that the device is gone. + */ + if (status == -EPROTO) + clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + rt2x00_err(rt2x00dev, "Vendor Request 0x%02x failed for offset 0x%04x with error %d\n", request, offset, status); @@ -523,7 +527,9 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", queue->qid); + rt2x00queue_stop_queue(queue); rt2x00queue_flush_queue(queue, true); + rt2x00queue_start_queue(queue); } static int rt2x00usb_dma_timeout(struct data_queue *queue) diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index 323ca7b2b09..831b65f93fe 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -95,6 +93,7 @@ enum rt2x00usb_mode_offset { USB_MODE_SLEEP = 7, /* RT73USB */ USB_MODE_FIRMWARE = 8, /* RT73USB */ USB_MODE_WAKEUP = 9, /* RT73USB */ + USB_MODE_AUTORUN = 17, /* RT2800USB */ }; /** diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 54d3ddfc988..9048a9cbe52 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,7 +25,6 @@ #include <linux/crc-itu-t.h> #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -685,7 +682,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)); + !rt2x00_has_cap_frame_type(rt2x00dev)); /* * Configure the RX antenna. @@ -813,10 +810,10 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); } else { sel = antenna_sel_bg; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) @@ -836,7 +833,7 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, else if (rt2x00_rf(rt2x00dev, RF2527)) rt61pci_config_antenna_2x(rt2x00dev, ant); else if (rt2x00_rf(rt2x00dev, RF2529)) { - if (test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_double_antenna(rt2x00dev)) rt61pci_config_antenna_2x(rt2x00dev, ant); else rt61pci_config_antenna_2529(rt2x00dev, ant); @@ -850,13 +847,13 @@ static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, short lna_gain = 0; if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } else { - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); @@ -1054,14 +1051,14 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { low_bound = 0x28; up_bound = 0x48; - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } } else { low_bound = 0x20; up_bound = 0x40; - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } @@ -2034,13 +2031,14 @@ static void rt61pci_write_beacon(struct queue_entry *entry, static void rt61pci_clear_beacon(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 reg; + u32 orig_reg, reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ - rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00mmio_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); + reg = orig_reg; rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); @@ -2051,10 +2049,9 @@ static void rt61pci_clear_beacon(struct queue_entry *entry) HW_BEACON_OFFSET(entry->entry_idx), 0); /* - * Enable beaconing again. + * Restore global beaconing state. */ - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, reg); + rt2x00mmio_register_write(rt2x00dev, TXRX_CSR9, orig_reg); } /* @@ -2578,7 +2575,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * eeprom word. */ if (rt2x00_rf(rt2x00dev, RF2529) && - !test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) { + !rt2x00_has_cap_double_antenna(rt2x00dev)) { rt2x00dev->default_ant.rx = ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); rt2x00dev->default_ant.tx = @@ -2793,7 +2790,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (!test_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) { spec->num_channels = 14; spec->channels = rf_vals_noseq; } else { diff --git a/drivers/net/wireless/rt2x00/rt61pci.h b/drivers/net/wireless/rt2x00/rt61pci.h index 9bc6b6044e3..1442075a838 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.h +++ b/drivers/net/wireless/rt2x00/rt61pci.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 1d3880e09a1..95724ff9c72 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* @@ -27,7 +25,6 @@ #include <linux/crc-itu-t.h> #include <linux/delay.h> #include <linux/etherdevice.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -595,8 +592,8 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags) - && (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); + temp = !rt2x00_has_cap_frame_type(rt2x00dev) && + (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); break; case ANTENNA_A: @@ -636,7 +633,7 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)); + !rt2x00_has_cap_frame_type(rt2x00dev)); /* * Configure the RX antenna. @@ -709,10 +706,10 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); } else { sel = antenna_sel_bg; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) @@ -740,7 +737,7 @@ static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, short lna_gain = 0; if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); @@ -930,7 +927,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, low_bound = 0x28; up_bound = 0x48; - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } @@ -946,7 +943,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, up_bound = 0x1c; } - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { low_bound += 0x14; up_bound += 0x10; } @@ -1600,13 +1597,14 @@ static void rt73usb_clear_beacon(struct queue_entry *entry) { struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; unsigned int beacon_base; - u32 reg; + u32 orig_reg, reg; /* * Disable beaconing while we are reloading the beacon data, * otherwise we might be sending out invalid data. */ - rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, ®); + rt2x00usb_register_read(rt2x00dev, TXRX_CSR9, &orig_reg); + reg = orig_reg; rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 0); rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); @@ -1617,10 +1615,9 @@ static void rt73usb_clear_beacon(struct queue_entry *entry) rt2x00usb_register_write(rt2x00dev, beacon_base, 0); /* - * Enable beaconing again. + * Restore beaconing state. */ - rt2x00_set_field32(®, TXRX_CSR9_BEACON_GEN, 1); - rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, reg); + rt2x00usb_register_write(rt2x00dev, TXRX_CSR9, orig_reg); } static int rt73usb_get_tx_data_len(struct queue_entry *entry) @@ -1661,7 +1658,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) } if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { if (lna == 3 || lna == 2) offset += 10; } else { diff --git a/drivers/net/wireless/rt2x00/rt73usb.h b/drivers/net/wireless/rt2x00/rt73usb.h index 7577e0ba387..4a4f235466d 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.h +++ b/drivers/net/wireless/rt2x00/rt73usb.h @@ -13,9 +13,7 @@ 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* diff --git a/drivers/net/wireless/rtl818x/Kconfig b/drivers/net/wireless/rtl818x/Kconfig index 30332175bcd..1ce1d55f001 100644 --- a/drivers/net/wireless/rtl818x/Kconfig +++ b/drivers/net/wireless/rtl818x/Kconfig @@ -2,11 +2,11 @@ # RTL818X Wireless LAN device configuration # config RTL8180 - tristate "Realtek 8180/8185 PCI support" + tristate "Realtek 8180/8185/8187SE PCI support" depends on MAC80211 && PCI select EEPROM_93CX6 ---help--- - This is a driver for RTL8180 and RTL8185 based cards. + This is a driver for RTL8180, RTL8185 and RTL8187SE based cards. These are PCI based chips found in cards such as: (RTL8185 802.11g) diff --git a/drivers/net/wireless/rtl818x/rtl8180/Makefile b/drivers/net/wireless/rtl818x/rtl8180/Makefile index cb4fb8596f0..21005bd8b43 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/Makefile +++ b/drivers/net/wireless/rtl818x/rtl8180/Makefile @@ -1,5 +1,5 @@ -rtl8180-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o +rtl818x_pci-objs := dev.o rtl8225.o sa2400.o max2820.o grf5101.o rtl8225se.o -obj-$(CONFIG_RTL8180) += rtl8180.o +obj-$(CONFIG_RTL8180) += rtl818x_pci.o ccflags-y += -Idrivers/net/wireless/rtl818x diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index fc207b268e4..2c1c02bafa1 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -1,21 +1,47 @@ -/* - * Linux device driver for RTL8180 / RTL8185 +/* Linux device driver for RTL8180 / RTL8185 / RTL8187SE * * Copyright 2007 Michael Wu <flamingice@sourmilk.net> - * Copyright 2007 Andrea Merello <andrea.merello@gmail.com> + * Copyright 2007,2014 Andrea Merello <andrea.merello@gmail.com> * * Based on the r8180 driver, which is: * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. * * Thanks to Realtek for their support! * + ************************************************************************ + * + * The driver was extended to the RTL8187SE in 2014 by + * Andrea Merello <andrea.merello@gmail.com> + * + * based also on: + * - portions of rtl8187se Linux staging driver, Copyright Realtek corp. + * - other GPL, unpublished (until now), Linux driver code, + * Copyright Larry Finger <Larry.Finger@lwfinger.net> + * + * A huge thanks goes to Sara V. Nari who forgives me when I'm + * sitting in front of my laptop at evening, week-end, night... + * + * A special thanks goes to Antonio Cuni, who helped me with + * some python userspace stuff I used to debug RTL8187SE code, and who + * bought a laptop with an unsupported Wi-Fi card some years ago... + * + * Thanks to Larry Finger for writing some code for rtl8187se and for + * his suggestions. + * + * Thanks to Dan Carpenter for reviewing my initial patch and for his + * suggestions. + * + * Thanks to Bernhard Schiffner for his help in testing and for his + * suggestions. + * + ************************************************************************ + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/slab.h> @@ -30,13 +56,18 @@ #include "sa2400.h" #include "max2820.h" #include "grf5101.h" +#include "rtl8225se.h" MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>"); MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>"); -MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver"); +MODULE_DESCRIPTION("RTL8180 / RTL8185 / RTL8187SE PCI wireless driver"); MODULE_LICENSE("GPL"); static DEFINE_PCI_DEVICE_TABLE(rtl8180_table) = { + + /* rtl8187se */ + { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8199) }, + /* rtl8185 */ { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8185) }, { PCI_DEVICE(PCI_VENDOR_ID_BELKIN, 0x700f) }, @@ -86,6 +117,76 @@ static const struct ieee80211_channel rtl818x_channels[] = { { .center_freq = 2484 }, }; +/* Queues for rtl8187se card + * + * name | reg | queue + * BC | 7 | 6 + * MG | 1 | 0 + * HI | 6 | 1 + * VO | 5 | 2 + * VI | 4 | 3 + * BE | 3 | 4 + * BK | 2 | 5 + * + * The complete map for DMA kick reg using use all queue is: + * static const int rtl8187se_queues_map[RTL8187SE_NR_TX_QUEUES] = + * {1, 6, 5, 4, 3, 2, 7}; + * + * .. but.. Because for mac80211 4 queues are enough for QoS we use this + * + * name | reg | queue + * BC | 7 | 4 <- currently not used yet + * MG | 1 | x <- Not used + * HI | 6 | x <- Not used + * VO | 5 | 0 <- used + * VI | 4 | 1 <- used + * BE | 3 | 2 <- used + * BK | 2 | 3 <- used + * + * Beacon queue could be used, but this is not finished yet. + * + * I thougth about using the other two queues but I decided not to do this: + * + * - I'm unsure whether the mac80211 will ever try to use more than 4 queues + * by itself. + * + * - I could route MGMT frames (currently sent over VO queue) to the MGMT + * queue but since mac80211 will do not know about it, I will probably gain + * some HW priority whenever the VO queue is not empty, but this gain is + * limited by the fact that I had to stop the mac80211 queue whenever one of + * the VO or MGMT queues is full, stopping also submitting of MGMT frame + * to the driver. + * + * - I don't know how to set in the HW the contention window params for MGMT + * and HI-prio queues. + */ + +static const int rtl8187se_queues_map[RTL8187SE_NR_TX_QUEUES] = {5, 4, 3, 2, 7}; + +/* Queues for rtl8180/rtl8185 cards + * + * name | reg | prio + * BC | 7 | 3 + * HI | 6 | 0 + * NO | 5 | 1 + * LO | 4 | 2 + * + * The complete map for DMA kick reg using all queue is: + * static const int rtl8180_queues_map[RTL8180_NR_TX_QUEUES] = {6, 5, 4, 7}; + * + * .. but .. Because the mac80211 needs at least 4 queues for QoS or + * otherwise QoS can't be done, we use just one. + * Beacon queue could be used, but this is not finished yet. + * Actual map is: + * + * name | reg | prio + * BC | 7 | 1 <- currently not used yet. + * HI | 6 | x <- not used + * NO | 5 | x <- not used + * LO | 4 | 0 <- used + */ + +static const int rtl8180_queues_map[RTL8180_NR_TX_QUEUES] = {4, 7}; void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) { @@ -106,13 +207,30 @@ void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data) static void rtl8180_handle_rx(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; + struct rtl818x_rx_cmd_desc *cmd_desc; unsigned int count = 32; u8 signal, agc, sq; + dma_addr_t mapping; while (count--) { - struct rtl8180_rx_desc *entry = &priv->rx_ring[priv->rx_idx]; + void *entry = priv->rx_ring + priv->rx_idx * priv->rx_ring_sz; struct sk_buff *skb = priv->rx_buf[priv->rx_idx]; - u32 flags = le32_to_cpu(entry->flags); + u32 flags, flags2; + u64 tsft; + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + struct rtl8187se_rx_desc *desc = entry; + + flags = le32_to_cpu(desc->flags); + flags2 = le32_to_cpu(desc->flags2); + tsft = le64_to_cpu(desc->tsft); + } else { + struct rtl8180_rx_desc *desc = entry; + + flags = le32_to_cpu(desc->flags); + flags2 = le32_to_cpu(desc->flags2); + tsft = le64_to_cpu(desc->tsft); + } if (flags & RTL818X_RX_DESC_FLAG_OWN) return; @@ -122,13 +240,23 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) RTL818X_RX_DESC_FLAG_RX_ERR))) goto done; else { - u32 flags2 = le32_to_cpu(entry->flags2); struct ieee80211_rx_status rx_status = {0}; struct sk_buff *new_skb = dev_alloc_skb(MAX_RX_SIZE); if (unlikely(!new_skb)) goto done; + mapping = pci_map_single(priv->pdev, + skb_tail_pointer(new_skb), + MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + + if (pci_dma_mapping_error(priv->pdev, mapping)) { + kfree_skb(new_skb); + dev_err(&priv->pdev->dev, "RX DMA map error\n"); + + goto done; + } + pci_unmap_single(priv->pdev, *((dma_addr_t *)skb->cb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); @@ -137,20 +265,27 @@ 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 signal = 95 - clamp_t(u8, agc, 30, 95); - } else { + } else if (priv->chip_family == + RTL818X_CHIP_FAMILY_RTL8180) { sq = flags2 & 0xff; signal = priv->rf->calc_rssi(agc, sq); + } else { + /* TODO: rtl8187se rssi */ + signal = 10; } rx_status.signal = signal; rx_status.freq = dev->conf.chandef.chan->center_freq; rx_status.band = dev->conf.chandef.chan->band; - rx_status.mactime = le64_to_cpu(entry->tsft); + rx_status.mactime = tsft; rx_status.flag |= RX_FLAG_MACTIME_START; + if (flags & RTL818X_RX_DESC_FLAG_SPLCP) + rx_status.flag |= RX_FLAG_SHORTPRE; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; @@ -159,17 +294,17 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) skb = new_skb; priv->rx_buf[priv->rx_idx] = skb; - *((dma_addr_t *) skb->cb) = - pci_map_single(priv->pdev, skb_tail_pointer(skb), - MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + *((dma_addr_t *) skb->cb) = mapping; } done: - entry->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); - entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | + cmd_desc = entry; + cmd_desc->rx_buf = cpu_to_le32(*((dma_addr_t *)skb->cb)); + cmd_desc->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); if (priv->rx_idx == 31) - entry->flags |= cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); + cmd_desc->flags |= + cpu_to_le32(RTL818X_RX_DESC_FLAG_EOR); priv->rx_idx = (priv->rx_idx + 1) % 32; } } @@ -209,6 +344,55 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio) } } +static irqreturn_t rtl8187se_interrupt(int irq, void *dev_id) +{ + struct ieee80211_hw *dev = dev_id; + struct rtl8180_priv *priv = dev->priv; + u32 reg; + unsigned long flags; + static int desc_err; + + spin_lock_irqsave(&priv->lock, flags); + /* Note: 32-bit interrupt status */ + reg = rtl818x_ioread32(priv, &priv->map->INT_STATUS_SE); + if (unlikely(reg == 0xFFFFFFFF)) { + spin_unlock_irqrestore(&priv->lock, flags); + return IRQ_HANDLED; + } + + rtl818x_iowrite32(priv, &priv->map->INT_STATUS_SE, reg); + + if (reg & IMR_TIMEOUT1) + rtl818x_iowrite32(priv, &priv->map->INT_TIMEOUT, 0); + + if (reg & (IMR_TBDOK | IMR_TBDER)) + rtl8180_handle_tx(dev, 4); + + if (reg & (IMR_TVODOK | IMR_TVODER)) + rtl8180_handle_tx(dev, 0); + + if (reg & (IMR_TVIDOK | IMR_TVIDER)) + rtl8180_handle_tx(dev, 1); + + if (reg & (IMR_TBEDOK | IMR_TBEDER)) + rtl8180_handle_tx(dev, 2); + + if (reg & (IMR_TBKDOK | IMR_TBKDER)) + rtl8180_handle_tx(dev, 3); + + if (reg & (IMR_ROK | IMR_RER | RTL818X_INT_SE_RX_DU | IMR_RQOSOK)) + rtl8180_handle_rx(dev); + /* The interface sometimes generates several RX DMA descriptor errors + * at startup. Do not report these. + */ + if ((reg & RTL818X_INT_SE_RX_DU) && desc_err++ > 2) + if (net_ratelimit()) + wiphy_err(dev->wiphy, "No RX DMA Descriptor avail\n"); + + spin_unlock_irqrestore(&priv->lock, flags); + return IRQ_HANDLED; +} + static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) { struct ieee80211_hw *dev = dev_id; @@ -225,12 +409,6 @@ static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) rtl818x_iowrite16(priv, &priv->map->INT_STATUS, reg); if (reg & (RTL818X_INT_TXB_OK | RTL818X_INT_TXB_ERR)) - rtl8180_handle_tx(dev, 3); - - if (reg & (RTL818X_INT_TXH_OK | RTL818X_INT_TXH_ERR)) - rtl8180_handle_tx(dev, 2); - - if (reg & (RTL818X_INT_TXN_OK | RTL818X_INT_TXN_ERR)) rtl8180_handle_tx(dev, 1); if (reg & (RTL818X_INT_TXL_OK | RTL818X_INT_TXL_ERR)) @@ -254,12 +432,14 @@ static void rtl8180_tx(struct ieee80211_hw *dev, struct rtl8180_tx_ring *ring; struct rtl8180_tx_desc *entry; unsigned long flags; - unsigned int idx, prio; + unsigned int idx, prio, hw_prio; dma_addr_t mapping; u32 tx_flags; u8 rc_flags; u16 plcp_len = 0; __le16 rts_duration = 0; + /* do arithmetic and then convert to le16 */ + u16 frame_duration = 0; prio = skb_get_queue_mapping(skb); ring = &priv->tx_ring[prio]; @@ -267,29 +447,40 @@ static void rtl8180_tx(struct ieee80211_hw *dev, mapping = pci_map_single(priv->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pdev, mapping)) { + kfree_skb(skb); + dev_err(&priv->pdev->dev, "TX DMA mapping error\n"); + return; + } + tx_flags = RTL818X_TX_DESC_FLAG_OWN | RTL818X_TX_DESC_FLAG_FS | RTL818X_TX_DESC_FLAG_LS | (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; rc_flags = info->control.rates[0].flags; + + /* HW will perform RTS-CTS when only RTS flags is set. + * HW will perform CTS-to-self when both RTS and CTS flags are set. + * RTS rate and RTS duration will be used also for CTS-to-self. + */ if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) { tx_flags |= RTL818X_TX_DESC_FLAG_RTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_duration = ieee80211_rts_duration(dev, priv->vif, + skb->len, info); } else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - tx_flags |= RTL818X_TX_DESC_FLAG_CTS; + tx_flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS; tx_flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_duration = ieee80211_ctstoself_duration(dev, priv->vif, + skb->len, info); } - if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) - 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), @@ -300,6 +491,18 @@ static void rtl8180_tx(struct ieee80211_hw *dev, plcp_len |= 1 << 15; } + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + __le16 duration; + /* SIFS time (required by HW) is already included by + * ieee80211_generic_frame_duration + */ + duration = ieee80211_generic_frame_duration(dev, priv->vif, + IEEE80211_BAND_2GHZ, skb->len, + ieee80211_get_tx_rate(dev, info)); + + frame_duration = priv->ack_time + le16_to_cpu(duration); + } + spin_lock_irqsave(&priv->lock, flags); if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { @@ -312,21 +515,91 @@ static void rtl8180_tx(struct ieee80211_hw *dev, idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries; entry = &ring->desc[idx]; + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + entry->frame_duration = cpu_to_le16(frame_duration); + entry->frame_len_se = cpu_to_le16(skb->len); + + /* tpc polarity */ + entry->flags3 = cpu_to_le16(1<<4); + } else + entry->frame_len = cpu_to_le32(skb->len); + entry->rts_duration = rts_duration; entry->plcp_len = cpu_to_le16(plcp_len); entry->tx_buf = cpu_to_le32(mapping); - entry->frame_len = cpu_to_le32(skb->len); + entry->flags2 = info->control.rates[1].idx >= 0 ? ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0; entry->retry_limit = info->control.rates[0].count; + + /* We must be sure that tx_flags is written last because the HW + * looks at it to check if the rest of data is valid or not + */ + wmb(); entry->flags = cpu_to_le32(tx_flags); + /* We must be sure this has been written before followings HW + * register write, because this write will made the HW attempts + * to DMA the just-written data + */ + wmb(); + __skb_queue_tail(&ring->queue, skb); if (ring->entries - skb_queue_len(&ring->queue) < 2) ieee80211_stop_queue(dev, prio); spin_unlock_irqrestore(&priv->lock, flags); - rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + /* just poll: rings are stopped with TPPollStop reg */ + hw_prio = rtl8187se_queues_map[prio]; + rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, + (1 << hw_prio)); + } else { + hw_prio = rtl8180_queues_map[prio]; + rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, + (1 << hw_prio) | /* ring to poll */ + (1<<1) | (1<<2));/* stopped rings */ + } +} + +static void rtl8180_set_anaparam3(struct rtl8180_priv *priv, u16 anaparam3) +{ + u8 reg; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + + rtl818x_iowrite16(priv, &priv->map->ANAPARAM3, anaparam3); + + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); +} + +void rtl8180_set_anaparam2(struct rtl8180_priv *priv, u32 anaparam2) +{ + u8 reg; + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_CONFIG); + + reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg | RTL818X_CONFIG3_ANAPARAM_WRITE); + + rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); + + rtl818x_iowrite8(priv, &priv->map->CONFIG3, + reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE); + + rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, + RTL818X_EEPROM_CMD_NORMAL); } void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) @@ -343,17 +616,170 @@ void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL); } +static void rtl8187se_mac_config(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u8 reg; + + rtl818x_iowrite32(priv, REG_ADDR4(0x1F0), 0); + rtl818x_ioread32(priv, REG_ADDR4(0x1F0)); + rtl818x_iowrite32(priv, REG_ADDR4(0x1F4), 0); + rtl818x_ioread32(priv, REG_ADDR4(0x1F4)); + rtl818x_iowrite8(priv, REG_ADDR1(0x1F8), 0); + rtl818x_ioread8(priv, REG_ADDR1(0x1F8)); + /* Enable DA10 TX power saving */ + reg = rtl818x_ioread8(priv, &priv->map->PHY_PR); + rtl818x_iowrite8(priv, &priv->map->PHY_PR, reg | 0x04); + /* Power */ + rtl818x_iowrite16(priv, PI_DATA_REG, 0x1000); + rtl818x_iowrite16(priv, SI_DATA_REG, 0x1000); + /* AFE - default to power ON */ + rtl818x_iowrite16(priv, REG_ADDR2(0x370), 0x0560); + rtl818x_iowrite16(priv, REG_ADDR2(0x372), 0x0560); + rtl818x_iowrite16(priv, REG_ADDR2(0x374), 0x0DA4); + rtl818x_iowrite16(priv, REG_ADDR2(0x376), 0x0DA4); + rtl818x_iowrite16(priv, REG_ADDR2(0x378), 0x0560); + rtl818x_iowrite16(priv, REG_ADDR2(0x37A), 0x0560); + rtl818x_iowrite16(priv, REG_ADDR2(0x37C), 0x00EC); + rtl818x_iowrite16(priv, REG_ADDR2(0x37E), 0x00EC); + rtl818x_iowrite8(priv, REG_ADDR1(0x24E), 0x01); + /* unknown, needed for suspend to RAM resume */ + rtl818x_iowrite8(priv, REG_ADDR1(0x0A), 0x72); +} + +static void rtl8187se_set_antenna_config(struct ieee80211_hw *dev, u8 def_ant, + bool diversity) +{ + struct rtl8180_priv *priv = dev->priv; + + rtl8225_write_phy_cck(dev, 0x0C, 0x09); + if (diversity) { + if (def_ant == 1) { + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x00); + rtl8225_write_phy_cck(dev, 0x11, 0xBB); + rtl8225_write_phy_cck(dev, 0x01, 0xC7); + rtl8225_write_phy_ofdm(dev, 0x0D, 0x54); + rtl8225_write_phy_ofdm(dev, 0x18, 0xB2); + } else { /* main antenna */ + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); + rtl8225_write_phy_cck(dev, 0x11, 0x9B); + rtl8225_write_phy_cck(dev, 0x01, 0xC7); + rtl8225_write_phy_ofdm(dev, 0x0D, 0x5C); + rtl8225_write_phy_ofdm(dev, 0x18, 0xB2); + } + } else { /* disable antenna diversity */ + if (def_ant == 1) { + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x00); + rtl8225_write_phy_cck(dev, 0x11, 0xBB); + rtl8225_write_phy_cck(dev, 0x01, 0x47); + rtl8225_write_phy_ofdm(dev, 0x0D, 0x54); + rtl8225_write_phy_ofdm(dev, 0x18, 0x32); + } else { /* main antenna */ + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); + rtl8225_write_phy_cck(dev, 0x11, 0x9B); + rtl8225_write_phy_cck(dev, 0x01, 0x47); + rtl8225_write_phy_ofdm(dev, 0x0D, 0x5C); + rtl8225_write_phy_ofdm(dev, 0x18, 0x32); + } + } + /* priv->curr_ant = def_ant; */ +} + +static void rtl8180_int_enable(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + rtl818x_iowrite32(priv, &priv->map->IMR, + IMR_TBDER | IMR_TBDOK | + IMR_TVODER | IMR_TVODOK | + IMR_TVIDER | IMR_TVIDOK | + IMR_TBEDER | IMR_TBEDOK | + IMR_TBKDER | IMR_TBKDOK | + IMR_RDU | IMR_RER | + IMR_ROK | IMR_RQOSOK); + } else { + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + } +} + +static void rtl8180_int_disable(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + rtl818x_iowrite32(priv, &priv->map->IMR, 0); + } else { + rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + } +} + +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; + + case RTL818X_CHIP_FAMILY_RTL8187SE: + /* in 8187se this is a BITMAP */ + rtl818x_iowrite16(priv, &priv->map->BRSR_8187SE, rates_mask); + break; + } +} + +static void rtl8180_config_cardbus(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u16 reg16; + u8 reg8; + + reg8 = rtl818x_ioread8(priv, &priv->map->CONFIG3); + reg8 |= 1 << 1; + rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg8); + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + rtl818x_iowrite16(priv, FEMR_SE, 0xffff); + } else { + reg16 = rtl818x_ioread16(priv, &priv->map->FEMR); + reg16 |= (1 << 15) | (1 << 14) | (1 << 4); + rtl818x_iowrite16(priv, &priv->map->FEMR, reg16); + } + +} + static int rtl8180_init_hw(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; u16 reg; + u32 reg32; rtl818x_iowrite8(priv, &priv->map->CMD, 0); rtl818x_ioread8(priv, &priv->map->CMD); msleep(10); /* reset */ - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + rtl8180_int_disable(dev); rtl818x_ioread8(priv, &priv->map->CMD); reg = rtl818x_ioread8(priv, &priv->map->CMD); @@ -374,31 +800,45 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) msleep(200); if (rtl818x_ioread8(priv, &priv->map->CONFIG3) & (1 << 3)) { - /* For cardbus */ - reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); - reg |= 1 << 1; - rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); - reg = rtl818x_ioread16(priv, &priv->map->FEMR); - reg |= (1 << 15) | (1 << 14) | (1 << 4); - rtl818x_iowrite16(priv, &priv->map->FEMR, reg); + rtl8180_config_cardbus(dev); } - rtl818x_iowrite8(priv, &priv->map->MSR, 0); + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + rtl818x_iowrite8(priv, &priv->map->MSR, RTL818X_MSR_ENEDCA); + else + 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); - rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); - rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); - rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); - rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); + /* mac80211 queue have higher prio for lower index. The last queue + * (that mac80211 is not aware of) is reserved for beacons (and have + * the highest priority on the NIC) + */ + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8187SE) { + rtl818x_iowrite32(priv, &priv->map->TBDA, + priv->tx_ring[1].dma); + rtl818x_iowrite32(priv, &priv->map->TLPDA, + priv->tx_ring[0].dma); + } else { + rtl818x_iowrite32(priv, &priv->map->TBDA, + priv->tx_ring[4].dma); + rtl818x_iowrite32(priv, &priv->map->TVODA, + priv->tx_ring[0].dma); + rtl818x_iowrite32(priv, &priv->map->TVIDA, + priv->tx_ring[1].dma); + rtl818x_iowrite32(priv, &priv->map->TBEDA, + priv->tx_ring[2].dma); + rtl818x_iowrite32(priv, &priv->map->TBKDA, + priv->tx_ring[3].dma); + } /* TODO: necessary? specs indicate not */ 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)); } @@ -410,13 +850,17 @@ 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); + } else { + rtl818x_iowrite8(priv, &priv->map->SECURITY, 0); - rtl818x_iowrite16(priv, &priv->map->BRSR, 0x01F3); + rtl818x_iowrite8(priv, &priv->map->PHY_DELAY, 0x6); + rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); + } + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) { /* TODO: set ClkRun enable? necessary? */ reg = rtl818x_ioread8(priv, &priv->map->GP_ENABLE); rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, reg & ~(1 << 6)); @@ -424,28 +868,93 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev) reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); 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); - rtl818x_iowrite8(priv, &priv->map->CARRIER_SENSE_COUNTER, 0x4C); } + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + + /* the set auto rate fallback bitmask from 1M to 54 Mb/s */ + rtl818x_iowrite16(priv, ARFR, 0xFFF); + rtl818x_ioread16(priv, ARFR); + + /* stop unused queus (no dma alloc) */ + rtl818x_iowrite8(priv, &priv->map->TPPOLL_STOP, + RTL818x_TPPOLL_STOP_MG | RTL818x_TPPOLL_STOP_HI); + + rtl818x_iowrite8(priv, &priv->map->ACM_CONTROL, 0x00); + rtl818x_iowrite16(priv, &priv->map->TID_AC_MAP, 0xFA50); + + rtl818x_iowrite16(priv, &priv->map->INT_MIG, 0); + + /* some black magic here.. */ + rtl8187se_mac_config(dev); + + rtl818x_iowrite16(priv, RFSW_CTRL, 0x569A); + rtl818x_ioread16(priv, RFSW_CTRL); + + rtl8180_set_anaparam(priv, RTL8225SE_ANAPARAM_ON); + rtl8180_set_anaparam2(priv, RTL8225SE_ANAPARAM2_ON); + rtl8180_set_anaparam3(priv, RTL8225SE_ANAPARAM3); + + + rtl818x_iowrite8(priv, &priv->map->CONFIG5, + rtl818x_ioread8(priv, &priv->map->CONFIG5) & 0x7F); + + /*probably this switch led on */ + rtl818x_iowrite8(priv, &priv->map->PGSELECT, + rtl818x_ioread8(priv, &priv->map->PGSELECT) | 0x08); + + rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480); + rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1BFF); + rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x2488); + + rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x4003); + + /* the reference code mac hardcode table write + * this reg by doing byte-wide accesses. + * It does it just for lowest and highest byte.. + */ + reg32 = rtl818x_ioread32(priv, &priv->map->RF_PARA); + reg32 &= 0x00ffff00; + reg32 |= 0xb8000054; + rtl818x_iowrite32(priv, &priv->map->RF_PARA, reg32); + } else + /* stop unused queus (no dma alloc) */ + rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, + (1<<1) | (1<<2)); + 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); + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + rtl8187se_set_antenna_config(dev, + priv->antenna_diversity_default, + priv->antenna_diversity_en); return 0; } static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) { struct rtl8180_priv *priv = dev->priv; - struct rtl8180_rx_desc *entry; + struct rtl818x_rx_cmd_desc *entry; int i; + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + priv->rx_ring_sz = sizeof(struct rtl8187se_rx_desc); + else + priv->rx_ring_sz = sizeof(struct rtl8180_rx_desc); + priv->rx_ring = pci_alloc_consistent(priv->pdev, - sizeof(*priv->rx_ring) * 32, + priv->rx_ring_sz * 32, &priv->rx_ring_dma); if (!priv->rx_ring || (unsigned long)priv->rx_ring & 0xFF) { @@ -453,20 +962,28 @@ static int rtl8180_init_rx_ring(struct ieee80211_hw *dev) return -ENOMEM; } - memset(priv->rx_ring, 0, sizeof(*priv->rx_ring) * 32); + memset(priv->rx_ring, 0, priv->rx_ring_sz * 32); priv->rx_idx = 0; for (i = 0; i < 32; i++) { struct sk_buff *skb = dev_alloc_skb(MAX_RX_SIZE); dma_addr_t *mapping; - entry = &priv->rx_ring[i]; - if (!skb) - return 0; - + entry = priv->rx_ring + priv->rx_ring_sz*i; + if (!skb) { + wiphy_err(dev->wiphy, "Cannot allocate RX skb\n"); + return -ENOMEM; + } priv->rx_buf[i] = skb; mapping = (dma_addr_t *)skb->cb; *mapping = pci_map_single(priv->pdev, skb_tail_pointer(skb), MAX_RX_SIZE, PCI_DMA_FROMDEVICE); + + if (pci_dma_mapping_error(priv->pdev, *mapping)) { + kfree_skb(skb); + wiphy_err(dev->wiphy, "Cannot map DMA for RX skb\n"); + return -ENOMEM; + } + entry->rx_buf = cpu_to_le32(*mapping); entry->flags = cpu_to_le32(RTL818X_RX_DESC_FLAG_OWN | MAX_RX_SIZE); @@ -491,7 +1008,7 @@ static void rtl8180_free_rx_ring(struct ieee80211_hw *dev) kfree_skb(skb); } - pci_free_consistent(priv->pdev, sizeof(*priv->rx_ring) * 32, + pci_free_consistent(priv->pdev, priv->rx_ring_sz * 32, priv->rx_ring, priv->rx_ring_dma); priv->rx_ring = NULL; } @@ -555,7 +1072,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) if (ret) return ret; - for (i = 0; i < 4; i++) + for (i = 0; i < (dev->queues + 1); i++) if ((ret = rtl8180_init_tx_ring(dev, i, 16))) goto err_free_rings; @@ -563,23 +1080,28 @@ static int rtl8180_start(struct ieee80211_hw *dev) if (ret) goto err_free_rings; - rtl818x_iowrite32(priv, &priv->map->RDSAR, priv->rx_ring_dma); - rtl818x_iowrite32(priv, &priv->map->TBDA, priv->tx_ring[3].dma); - rtl818x_iowrite32(priv, &priv->map->THPDA, priv->tx_ring[2].dma); - rtl818x_iowrite32(priv, &priv->map->TNPDA, priv->tx_ring[1].dma); - rtl818x_iowrite32(priv, &priv->map->TLPDA, priv->tx_ring[0].dma); - - ret = request_irq(priv->pdev->irq, rtl8180_interrupt, + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + ret = request_irq(priv->pdev->irq, rtl8187se_interrupt, + IRQF_SHARED, KBUILD_MODNAME, dev); + } else { + ret = request_irq(priv->pdev->irq, rtl8180_interrupt, IRQF_SHARED, KBUILD_MODNAME, dev); + } + if (ret) { wiphy_err(dev->wiphy, "failed to register IRQ handler\n"); goto err_free_rings; } - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0xFFFF); + rtl8180_int_enable(dev); - rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); - rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); + /* in rtl8187se at MAR regs offset there is the management + * TX descriptor DMA addres.. + */ + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8187SE) { + rtl818x_iowrite32(priv, &priv->map->MAR[0], ~0); + rtl818x_iowrite32(priv, &priv->map->MAR[1], ~0); + } reg = RTL818X_RX_CONF_ONLYERLPKT | RTL818X_RX_CONF_RX_AUTORESETPHY | @@ -589,27 +1111,42 @@ 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 { + else if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8180) { reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE1) ? RTL818X_RX_CONF_CSDM1 : 0; reg |= (priv->rfparam & RF_PARAM_CARRIERSENSE2) ? RTL818X_RX_CONF_CSDM2 : 0; + } else { + reg &= ~(RTL818X_RX_CONF_CSDM1 | RTL818X_RX_CONF_CSDM2); } 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); - reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + + /* CW is not on per-packet basis. + * in rtl8185 the CW_VALUE reg is used. + * in rtl8187se the AC param regs are used. + */ + reg &= ~RTL818X_CW_CONF_PERPACKET_CW; + /* retry limit IS on per-packet basis. + * the short and long retry limit in TX_CONF + * reg are ignored + */ + reg |= RTL818X_CW_CONF_PERPACKET_RETRY; rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + /* TX antenna and TX gain are not on per-packet basis. + * TX Antenna is selected by ANTSEL reg (RX in BB regs). + * TX gain is selected with CCK_TX_AGC and OFDM_TX_AGC regs + */ + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL; reg |= RTL818X_TX_AGC_CTL_FEEDBACK_ANT; rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); @@ -621,11 +1158,16 @@ 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_RTL8187SE) + reg |= 1<<30; /* "duration procedure mode" */ + + if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) reg &= ~RTL818X_TX_CONF_PROBE_DTS; else reg &= ~RTL818X_TX_CONF_HW_SEQNUM; + reg &= ~RTL818X_TX_CONF_DISCW; + /* different meaning, same value on both rtl8185 and rtl8180 */ reg &= ~RTL818X_TX_CONF_SAT_HWPLCP; @@ -640,7 +1182,7 @@ static int rtl8180_start(struct ieee80211_hw *dev) err_free_rings: rtl8180_free_rx_ring(dev); - for (i = 0; i < 4; i++) + for (i = 0; i < (dev->queues + 1); i++) if (priv->tx_ring[i].desc) rtl8180_free_tx_ring(dev, i); @@ -653,7 +1195,7 @@ static void rtl8180_stop(struct ieee80211_hw *dev) u8 reg; int i; - rtl818x_iowrite16(priv, &priv->map->INT_MASK, 0); + rtl8180_int_disable(dev); reg = rtl818x_ioread8(priv, &priv->map->CMD); reg &= ~RTL818X_CMD_TX_ENABLE; @@ -670,7 +1212,7 @@ static void rtl8180_stop(struct ieee80211_hw *dev) free_irq(priv->pdev->irq, dev); rtl8180_free_rx_ring(dev); - for (i = 0; i < 4; i++) + for (i = 0; i < (dev->queues + 1); i++) rtl8180_free_tx_ring(dev, i); } @@ -778,6 +1320,123 @@ static int rtl8180_config(struct ieee80211_hw *dev, u32 changed) return 0; } +static void rtl8187se_conf_ac_parm(struct ieee80211_hw *dev, u8 queue) +{ + const struct ieee80211_tx_queue_params *params; + struct rtl8180_priv *priv = dev->priv; + + /* hw value */ + u32 ac_param; + + u8 aifs; + u8 txop; + u8 cw_min, cw_max; + + params = &priv->queue_param[queue]; + + cw_min = fls(params->cw_min); + cw_max = fls(params->cw_max); + + aifs = 10 + params->aifs * priv->slot_time; + + /* TODO: check if txop HW is in us (mult by 32) */ + txop = params->txop; + + ac_param = txop << AC_PARAM_TXOP_LIMIT_SHIFT | + cw_max << AC_PARAM_ECW_MAX_SHIFT | + cw_min << AC_PARAM_ECW_MIN_SHIFT | + aifs << AC_PARAM_AIFS_SHIFT; + + switch (queue) { + case IEEE80211_AC_BK: + rtl818x_iowrite32(priv, &priv->map->AC_BK_PARAM, ac_param); + break; + case IEEE80211_AC_BE: + rtl818x_iowrite32(priv, &priv->map->AC_BE_PARAM, ac_param); + break; + case IEEE80211_AC_VI: + rtl818x_iowrite32(priv, &priv->map->AC_VI_PARAM, ac_param); + break; + case IEEE80211_AC_VO: + rtl818x_iowrite32(priv, &priv->map->AC_VO_PARAM, ac_param); + break; + } +} + +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); + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + priv->queue_param[queue] = *params; + rtl8187se_conf_ac_parm(dev, queue); + } else + 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); + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + rtl818x_iowrite8(priv, &priv->map->EIFS_8187SE, hw_eifs); + else if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) { + /* 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, @@ -802,11 +1461,40 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev, reg = RTL818X_MSR_INFRA; } else reg = RTL818X_MSR_NO_LINK; + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + reg |= RTL818X_MSR_ENEDCA; + 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); + + /* mac80211 supplies aifs_n to driver and calls + * conf_tx callback whether aifs_n changes, NOT + * when aifs changes. + * Aifs should be recalculated if slot changes. + */ + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + for (i = 0; i < 4; i++) + rtl8187se_conf_ac_parm(dev, i); + } + } if (changed & BSS_CHANGED_BEACON_ENABLED) vif_priv->enable_beacon = info->enable_beacon; @@ -864,6 +1552,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, @@ -871,8 +1560,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; @@ -883,8 +1571,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) @@ -901,6 +1588,83 @@ 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); + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + eeprom_cck_table_adr = 0x30; + else + 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); + } + + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) { + eeprom_93cx6_read(&eeprom, 0x3F, &eeprom_val); + priv->antenna_diversity_en = !!(eeprom_val & 0x100); + priv->antenna_diversity_default = (eeprom_val & 0xC00) == 0x400; + + eeprom_93cx6_read(&eeprom, 0x7C, &eeprom_val); + priv->xtal_out = eeprom_val & 0xF; + priv->xtal_in = (eeprom_val & 0xF0) >> 4; + priv->xtal_cal = !!(eeprom_val & 0x1000); + priv->thermal_meter_val = (eeprom_val & 0xF00) >> 8; + priv->thermal_meter_en = !!(eeprom_val & 0x2000); + } + + 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) { @@ -908,12 +1672,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) { @@ -995,7 +1756,6 @@ static int rtl8180_probe(struct pci_dev *pdev, dev->vif_data_size = sizeof(struct rtl8180_vif); dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); - dev->queues = 1; dev->max_signal = 65; reg = rtl818x_ioread32(priv, &priv->map->TX_CONF); @@ -1003,43 +1763,55 @@ 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; + + case RTL818X_TX_CONF_RTL8187SE: + chip_name = "RTL8187SE"; + priv->chip_family = RTL818X_CHIP_FAMILY_RTL8187SE; + break; + default: printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n", pci_name(pdev), reg >> 25); goto err_iounmap; } - priv->r8185 = reg & RTL818X_TX_CONF_R8185_ABC; - if (priv->r8185) { + /* we declare to MAC80211 all the queues except for beacon queue + * that will be eventually handled by DRV. + * TX rings are arranged in such a way that lower is the IDX, + * higher is the priority, in order to achieve direct mapping + * with mac80211, however the beacon queue is an exception and it + * is mapped on the highst tx ring IDX. + */ + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + dev->queues = RTL8187SE_NR_TX_QUEUES - 1; + else + dev->queues = RTL8180_NR_TX_QUEUES - 1; + + 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"; @@ -1050,14 +1822,18 @@ static int rtl8180_probe(struct pci_dev *pdev, break; case 5: priv->rf = &grf5101_rf_ops; break; - case 9: priv->rf = rtl8180_detect_rf(dev); + case 9: + if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) + priv->rf = rtl8187se_detect_rf(dev); + else + priv->rf = rtl8180_detect_rf(dev); break; case 10: rf_name = "RTL8255"; 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; } @@ -1067,42 +1843,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); - } - 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; + eth_random_addr(priv->mac_addr); } - - /* 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); @@ -1114,15 +1860,14 @@ 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; err_iounmap: - iounmap(priv->map); + pci_iounmap(pdev, priv->map); err_free_dev: - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: diff --git a/drivers/net/wireless/rtl818x/rtl8180/grf5101.c b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c index dc845693f32..b1bfee73893 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/grf5101.c +++ b/drivers/net/wireless/rtl818x/rtl8180/grf5101.c @@ -19,7 +19,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> #include <net/mac80211.h> diff --git a/drivers/net/wireless/rtl818x/rtl8180/max2820.c b/drivers/net/wireless/rtl818x/rtl8180/max2820.c index a63c443c3c6..eebf2397652 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/max2820.c +++ b/drivers/net/wireless/rtl818x/rtl8180/max2820.c @@ -18,7 +18,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> #include <net/mac80211.h> diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h index 30523314da4..291a55970d1 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h @@ -24,27 +24,64 @@ #define ANAPARAM_PWR1_SHIFT 20 #define ANAPARAM_PWR1_MASK (0x7F << ANAPARAM_PWR1_SHIFT) +/* rtl8180/rtl8185 have 3 queue + beacon queue. + * mac80211 can use just one, + beacon = 2 tot. + */ +#define RTL8180_NR_TX_QUEUES 2 + +/* rtl8187SE have 6 queues + beacon queues + * mac80211 can use 4 QoS data queue, + beacon = 5 tot + */ +#define RTL8187SE_NR_TX_QUEUES 5 + +/* for array static allocation, it is the max of above */ +#define RTL818X_NR_TX_QUEUES 5 + struct rtl8180_tx_desc { __le32 flags; __le16 rts_duration; __le16 plcp_len; __le32 tx_buf; - __le32 frame_len; + union{ + __le32 frame_len; + struct { + __le16 frame_len_se; + __le16 frame_duration; + } __packed; + } __packed; __le32 next_tx_desc; u8 cw; u8 retry_limit; u8 agc; u8 flags2; - u32 reserved[2]; + /* rsvd for 8180/8185. + * valid for 8187se but we dont use it + */ + u32 reserved; + /* all rsvd for 8180/8185 */ + __le16 flags3; + __le16 frag_qsize; +} __packed; + +struct rtl818x_rx_cmd_desc { + __le32 flags; + u32 reserved; + __le32 rx_buf; } __packed; struct rtl8180_rx_desc { __le32 flags; __le32 flags2; - union { - __le32 rx_buf; - __le64 tsft; - }; + __le64 tsft; + +} __packed; + +struct rtl8187se_rx_desc { + __le32 flags; + __le64 tsft; + __le32 flags2; + __le32 flags3; + u32 reserved[3]; } __packed; struct rtl8180_tx_ring { @@ -71,28 +108,45 @@ struct rtl8180_priv { /* rtl8180 driver specific */ spinlock_t lock; - struct rtl8180_rx_desc *rx_ring; + void *rx_ring; + u8 rx_ring_sz; dma_addr_t rx_ring_dma; unsigned int rx_idx; struct sk_buff *rx_buf[32]; - struct rtl8180_tx_ring tx_ring[4]; + struct rtl8180_tx_ring tx_ring[RTL818X_NR_TX_QUEUES]; struct ieee80211_channel channels[14]; struct ieee80211_rate rates[12]; struct ieee80211_supported_band band; + struct ieee80211_tx_queue_params queue_param[4]; struct pci_dev *pdev; u32 rx_conf; - - int r8185; + u8 slot_time; + u16 ack_time; + + enum { + RTL818X_CHIP_FAMILY_RTL8180, + RTL818X_CHIP_FAMILY_RTL8185, + RTL818X_CHIP_FAMILY_RTL8187SE, + } chip_family; u32 anaparam; u16 rfparam; u8 csthreshold; - + u8 mac_addr[ETH_ALEN]; + u8 rf_type; + u8 xtal_out; + u8 xtal_in; + u8 xtal_cal; + u8 thermal_meter_val; + u8 thermal_meter_en; + u8 antenna_diversity_en; + u8 antenna_diversity_default; /* sequence # */ u16 seqno; }; void rtl8180_write_phy(struct ieee80211_hw *dev, u8 addr, u32 data); void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam); +void rtl8180_set_anaparam2(struct rtl8180_priv *priv, u32 anaparam2); static inline u8 rtl818x_ioread8(struct rtl8180_priv *priv, u8 __iomem *addr) { diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c index ee638d0749d..9bda5bc78ed 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225.c @@ -15,7 +15,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> #include <net/mac80211.h> @@ -283,6 +282,7 @@ static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel) msleep(1); /* FIXME: optional? */ + /* TODO: use set_anaparam2 dev.c_func*/ /* anaparam2 on */ rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG); reg = rtl818x_ioread8(priv, &priv->map->CONFIG3); @@ -731,32 +731,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 = { @@ -764,7 +743,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/rtl8180/rtl8225se.c b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c new file mode 100644 index 00000000000..fde89866fa8 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.c @@ -0,0 +1,475 @@ + +/* Radio tuning for RTL8225 on RTL8187SE + * + * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net> + * Copyright 2014 Andrea Merello <andrea.merello@gmail.com> + * + * Based on the r8180 and Realtek r8187se drivers, which are: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Also based on the rtl8187 driver, which is: + * Copyright 2007 Michael Wu <flamingice@sourmilk.net> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.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 as + * published by the Free Software Foundation. + */ + +#include <net/mac80211.h> + +#include "rtl8180.h" +#include "rtl8225se.h" + +#define PFX "rtl8225 (se) " + +static const u32 RF_GAIN_TABLE[] = { + 0x0096, 0x0076, 0x0056, 0x0036, 0x0016, 0x01f6, 0x01d6, 0x01b6, + 0x0196, 0x0176, 0x00F7, 0x00D7, 0x00B7, 0x0097, 0x0077, 0x0057, + 0x0037, 0x00FB, 0x00DB, 0x00BB, 0x00FF, 0x00E3, 0x00C3, 0x00A3, + 0x0083, 0x0063, 0x0043, 0x0023, 0x0003, 0x01E3, 0x01C3, 0x01A3, + 0x0183, 0x0163, 0x0143, 0x0123, 0x0103 +}; + +static const u8 cck_ofdm_gain_settings[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, +}; + +static const u8 rtl8225se_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static const u8 rtl8225se_tx_power_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static const u8 rtl8225se_tx_power_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225se_tx_power_ofdm[] = { + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static const u32 rtl8225se_chan[] = { + 0x0080, 0x0100, 0x0180, 0x0200, 0x0280, 0x0300, 0x0380, + 0x0400, 0x0480, 0x0500, 0x0580, 0x0600, 0x0680, 0x074A, +}; + +static const u8 rtl8225sez2_tx_power_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 rtl8225sez2_tx_power_cck_B[] = { + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04 +}; + +static const u8 rtl8225sez2_tx_power_cck_A[] = { + 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04 +}; + +static const u8 rtl8225sez2_tx_power_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static const u8 ZEBRA_AGC[] = { + 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7C, 0x7B, 0x7A, + 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, + 0x71, 0x70, 0x6F, 0x6E, 0x6D, 0x6C, 0x6B, 0x6A, + 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, + 0x48, 0x47, 0x46, 0x45, 0x44, 0x29, 0x28, 0x27, + 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x08, 0x07, + 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x15, 0x16, + 0x17, 0x17, 0x18, 0x18, 0x19, 0x1a, 0x1a, 0x1b, + 0x1b, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, + 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x21, + 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F, 0x2F +}; + +static const u8 OFDM_CONFIG[] = { + 0x10, 0x0F, 0x0A, 0x0C, 0x14, 0xFA, 0xFF, 0x50, + 0x00, 0x50, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, + 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xA8, 0x26, + 0x32, 0x33, 0x06, 0xA5, 0x6F, 0x55, 0xC8, 0xBB, + 0x0A, 0xE1, 0x2C, 0x4A, 0x86, 0x83, 0x34, 0x00, + 0x4F, 0x24, 0x6F, 0xC2, 0x03, 0x40, 0x80, 0x00, + 0xC0, 0xC1, 0x58, 0xF1, 0x00, 0xC4, 0x90, 0x3e, + 0xD8, 0x3C, 0x7B, 0x10, 0x10 +}; + +static void rtl8187se_three_wire_io(struct ieee80211_hw *dev, u8 *data, + u8 len, bool write) +{ + struct rtl8180_priv *priv = dev->priv; + int i; + u8 tmp; + + do { + for (i = 0; i < 5; i++) { + tmp = rtl818x_ioread8(priv, SW_3W_CMD1); + if (!(tmp & 0x3)) + break; + udelay(10); + } + if (i == 5) + wiphy_err(dev->wiphy, PFX + "CmdReg: 0x%x RE/WE bits aren't clear\n", tmp); + + tmp = rtl818x_ioread8(priv, &priv->map->rf_sw_config) | 0x02; + rtl818x_iowrite8(priv, &priv->map->rf_sw_config, tmp); + + tmp = rtl818x_ioread8(priv, REG_ADDR1(0x84)) & 0xF7; + rtl818x_iowrite8(priv, REG_ADDR1(0x84), tmp); + if (write) { + if (len == 16) { + rtl818x_iowrite16(priv, SW_3W_DB0, + *(u16 *)data); + } else if (len == 64) { + rtl818x_iowrite32(priv, SW_3W_DB0_4, + *((u32 *)data)); + rtl818x_iowrite32(priv, SW_3W_DB1_4, + *((u32 *)(data + 4))); + } else + wiphy_err(dev->wiphy, PFX + "Unimplemented length\n"); + } else { + rtl818x_iowrite16(priv, SW_3W_DB0, *(u16 *)data); + } + if (write) + tmp = 2; + else + tmp = 1; + rtl818x_iowrite8(priv, SW_3W_CMD1, tmp); + for (i = 0; i < 5; i++) { + tmp = rtl818x_ioread8(priv, SW_3W_CMD1); + if (!(tmp & 0x3)) + break; + udelay(10); + } + rtl818x_iowrite8(priv, SW_3W_CMD1, 0); + if (!write) { + *((u16 *)data) = rtl818x_ioread16(priv, SI_DATA_REG); + *((u16 *)data) &= 0x0FFF; + } + } while (0); +} + +static u32 rtl8187se_rf_readreg(struct ieee80211_hw *dev, u8 addr) +{ + u32 dataread = addr & 0x0F; + rtl8187se_three_wire_io(dev, (u8 *)&dataread, 16, 0); + return dataread; +} + +static void rtl8187se_rf_writereg(struct ieee80211_hw *dev, u8 addr, u32 data) +{ + u32 outdata = (data << 4) | (u32)(addr & 0x0F); + rtl8187se_three_wire_io(dev, (u8 *)&outdata, 16, 1); +} + + +static void rtl8225se_write_zebra_agc(struct ieee80211_hw *dev) +{ + int i; + + for (i = 0; i < 128; i++) { + rtl8225se_write_phy_ofdm(dev, 0xF, ZEBRA_AGC[i]); + rtl8225se_write_phy_ofdm(dev, 0xE, i+0x80); + rtl8225se_write_phy_ofdm(dev, 0xE, 0); + } +} + +static void rtl8187se_write_ofdm_config(struct ieee80211_hw *dev) +{ + /* write OFDM_CONFIG table */ + int i; + + for (i = 0; i < 60; i++) + rtl8225se_write_phy_ofdm(dev, i, OFDM_CONFIG[i]); + +} + +static void rtl8225sez2_rf_set_tx_power(struct ieee80211_hw *dev, int channel) +{ + struct rtl8180_priv *priv = dev->priv; + u8 cck_power, ofdm_power; + + cck_power = priv->channels[channel - 1].hw_value & 0xFF; + if (cck_power > 35) + cck_power = 35; + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, + cck_ofdm_gain_settings[cck_power]); + + usleep_range(1000, 5000); + ofdm_power = priv->channels[channel - 1].hw_value >> 8; + if (ofdm_power > 35) + ofdm_power = 35; + + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, + cck_ofdm_gain_settings[ofdm_power]); + if (ofdm_power < 12) { + rtl8225se_write_phy_ofdm(dev, 7, 0x5C); + rtl8225se_write_phy_ofdm(dev, 9, 0x5C); + } + if (ofdm_power < 18) { + rtl8225se_write_phy_ofdm(dev, 7, 0x54); + rtl8225se_write_phy_ofdm(dev, 9, 0x54); + } else { + rtl8225se_write_phy_ofdm(dev, 7, 0x50); + rtl8225se_write_phy_ofdm(dev, 9, 0x50); + } + + usleep_range(1000, 5000); +} + +static void rtl8187se_write_rf_gain(struct ieee80211_hw *dev) +{ + int i; + + for (i = 0; i <= 36; i++) { + rtl8187se_rf_writereg(dev, 0x01, i); mdelay(1); + rtl8187se_rf_writereg(dev, 0x02, RF_GAIN_TABLE[i]); mdelay(1); + } +} + +static void rtl8187se_write_initial_gain(struct ieee80211_hw *dev, + int init_gain) +{ + switch (init_gain) { + default: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x26); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFA); mdelay(1); + break; + case 2: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x36); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFA); mdelay(1); + break; + case 3: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x36); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFB); mdelay(1); + break; + case 4: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x46); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0x86); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFB); mdelay(1); + break; + case 5: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x46); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0x96); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFB); mdelay(1); + break; + case 6: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x56); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0x96); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFC); mdelay(1); + break; + case 7: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x56); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0xA6); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFC); mdelay(1); + break; + case 8: + rtl8225se_write_phy_ofdm(dev, 0x17, 0x66); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x24, 0xB6); mdelay(1); + rtl8225se_write_phy_ofdm(dev, 0x05, 0xFC); mdelay(1); + break; + } +} + +void rtl8225se_rf_init(struct ieee80211_hw *dev) +{ + struct rtl8180_priv *priv = dev->priv; + u32 rf23, rf24; + u8 d_cut = 0; + u8 tmp; + + /* Page 1 */ + rtl8187se_rf_writereg(dev, 0x00, 0x013F); mdelay(1); + rf23 = rtl8187se_rf_readreg(dev, 0x08); mdelay(1); + rf24 = rtl8187se_rf_readreg(dev, 0x09); mdelay(1); + if (rf23 == 0x0818 && rf24 == 0x070C) + d_cut = 1; + + wiphy_info(dev->wiphy, "RTL8225-SE version %s\n", + d_cut ? "D" : "not-D"); + + /* Page 0: reg 0 - 15 */ + rtl8187se_rf_writereg(dev, 0x00, 0x009F); mdelay(1); + rtl8187se_rf_writereg(dev, 0x01, 0x06E0); mdelay(1); + rtl8187se_rf_writereg(dev, 0x02, 0x004D); mdelay(1); + rtl8187se_rf_writereg(dev, 0x03, 0x07F1); mdelay(1); + rtl8187se_rf_writereg(dev, 0x04, 0x0975); mdelay(1); + rtl8187se_rf_writereg(dev, 0x05, 0x0C72); mdelay(1); + rtl8187se_rf_writereg(dev, 0x06, 0x0AE6); mdelay(1); + rtl8187se_rf_writereg(dev, 0x07, 0x00CA); mdelay(1); + rtl8187se_rf_writereg(dev, 0x08, 0x0E1C); mdelay(1); + rtl8187se_rf_writereg(dev, 0x09, 0x02F0); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0A, 0x09D0); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0B, 0x01BA); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0C, 0x0640); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0D, 0x08DF); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0E, 0x0020); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0F, 0x0990); mdelay(1); + /* page 1: reg 16-30 */ + rtl8187se_rf_writereg(dev, 0x00, 0x013F); mdelay(1); + rtl8187se_rf_writereg(dev, 0x03, 0x0806); mdelay(1); + rtl8187se_rf_writereg(dev, 0x04, 0x03A7); mdelay(1); + rtl8187se_rf_writereg(dev, 0x05, 0x059B); mdelay(1); + rtl8187se_rf_writereg(dev, 0x06, 0x0081); mdelay(1); + rtl8187se_rf_writereg(dev, 0x07, 0x01A0); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0A, 0x0001); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0B, 0x0418); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0C, 0x0FBE); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0D, 0x0008); mdelay(1); + if (d_cut) + rtl8187se_rf_writereg(dev, 0x0E, 0x0807); + else + rtl8187se_rf_writereg(dev, 0x0E, 0x0806); + mdelay(1); + rtl8187se_rf_writereg(dev, 0x0F, 0x0ACC); mdelay(1); + rtl8187se_rf_writereg(dev, 0x00, 0x01D7); mdelay(1); + rtl8187se_rf_writereg(dev, 0x03, 0x0E00); mdelay(1); + rtl8187se_rf_writereg(dev, 0x04, 0x0E50); mdelay(1); + + rtl8187se_write_rf_gain(dev); + + rtl8187se_rf_writereg(dev, 0x05, 0x0203); mdelay(1); + rtl8187se_rf_writereg(dev, 0x06, 0x0200); mdelay(1); + rtl8187se_rf_writereg(dev, 0x00, 0x0137); mdelay(11); + rtl8187se_rf_writereg(dev, 0x0D, 0x0008); mdelay(11); + rtl8187se_rf_writereg(dev, 0x00, 0x0037); mdelay(11); + rtl8187se_rf_writereg(dev, 0x04, 0x0160); mdelay(11); + rtl8187se_rf_writereg(dev, 0x07, 0x0080); mdelay(11); + rtl8187se_rf_writereg(dev, 0x02, 0x088D); mdelay(221); + rtl8187se_rf_writereg(dev, 0x00, 0x0137); mdelay(11); + rtl8187se_rf_writereg(dev, 0x07, 0x0000); mdelay(1); + rtl8187se_rf_writereg(dev, 0x07, 0x0180); mdelay(1); + rtl8187se_rf_writereg(dev, 0x07, 0x0220); mdelay(1); + rtl8187se_rf_writereg(dev, 0x07, 0x03E0); mdelay(1); + rtl8187se_rf_writereg(dev, 0x06, 0x00C1); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0A, 0x0001); mdelay(1); + if (priv->xtal_cal) { + tmp = (priv->xtal_in << 4) | (priv->xtal_out << 1) | + (1 << 11) | (1 << 9); + rtl8187se_rf_writereg(dev, 0x0F, tmp); + wiphy_info(dev->wiphy, "Xtal cal\n"); + mdelay(1); + } else { + wiphy_info(dev->wiphy, "NO Xtal cal\n"); + rtl8187se_rf_writereg(dev, 0x0F, 0x0ACC); + mdelay(1); + } + /* page 0 */ + rtl8187se_rf_writereg(dev, 0x00, 0x00BF); mdelay(1); + rtl8187se_rf_writereg(dev, 0x0D, 0x08DF); mdelay(1); + rtl8187se_rf_writereg(dev, 0x02, 0x004D); mdelay(1); + rtl8187se_rf_writereg(dev, 0x04, 0x0975); mdelay(31); + rtl8187se_rf_writereg(dev, 0x00, 0x0197); mdelay(1); + rtl8187se_rf_writereg(dev, 0x05, 0x05AB); mdelay(1); + + rtl8187se_rf_writereg(dev, 0x00, 0x009F); mdelay(1); + rtl8187se_rf_writereg(dev, 0x01, 0x0000); mdelay(1); + rtl8187se_rf_writereg(dev, 0x02, 0x0000); mdelay(1); + /* power save parameters */ + /* TODO: move to dev.c */ + rtl818x_iowrite8(priv, REG_ADDR1(0x024E), + rtl818x_ioread8(priv, REG_ADDR1(0x24E)) & 0x9F); + rtl8225se_write_phy_cck(dev, 0x00, 0xC8); + rtl8225se_write_phy_cck(dev, 0x06, 0x1C); + rtl8225se_write_phy_cck(dev, 0x10, 0x78); + rtl8225se_write_phy_cck(dev, 0x2E, 0xD0); + rtl8225se_write_phy_cck(dev, 0x2F, 0x06); + rtl8225se_write_phy_cck(dev, 0x01, 0x46); + + /* power control */ + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, 0x10); + rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, 0x1B); + + rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); + rtl8225se_write_phy_ofdm(dev, 0x00, 0x12); + + rtl8225se_write_zebra_agc(dev); + + rtl8225se_write_phy_ofdm(dev, 0x10, 0x00); + + rtl8187se_write_ofdm_config(dev); + + /* turn on RF */ + rtl8187se_rf_writereg(dev, 0x00, 0x009F); udelay(500); + rtl8187se_rf_writereg(dev, 0x04, 0x0972); udelay(500); + /* turn on RF again */ + rtl8187se_rf_writereg(dev, 0x00, 0x009F); udelay(500); + rtl8187se_rf_writereg(dev, 0x04, 0x0972); udelay(500); + /* turn on BB */ + rtl8225se_write_phy_ofdm(dev, 0x10, 0x40); + rtl8225se_write_phy_ofdm(dev, 0x12, 0x40); + + rtl8187se_write_initial_gain(dev, 4); +} + +void rtl8225se_rf_stop(struct ieee80211_hw *dev) +{ + /* checked for 8187se */ + struct rtl8180_priv *priv = dev->priv; + + /* turn off BB RXIQ matrix to cut off rx signal */ + rtl8225se_write_phy_ofdm(dev, 0x10, 0x00); + rtl8225se_write_phy_ofdm(dev, 0x12, 0x00); + /* turn off RF */ + rtl8187se_rf_writereg(dev, 0x04, 0x0000); + rtl8187se_rf_writereg(dev, 0x00, 0x0000); + + usleep_range(1000, 5000); + /* turn off A/D and D/A */ + rtl8180_set_anaparam(priv, RTL8225SE_ANAPARAM_OFF); + rtl8180_set_anaparam2(priv, RTL8225SE_ANAPARAM2_OFF); +} + +void rtl8225se_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf) +{ + int chan = + ieee80211_frequency_to_channel(conf->chandef.chan->center_freq); + + rtl8225sez2_rf_set_tx_power(dev, chan); + rtl8187se_rf_writereg(dev, 0x7, rtl8225se_chan[chan - 1]); + if ((rtl8187se_rf_readreg(dev, 0x7) & 0x0F80) != + rtl8225se_chan[chan - 1]) + rtl8187se_rf_writereg(dev, 0x7, rtl8225se_chan[chan - 1]); + usleep_range(10000, 20000); +} + +static const struct rtl818x_rf_ops rtl8225se_ops = { + .name = "rtl8225-se", + .init = rtl8225se_rf_init, + .stop = rtl8225se_rf_stop, + .set_chan = rtl8225se_rf_set_channel, +}; + +const struct rtl818x_rf_ops *rtl8187se_detect_rf(struct ieee80211_hw *dev) +{ + return &rtl8225se_ops; +} diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h new file mode 100644 index 00000000000..22940026408 --- /dev/null +++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8225se.h @@ -0,0 +1,61 @@ + +/* Definitions for RTL8187SE hardware + * + * Copyright 2009 Larry Finger <Larry.Finger@lwfinger.net> + * Copyright 2014 Andrea Merello <andrea.merello@gmail.com> + * + * Based on the r8180 and Realtek r8187se drivers, which are: + * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al. + * + * Also based on the rtl8187 driver, which is: + * Copyright 2007 Michael Wu <flamingice@sourmilk.net> + * Copyright 2007 Andrea Merello <andrea.merello@gmail.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 as + * published by the Free Software Foundation. + */ + +#ifndef RTL8187SE_RTL8225_H +#define RTL8187SE_RTL8225_H + +#define RTL8225SE_ANAPARAM_ON 0xb0054d00 +#define RTL8225SE_ANAPARAM2_ON 0x000004c6 + +/* all off except PLL */ +#define RTL8225SE_ANAPARAM_OFF 0xb0054dec +/* all on including PLL */ +#define RTL8225SE_ANAPARAM_OFF2 0xb0054dfc + +#define RTL8225SE_ANAPARAM2_OFF 0x00ff04c6 + +#define RTL8225SE_ANAPARAM3 0x10 + +enum rtl8187se_power_state { + RTL8187SE_POWER_ON, + RTL8187SE_POWER_OFF, + RTL8187SE_POWER_SLEEP +}; + +static inline void rtl8225se_write_phy_ofdm(struct ieee80211_hw *dev, + u8 addr, u8 data) +{ + rtl8180_write_phy(dev, addr, data); +} + +static inline void rtl8225se_write_phy_cck(struct ieee80211_hw *dev, + u8 addr, u8 data) +{ + rtl8180_write_phy(dev, addr, data | 0x10000); +} + + +const struct rtl818x_rf_ops *rtl8187se_detect_rf(struct ieee80211_hw *); +void rtl8225se_rf_stop(struct ieee80211_hw *dev); +void rtl8225se_rf_set_channel(struct ieee80211_hw *dev, + struct ieee80211_conf *conf); +void rtl8225se_rf_conf_erp(struct ieee80211_hw *dev, + struct ieee80211_bss_conf *info); +void rtl8225se_rf_init(struct ieee80211_hw *dev); + +#endif /* RTL8187SE_RTL8225_H */ diff --git a/drivers/net/wireless/rtl818x/rtl8180/sa2400.c b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c index 7614d9ccc72..959b049827d 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/sa2400.c +++ b/drivers/net/wireless/rtl818x/rtl8180/sa2400.c @@ -19,7 +19,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> #include <net/mac80211.h> diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 9a6edb0c014..629ad8cfa17 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -20,7 +20,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/usb.h> #include <linux/slab.h> #include <linux/delay.h> @@ -254,14 +253,21 @@ static void rtl8187_tx(struct ieee80211_hw *dev, flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24; if (ieee80211_has_morefrags(tx_hdr->frame_control)) flags |= RTL818X_TX_DESC_FLAG_MOREFRAG; + + /* HW will perform RTS-CTS when only RTS flags is set. + * HW will perform CTS-to-self when both RTS and CTS flags are set. + * RTS rate and RTS duration will be used also for CTS-to-self. + */ if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { flags |= RTL818X_TX_DESC_FLAG_RTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; rts_dur = ieee80211_rts_duration(dev, priv->vif, skb->len, info); } else if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { - flags |= RTL818X_TX_DESC_FLAG_CTS; + flags |= RTL818X_TX_DESC_FLAG_RTS | RTL818X_TX_DESC_FLAG_CTS; flags |= ieee80211_get_rts_cts_rate(dev, info)->hw_value << 19; + rts_dur = ieee80211_ctstoself_duration(dev, priv->vif, + skb->len, info); } if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { @@ -382,6 +388,8 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.freq = dev->conf.chandef.chan->center_freq; rx_status.band = dev->conf.chandef.chan->band; rx_status.flag |= RX_FLAG_MACTIME_START; + if (flags & RTL818X_RX_DESC_FLAG_SPLCP) + rx_status.flag |= RX_FLAG_SHORTPRE; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); @@ -416,7 +424,7 @@ static int rtl8187_init_urbs(struct ieee80211_hw *dev) struct rtl8187_rx_info *info; int ret = 0; - while (skb_queue_len(&priv->rx_queue) < 16) { + while (skb_queue_len(&priv->rx_queue) < 32) { skb = __dev_alloc_skb(RTL8187_MAX_RX, GFP_KERNEL); if (!skb) { ret = -ENOMEM; @@ -593,7 +601,7 @@ static void rtl8187_set_anaparam(struct rtl8187_priv *priv, bool rfon) rtl818x_iowrite32(priv, &priv->map->ANAPARAM, anaparam); rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, anaparam2); if (priv->is_rtl8187b) - rtl818x_iowrite8(priv, &priv->map->ANAPARAM3, anaparam3); + rtl818x_iowrite8(priv, &priv->map->ANAPARAM3A, anaparam3); reg &= ~RTL818X_CONFIG3_ANAPARAM_WRITE; rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg); rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, @@ -786,7 +794,7 @@ static int rtl8187b_init_hw(struct ieee80211_hw *dev) rtl818x_iowrite16(priv, (__le16 *)0xFF34, 0x0FFF); reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY; rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); /* Auto Rate Fallback Register (ARFR): 1M-54M setting */ @@ -944,8 +952,8 @@ static int rtl8187_start(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL; reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); @@ -987,13 +995,13 @@ static int rtl8187_start(struct ieee80211_hw *dev) rtl818x_iowrite32(priv, &priv->map->RX_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->CW_CONF); - reg &= ~RTL818X_CW_CONF_PERPACKET_CW_SHIFT; - reg |= RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT; + reg &= ~RTL818X_CW_CONF_PERPACKET_CW; + reg |= RTL818X_CW_CONF_PERPACKET_RETRY; rtl818x_iowrite8(priv, &priv->map->CW_CONF, reg); reg = rtl818x_ioread8(priv, &priv->map->TX_AGC_CTL); - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT; - reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_GAIN; + reg &= ~RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL; reg &= ~RTL818X_TX_AGC_CTL_FEEDBACK_ANT; rtl818x_iowrite8(priv, &priv->map->TX_AGC_CTL, reg); @@ -1637,10 +1645,10 @@ static int rtl8187_probe(struct usb_interface *intf, err_free_dmabuf: kfree(priv->io_dmabuf); - err_free_dev: - ieee80211_free_hw(dev); usb_set_intfdata(intf, NULL); usb_put_dev(udev); + err_free_dev: + ieee80211_free_hw(dev); return err; } diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h index 56aee067f32..a6ad79f61bf 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8187.h @@ -15,6 +15,8 @@ #ifndef RTL8187_H #define RTL8187_H +#include <linux/cache.h> + #include "rtl818x.h" #include "leds.h" @@ -139,7 +141,10 @@ struct rtl8187_priv { u8 aifsn[4]; u8 rfkill_mask; struct { - __le64 buf; + union { + __le64 buf; + u8 dummy1[L1_CACHE_BYTES]; + } ____cacheline_aligned; struct sk_buff_head queue; } b_tx_status; /* This queue is used by both -b and non-b devices */ struct mutex io_mutex; @@ -147,7 +152,8 @@ struct rtl8187_priv { u8 bits8; __le16 bits16; __le32 bits32; - } *io_dmabuf; + u8 dummy2[L1_CACHE_BYTES]; + } *io_dmabuf ____cacheline_aligned; bool rfkill_off; u16 seqno; }; diff --git a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c index a26193a0444..5ecf18ed67b 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c +++ b/drivers/net/wireless/rtl818x/rtl8187/rtl8225.c @@ -16,7 +16,6 @@ * published by the Free Software Foundation. */ -#include <linux/init.h> #include <linux/usb.h> #include <net/mac80211.h> diff --git a/drivers/net/wireless/rtl818x/rtl818x.h b/drivers/net/wireless/rtl818x/rtl818x.h index ce23dfd4238..7abef95d278 100644 --- a/drivers/net/wireless/rtl818x/rtl818x.h +++ b/drivers/net/wireless/rtl818x/rtl818x.h @@ -16,30 +16,82 @@ #define RTL818X_H struct rtl818x_csr { + u8 MAC[6]; u8 reserved_0[2]; - __le32 MAR[2]; - u8 RX_FIFO_COUNT; - u8 reserved_1; - u8 TX_FIFO_COUNT; - u8 BQREQ; - u8 reserved_2[4]; + + union { + __le32 MAR[2]; /* 0x8 */ + + struct{ /* rtl8187se */ + u8 rf_sw_config; /* 0x8 */ + u8 reserved_01[3]; + __le32 TMGDA; /* 0xc */ + } __packed; + } __packed; + + union { /* 0x10 */ + struct { + u8 RX_FIFO_COUNT; + u8 reserved_1; + u8 TX_FIFO_COUNT; + u8 BQREQ; + } __packed; + + __le32 TBKDA; /* for 8187se */ + } __packed; + + __le32 TBEDA; /* 0x14 - for rtl8187se */ + __le32 TSFT[2]; - __le32 TLPDA; - __le32 TNPDA; - __le32 THPDA; - __le16 BRSR; - u8 BSSID[6]; - u8 RESP_RATE; - u8 EIFS; - u8 reserved_3[1]; - u8 CMD; + + union { /* 0x20 */ + __le32 TLPDA; + __le32 TVIDA; /* for 8187se */ + } __packed; + + union { /* 0x24 */ + __le32 TNPDA; + __le32 TVODA; /* for 8187se */ + } __packed; + + /* hi pri ring for all cards */ + __le32 THPDA; /* 0x28 */ + + union { /* 0x2c */ + struct { + u8 reserved_2a; + u8 EIFS_8187SE; + } __packed; + + __le16 BRSR; + } __packed; + + u8 BSSID[6]; /* 0x2e */ + + union { /* 0x34 */ + struct { + u8 RESP_RATE; + u8 EIFS; + } __packed; + __le16 BRSR_8187SE; + } __packed; + + u8 reserved_3[1]; /* 0x36 */ + u8 CMD; /* 0x37 */ #define RTL818X_CMD_TX_ENABLE (1 << 2) #define RTL818X_CMD_RX_ENABLE (1 << 3) #define RTL818X_CMD_RESET (1 << 4) - u8 reserved_4[4]; - __le16 INT_MASK; - __le16 INT_STATUS; + u8 reserved_4[4]; /* 0x38 */ + union { + struct { + __le16 INT_MASK; + __le16 INT_STATUS; + } __packed; + + __le32 INT_STATUS_SE; /* 0x3c */ + } __packed; +/* status bits for rtl8187 and rtl8180/8185 */ #define RTL818X_INT_RX_OK (1 << 0) #define RTL818X_INT_RX_ERR (1 << 1) #define RTL818X_INT_TXL_OK (1 << 2) @@ -56,7 +108,34 @@ struct rtl818x_csr { #define RTL818X_INT_BEACON (1 << 13) #define RTL818X_INT_TIME_OUT (1 << 14) #define RTL818X_INT_TX_FO (1 << 15) - __le32 TX_CONF; +/* status bits for rtl8187se */ +#define RTL818X_INT_SE_TIMER3 (1 << 0) +#define RTL818X_INT_SE_TIMER2 (1 << 1) +#define RTL818X_INT_SE_RQ0SOR (1 << 2) +#define RTL818X_INT_SE_TXBED_OK (1 << 3) +#define RTL818X_INT_SE_TXBED_ERR (1 << 4) +#define RTL818X_INT_SE_TXBE_OK (1 << 5) +#define RTL818X_INT_SE_TXBE_ERR (1 << 6) +#define RTL818X_INT_SE_RX_OK (1 << 7) +#define RTL818X_INT_SE_RX_ERR (1 << 8) +#define RTL818X_INT_SE_TXL_OK (1 << 9) +#define RTL818X_INT_SE_TXL_ERR (1 << 10) +#define RTL818X_INT_SE_RX_DU (1 << 11) +#define RTL818X_INT_SE_RX_FIFO (1 << 12) +#define RTL818X_INT_SE_TXN_OK (1 << 13) +#define RTL818X_INT_SE_TXN_ERR (1 << 14) +#define RTL818X_INT_SE_TXH_OK (1 << 15) +#define RTL818X_INT_SE_TXH_ERR (1 << 16) +#define RTL818X_INT_SE_TXB_OK (1 << 17) +#define RTL818X_INT_SE_TXB_ERR (1 << 18) +#define RTL818X_INT_SE_ATIM_TO (1 << 19) +#define RTL818X_INT_SE_BK_TO (1 << 20) +#define RTL818X_INT_SE_TIMER1 (1 << 21) +#define RTL818X_INT_SE_TX_FIFO (1 << 22) +#define RTL818X_INT_SE_WAKEUP (1 << 23) +#define RTL818X_INT_SE_BK_DMA (1 << 24) +#define RTL818X_INT_SE_TMGD_OK (1 << 30) + __le32 TX_CONF; /* 0x40 */ #define RTL818X_TX_CONF_LOOPBACK_MAC (1 << 17) #define RTL818X_TX_CONF_LOOPBACK_CONT (3 << 17) #define RTL818X_TX_CONF_NO_ICV (1 << 19) @@ -68,6 +147,7 @@ struct rtl818x_csr { #define RTL818X_TX_CONF_R8185_D (5 << 25) #define RTL818X_TX_CONF_R8187vD (5 << 25) #define RTL818X_TX_CONF_R8187vD_B (6 << 25) +#define RTL818X_TX_CONF_RTL8187SE (6 << 25) #define RTL818X_TX_CONF_HWVER_MASK (7 << 25) #define RTL818X_TX_CONF_DISREQQSIZE (1 << 28) #define RTL818X_TX_CONF_PROBE_DTS (1 << 29) @@ -122,31 +202,67 @@ struct rtl818x_csr { u8 PGSELECT; u8 SECURITY; __le32 ANAPARAM2; - u8 reserved_10[12]; - __le16 BEACON_INTERVAL; - __le16 ATIM_WND; - __le16 BEACON_INTERVAL_TIME; - __le16 ATIMTR_INTERVAL; - u8 PHY_DELAY; - u8 CARRIER_SENSE_COUNTER; - u8 reserved_11[2]; - u8 PHY[4]; - __le16 RFPinsOutput; - __le16 RFPinsEnable; - __le16 RFPinsSelect; - __le16 RFPinsInput; - __le32 RF_PARA; - __le32 RF_TIMING; - u8 GP_ENABLE; - u8 GPIO0; - u8 GPIO1; - u8 reserved_12; - __le32 HSSI_PARA; - u8 reserved_13[4]; - u8 TX_AGC_CTL; -#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN_SHIFT (1 << 0) -#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL_SHIFT (1 << 1) -#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2) + u8 reserved_10[8]; + __le32 IMR; /* 0x6c - Interrupt mask reg for 8187se */ +#define IMR_TMGDOK ((1 << 30)) +#define IMR_DOT11HINT ((1 << 25)) /* 802.11h Measurement Interrupt */ +#define IMR_BCNDMAINT ((1 << 24)) /* Beacon DMA Interrupt */ +#define IMR_WAKEINT ((1 << 23)) /* Wake Up Interrupt */ +#define IMR_TXFOVW ((1 << 22)) /* Tx FIFO Overflow */ +#define IMR_TIMEOUT1 ((1 << 21)) /* Time Out Interrupt 1 */ +#define IMR_BCNINT ((1 << 20)) /* Beacon Time out */ +#define IMR_ATIMINT ((1 << 19)) /* ATIM Time Out */ +#define IMR_TBDER ((1 << 18)) /* Tx Beacon Descriptor Error */ +#define IMR_TBDOK ((1 << 17)) /* Tx Beacon Descriptor OK */ +#define IMR_THPDER ((1 << 16)) /* Tx High Priority Descriptor Error */ +#define IMR_THPDOK ((1 << 15)) /* Tx High Priority Descriptor OK */ +#define IMR_TVODER ((1 << 14)) /* Tx AC_VO Descriptor Error Int */ +#define IMR_TVODOK ((1 << 13)) /* Tx AC_VO Descriptor OK Interrupt */ +#define IMR_FOVW ((1 << 12)) /* Rx FIFO Overflow Interrupt */ +#define IMR_RDU ((1 << 11)) /* Rx Descriptor Unavailable */ +#define IMR_TVIDER ((1 << 10)) /* Tx AC_VI Descriptor Error */ +#define IMR_TVIDOK ((1 << 9)) /* Tx AC_VI Descriptor OK Interrupt */ +#define IMR_RER ((1 << 8)) /* Rx Error Interrupt */ +#define IMR_ROK ((1 << 7)) /* Receive OK Interrupt */ +#define IMR_TBEDER ((1 << 6)) /* Tx AC_BE Descriptor Error */ +#define IMR_TBEDOK ((1 << 5)) /* Tx AC_BE Descriptor OK */ +#define IMR_TBKDER ((1 << 4)) /* Tx AC_BK Descriptor Error */ +#define IMR_TBKDOK ((1 << 3)) /* Tx AC_BK Descriptor OK */ +#define IMR_RQOSOK ((1 << 2)) /* Rx QoS OK Interrupt */ +#define IMR_TIMEOUT2 ((1 << 1)) /* Time Out Interrupt 2 */ +#define IMR_TIMEOUT3 ((1 << 0)) /* Time Out Interrupt 3 */ + __le16 BEACON_INTERVAL; /* 0x70 */ + __le16 ATIM_WND; /* 0x72 */ + __le16 BEACON_INTERVAL_TIME; /* 0x74 */ + __le16 ATIMTR_INTERVAL; /* 0x76 */ + u8 PHY_DELAY; /* 0x78 */ + u8 CARRIER_SENSE_COUNTER; /* 0x79 */ + u8 reserved_11[2]; /* 0x7a */ + u8 PHY[4]; /* 0x7c */ + __le16 RFPinsOutput; /* 0x80 */ + __le16 RFPinsEnable; /* 0x82 */ + __le16 RFPinsSelect; /* 0x84 */ + __le16 RFPinsInput; /* 0x86 */ + __le32 RF_PARA; /* 0x88 */ + __le32 RF_TIMING; /* 0x8c */ + u8 GP_ENABLE; /* 0x90 */ + u8 GPIO0; /* 0x91 */ + u8 GPIO1; /* 0x92 */ + u8 TPPOLL_STOP; /* 0x93 - rtl8187se only */ +#define RTL818x_TPPOLL_STOP_BQ (1 << 7) +#define RTL818x_TPPOLL_STOP_VI (1 << 4) +#define RTL818x_TPPOLL_STOP_VO (1 << 5) +#define RTL818x_TPPOLL_STOP_BE (1 << 3) +#define RTL818x_TPPOLL_STOP_BK (1 << 2) +#define RTL818x_TPPOLL_STOP_MG (1 << 1) +#define RTL818x_TPPOLL_STOP_HI (1 << 6) + + __le32 HSSI_PARA; /* 0x94 */ + u8 reserved_13[4]; /* 0x98 */ + u8 TX_AGC_CTL; /* 0x9c */ +#define RTL818X_TX_AGC_CTL_PERPACKET_GAIN (1 << 0) +#define RTL818X_TX_AGC_CTL_PERPACKET_ANTSEL (1 << 1) +#define RTL818X_TX_AGC_CTL_FEEDBACK_ANT (1 << 2) u8 TX_GAIN_CCK; u8 TX_GAIN_OFDM; u8 TX_ANTENNA; @@ -158,8 +274,8 @@ struct rtl818x_csr { u8 SLOT; u8 reserved_16[5]; u8 CW_CONF; -#define RTL818X_CW_CONF_PERPACKET_CW_SHIFT (1 << 0) -#define RTL818X_CW_CONF_PERPACKET_RETRY_SHIFT (1 << 1) +#define RTL818X_CW_CONF_PERPACKET_CW (1 << 0) +#define RTL818X_CW_CONF_PERPACKET_RETRY (1 << 1) u8 CW_VAL; u8 RATE_FALLBACK; #define RTL818X_RATE_FALLBACK_ENABLE (1 << 7) @@ -167,7 +283,8 @@ struct rtl818x_csr { u8 reserved_17[24]; u8 CONFIG5; u8 TX_DMA_POLLING; - u8 reserved_18[2]; + u8 PHY_PR; + u8 reserved_18; __le16 CWR; u8 RETRY_CTR; u8 reserved_19[3]; @@ -179,20 +296,64 @@ struct rtl818x_csr { __le32 RDSAR; __le16 TID_AC_MAP; u8 reserved_20[4]; - u8 ANAPARAM3; - u8 reserved_21[5]; - __le16 FEMR; - u8 reserved_22[4]; - __le16 TALLY_CNT; - u8 TALLY_SEL; + union { + __le16 ANAPARAM3; /* 0xee */ + u8 ANAPARAM3A; /* for rtl8187 */ + }; + +#define AC_PARAM_TXOP_LIMIT_SHIFT 16 +#define AC_PARAM_ECW_MAX_SHIFT 12 +#define AC_PARAM_ECW_MIN_SHIFT 8 +#define AC_PARAM_AIFS_SHIFT 0 + + __le32 AC_VO_PARAM; /* 0xf0 */ + + union { /* 0xf4 */ + __le32 AC_VI_PARAM; + __le16 FEMR; + } __packed; + + union{ /* 0xf8 */ + __le32 AC_BE_PARAM; /* rtl8187se */ + struct{ + u8 reserved_21[2]; + __le16 TALLY_CNT; /* 0xfa */ + } __packed; + } __packed; + + union { + u8 TALLY_SEL; /* 0xfc */ + __le32 AC_BK_PARAM; + + } __packed; + } __packed; +/* These are addresses with NON-standard usage. + * They have offsets very far from this struct. + * I don't like to introduce a ton of "reserved".. + * They are for RTL8187SE + */ +#define REG_ADDR1(addr) ((u8 __iomem *)priv->map + (addr)) +#define REG_ADDR2(addr) ((__le16 __iomem *)priv->map + ((addr) >> 1)) +#define REG_ADDR4(addr) ((__le32 __iomem *)priv->map + ((addr) >> 2)) + +#define FEMR_SE REG_ADDR2(0x1D4) +#define ARFR REG_ADDR2(0x1E0) +#define RFSW_CTRL REG_ADDR2(0x272) +#define SW_3W_DB0 REG_ADDR2(0x274) +#define SW_3W_DB0_4 REG_ADDR4(0x274) +#define SW_3W_DB1 REG_ADDR2(0x278) +#define SW_3W_DB1_4 REG_ADDR4(0x278) +#define SW_3W_CMD1 REG_ADDR1(0x27D) +#define PI_DATA_REG REG_ADDR2(0x360) +#define SI_DATA_REG REG_ADDR2(0x362) + struct rtl818x_rf_ops { char *name; 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/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig index c2ffce7a907..bf3cf124e4e 100644 --- a/drivers/net/wireless/rtlwifi/Kconfig +++ b/drivers/net/wireless/rtlwifi/Kconfig @@ -5,7 +5,7 @@ menuconfig RTL_CARDS ---help--- This option will enable support for the Realtek mac80211-based wireless drivers. Drivers rtl8192ce, rtl8192cu, rtl8192se, rtl8192de, - rtl8723eu, and rtl8188eu share some common code. + rtl8723ae, rtl8723be, and rtl8188ae share some common code. if RTL_CARDS @@ -48,12 +48,27 @@ config RTL8723AE depends on PCI select RTLWIFI select RTLWIFI_PCI + select RTL8723_COMMON + select RTLBTCOEXIST ---help--- This is the driver for Realtek RTL8723AE 802.11n PCIe wireless network adapters. If you choose to build it as a module, it will be called rtl8723ae +config RTL8723BE + tristate "Realtek RTL8723BE PCIe Wireless Network Adapter" + depends on PCI + select RTLWIFI + select RTLWIFI_PCI + select RTL8723_COMMON + select RTLBTCOEXIST + ---help--- + This is the driver for Realtek RTL8723BE 802.11n PCIe + wireless network adapters. + + If you choose to build it as a module, it will be called rtl8723be + config RTL8188EE tristate "Realtek RTL8188EE Wireless Network Adapter" depends on PCI @@ -101,4 +116,14 @@ config RTL8192C_COMMON depends on RTL8192CE || RTL8192CU default y +config RTL8723_COMMON + tristate + depends on RTL8723AE || RTL8723BE + default y + +config RTLBTCOEXIST + tristate + depends on RTL8723AE || RTL8723BE + default y + endif diff --git a/drivers/net/wireless/rtlwifi/Makefile b/drivers/net/wireless/rtlwifi/Makefile index d56f023a4b9..bba36a06abc 100644 --- a/drivers/net/wireless/rtlwifi/Makefile +++ b/drivers/net/wireless/rtlwifi/Makefile @@ -24,6 +24,9 @@ obj-$(CONFIG_RTL8192CU) += rtl8192cu/ obj-$(CONFIG_RTL8192SE) += rtl8192se/ obj-$(CONFIG_RTL8192DE) += rtl8192de/ obj-$(CONFIG_RTL8723AE) += rtl8723ae/ +obj-$(CONFIG_RTL8723BE) += rtl8723be/ obj-$(CONFIG_RTL8188EE) += rtl8188ee/ +obj-$(CONFIG_RTLBTCOEXIST) += btcoexist/ +obj-$(CONFIG_RTL8723_COMMON) += rtl8723com/ ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 8bb4a9a01a1..93bb384eb00 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -37,6 +37,7 @@ #include <linux/ip.h> #include <linux/module.h> +#include <linux/udp.h> /* *NOTICE!!!: This file will be very big, we should @@ -352,7 +353,6 @@ static void _rtl_init_mac80211(struct ieee80211_hw *hw) /* TODO: Correct this value for our hw */ /* TODO: define these hard code value */ - hw->channel_change_time = 100; hw->max_listen_interval = 10; hw->max_rate_tries = 4; /* hw->max_rates = 1; */ @@ -1074,64 +1074,52 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) if (!ieee80211_is_data(fc)) return false; + ip = (const struct iphdr *)(skb->data + mac_hdr_len + + SNAP_SIZE + PROTOC_TYPE_SIZE); + ether_type = be16_to_cpup((__be16 *) + (skb->data + mac_hdr_len + SNAP_SIZE)); - ip = (struct iphdr *)((u8 *) skb->data + mac_hdr_len + - SNAP_SIZE + PROTOC_TYPE_SIZE); - ether_type = *(u16 *) ((u8 *) skb->data + mac_hdr_len + SNAP_SIZE); - /* ether_type = ntohs(ether_type); */ - - if (ETH_P_IP == ether_type) { - if (IPPROTO_UDP == ip->protocol) { - struct udphdr *udp = (struct udphdr *)((u8 *) ip + - (ip->ihl << 2)); - if (((((u8 *) udp)[1] == 68) && - (((u8 *) udp)[3] == 67)) || - ((((u8 *) udp)[1] == 67) && - (((u8 *) udp)[3] == 68))) { - /* - * 68 : UDP BOOTP client - * 67 : UDP BOOTP server - */ - RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), - DBG_DMESG, "dhcp %s !!\n", - is_tx ? "Tx" : "Rx"); - - if (is_tx) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv-> - works.lps_change_work); - ppsc->last_delaylps_stamp_jiffies = - jiffies; - } + switch (ether_type) { + case ETH_P_IP: { + struct udphdr *udp; + u16 src; + u16 dst; - return true; - } - } - } else if (ETH_P_ARP == ether_type) { - if (is_tx) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - ppsc->last_delaylps_stamp_jiffies = jiffies; - } + if (ip->protocol != IPPROTO_UDP) + return false; + udp = (struct udphdr *)((u8 *)ip + (ip->ihl << 2)); + src = be16_to_cpu(udp->source); + dst = be16_to_cpu(udp->dest); + + /* If this case involves port 68 (UDP BOOTP client) connecting + * with port 67 (UDP BOOTP server), then return true so that + * the lowest speed is used. + */ + if (!((src == 68 && dst == 67) || (src == 67 && dst == 68))) + return false; - return true; - } else if (ETH_P_PAE == ether_type) { + RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, + "dhcp %s !!\n", is_tx ? "Tx" : "Rx"); + break; + } + case ETH_P_ARP: + break; + case ETH_P_PAE: RT_TRACE(rtlpriv, (COMP_SEND | COMP_RECV), DBG_DMESG, "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx"); - - if (is_tx) { - rtlpriv->enter_ps = false; - schedule_work(&rtlpriv->works.lps_change_work); - ppsc->last_delaylps_stamp_jiffies = jiffies; - } - - return true; - } else if (ETH_P_IPV6 == ether_type) { - /* IPv6 */ - return true; + break; + case ETH_P_IPV6: + /* TODO: Is this right? */ + return false; + default: + return false; } - - return false; + if (is_tx) { + rtlpriv->enter_ps = false; + schedule_work(&rtlpriv->works.lps_change_work); + ppsc->last_delaylps_stamp_jiffies = jiffies; + } + return true; } EXPORT_SYMBOL_GPL(rtl_is_special_data); @@ -1304,7 +1292,7 @@ void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb) return; /* and only beacons from the associated BSSID, please */ - if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) return; rtlpriv->link_info.bcn_rx_inperiod++; @@ -1448,7 +1436,8 @@ void rtl_watchdog_wq_callback(void *data) /* if we can't recv beacon for 6s, we should * reconnect this AP */ - if (rtlpriv->link_info.roam_times >= 3) { + if ((rtlpriv->link_info.roam_times >= 3) && + !is_zero_ether_addr(rtlpriv->mac80211.bssid)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AP off, try to reconnect now\n"); rtlpriv->link_info.roam_times = 0; @@ -1613,6 +1602,35 @@ err_free: } EXPORT_SYMBOL(rtl_send_smps_action); +void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + enum io_type iotype; + + if (!is_hal_stop(rtlhal)) { + switch (operation) { + case SCAN_OPT_BACKUP: + iotype = IO_CMD_PAUSE_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + case SCAN_OPT_RESTORE: + iotype = IO_CMD_RESUME_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Unknown Scan Backup operation.\n"); + break; + } + } +} +EXPORT_SYMBOL(rtl_phy_scan_operation_backup); + /* There seem to be issues in mac80211 regarding when del ba frames can be * received. As a work around, we make a fake del_ba if we receive a ba_req; * however, rx_agg was opened to let mac80211 release some ba related @@ -1762,7 +1780,7 @@ void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len) return; /* and only beacons from the associated BSSID, please */ - if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) return; if (rtl_find_221_ie(hw, data, len)) diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index 0e5fe0902da..0cd07420777 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -114,7 +114,6 @@ void rtl_init_rfkill(struct ieee80211_hw *hw); void rtl_deinit_rfkill(struct ieee80211_hw *hw); void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb); -void rtl_watch_dog_timer_callback(unsigned long data); void rtl_deinit_deferred_work(struct ieee80211_hw *hw); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); @@ -153,5 +152,6 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw, u8 *sa, u8 *bssid, u16 tid); +void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation); #endif diff --git a/drivers/net/wireless/rtlwifi/btcoexist/Makefile b/drivers/net/wireless/rtlwifi/btcoexist/Makefile new file mode 100644 index 00000000000..47ceecfcb7d --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/Makefile @@ -0,0 +1,7 @@ +btcoexist-objs := halbtc8723b2ant.o \ + halbtcoutsrc.o \ + rtl_btc.o + +obj-$(CONFIG_RTLBTCOEXIST) += btcoexist.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h b/drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h new file mode 100644 index 00000000000..d76684eb24d --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/halbt_precomp.h @@ -0,0 +1,75 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Realtek 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * Larry Finger <Larry.Finger@lwfinger.net> + * + ******************************************************************************/ + +#ifndef __HALBT_PRECOMP_H__ +#define __HALBT_PRECOMP_H__ +/************************************************************* + * include files + *************************************************************/ +#include "../wifi.h" +#include "../efuse.h" +#include "../base.h" +#include "../regd.h" +#include "../cam.h" +#include "../ps.h" +#include "../pci.h" + +#include "halbtcoutsrc.h" + +#include "halbtc8723b2ant.h" + +#define BIT0 0x00000001 +#define BIT1 0x00000002 +#define BIT2 0x00000004 +#define BIT3 0x00000008 +#define BIT4 0x00000010 +#define BIT5 0x00000020 +#define BIT6 0x00000040 +#define BIT7 0x00000080 +#define BIT8 0x00000100 +#define BIT9 0x00000200 +#define BIT10 0x00000400 +#define BIT11 0x00000800 +#define BIT12 0x00001000 +#define BIT13 0x00002000 +#define BIT14 0x00004000 +#define BIT15 0x00008000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + +#endif /* __HALBT_PRECOMP_H__ */ diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c new file mode 100644 index 00000000000..d916ab9f3c3 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -0,0 +1,3698 @@ +/****************************************************************************** + * + * Copyright(c) 2012 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ +/*************************************************************** + * Description: + * + * This file is for RTL8723B Co-exist mechanism + * + * History + * 2012/11/15 Cosa first check in. + * + **************************************************************/ +/************************************************************** + * include files + **************************************************************/ +#include "halbt_precomp.h" +/************************************************************** + * Global variables, these are static variables + **************************************************************/ +static struct coex_dm_8723b_2ant glcoex_dm_8723b_2ant; +static struct coex_dm_8723b_2ant *coex_dm = &glcoex_dm_8723b_2ant; +static struct coex_sta_8723b_2ant glcoex_sta_8723b_2ant; +static struct coex_sta_8723b_2ant *coex_sta = &glcoex_sta_8723b_2ant; + +static const char *const glbt_info_src_8723b_2ant[] = { + "BT Info[wifi fw]", + "BT Info[bt rsp]", + "BT Info[bt auto report]", +}; + +static u32 glcoex_ver_date_8723b_2ant = 20130731; +static u32 glcoex_ver_8723b_2ant = 0x3b; + +/************************************************************** + * local function proto type if needed + **************************************************************/ +/************************************************************** + * local function start with btc8723b2ant_ + **************************************************************/ +static u8 btc8723b2ant_bt_rssi_state(u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) +{ + s32 bt_rssi = 0; + u8 bt_rssi_state = coex_sta->pre_bt_rssi_state; + + bt_rssi = coex_sta->bt_rssi; + + if (level_num == 2) { + if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || + (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + if (bt_rssi >= rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) { + bt_rssi_state = BTC_RSSI_STATE_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "switch to High\n"); + } else { + bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "stay at Low\n"); + } + } else { + if (bt_rssi < rssi_thresh) { + bt_rssi_state = BTC_RSSI_STATE_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "switch to Low\n"); + } else { + bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "stay at High\n"); + } + } + } else if (level_num == 3) { + if (rssi_thresh > rssi_thresh1) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi thresh error!!\n"); + return coex_sta->pre_bt_rssi_state; + } + + if ((coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_LOW) || + (coex_sta->pre_bt_rssi_state == BTC_RSSI_STATE_STAY_LOW)) { + if (bt_rssi >= rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) { + bt_rssi_state = BTC_RSSI_STATE_MEDIUM; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "switch to Medium\n"); + } else { + bt_rssi_state = BTC_RSSI_STATE_STAY_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "stay at Low\n"); + } + } else if ((coex_sta->pre_bt_rssi_state == + BTC_RSSI_STATE_MEDIUM) || + (coex_sta->pre_bt_rssi_state == + BTC_RSSI_STATE_STAY_MEDIUM)) { + if (bt_rssi >= rssi_thresh1 + + BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) { + bt_rssi_state = BTC_RSSI_STATE_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "switch to High\n"); + } else if (bt_rssi < rssi_thresh) { + bt_rssi_state = BTC_RSSI_STATE_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "switch to Low\n"); + } else { + bt_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "stay at Medium\n"); + } + } else { + if (bt_rssi < rssi_thresh1) { + bt_rssi_state = BTC_RSSI_STATE_MEDIUM; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "switch to Medium\n"); + } else { + bt_rssi_state = BTC_RSSI_STATE_STAY_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_RSSI_STATE, + "[BTCoex], BT Rssi state " + "stay at High\n"); + } + } + } + + coex_sta->pre_bt_rssi_state = bt_rssi_state; + + return bt_rssi_state; +} + +static u8 btc8723b2ant_wifi_rssi_state(struct btc_coexist *btcoexist, + u8 index, u8 level_num, + u8 rssi_thresh, u8 rssi_thresh1) +{ + s32 wifi_rssi = 0; + u8 wifi_rssi_state = coex_sta->pre_wifi_rssi_state[index]; + + btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); + + if (level_num == 2) { + if ((coex_sta->pre_wifi_rssi_state[index] == + BTC_RSSI_STATE_LOW) || + (coex_sta->pre_wifi_rssi_state[index] == + BTC_RSSI_STATE_STAY_LOW)) { + if (wifi_rssi >= rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) { + wifi_rssi_state = BTC_RSSI_STATE_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "switch to High\n"); + } else { + wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "stay at Low\n"); + } + } else { + if (wifi_rssi < rssi_thresh) { + wifi_rssi_state = BTC_RSSI_STATE_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "switch to Low\n"); + } else { + wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "stay at High\n"); + } + } + } else if (level_num == 3) { + if (rssi_thresh > rssi_thresh1) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI thresh error!!\n"); + return coex_sta->pre_wifi_rssi_state[index]; + } + + if ((coex_sta->pre_wifi_rssi_state[index] == + BTC_RSSI_STATE_LOW) || + (coex_sta->pre_wifi_rssi_state[index] == + BTC_RSSI_STATE_STAY_LOW)) { + if (wifi_rssi >= rssi_thresh + + BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) { + wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "switch to Medium\n"); + } else { + wifi_rssi_state = BTC_RSSI_STATE_STAY_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "stay at Low\n"); + } + } else if ((coex_sta->pre_wifi_rssi_state[index] == + BTC_RSSI_STATE_MEDIUM) || + (coex_sta->pre_wifi_rssi_state[index] == + BTC_RSSI_STATE_STAY_MEDIUM)) { + if (wifi_rssi >= rssi_thresh1 + + BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT) { + wifi_rssi_state = BTC_RSSI_STATE_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "switch to High\n"); + } else if (wifi_rssi < rssi_thresh) { + wifi_rssi_state = BTC_RSSI_STATE_LOW; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "switch to Low\n"); + } else { + wifi_rssi_state = BTC_RSSI_STATE_STAY_MEDIUM; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "stay at Medium\n"); + } + } else { + if (wifi_rssi < rssi_thresh1) { + wifi_rssi_state = BTC_RSSI_STATE_MEDIUM; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "switch to Medium\n"); + } else { + wifi_rssi_state = BTC_RSSI_STATE_STAY_HIGH; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_WIFI_RSSI_STATE, + "[BTCoex], wifi RSSI state " + "stay at High\n"); + } + } + } + + coex_sta->pre_wifi_rssi_state[index] = wifi_rssi_state; + + return wifi_rssi_state; +} + +static void btc8723b2ant_monitor_bt_ctr(struct btc_coexist *btcoexist) +{ + u32 reg_hp_txrx, reg_lp_txrx, u32tmp; + u32 reg_hp_tx = 0, reg_hp_rx = 0; + u32 reg_lp_tx = 0, reg_lp_rx = 0; + + reg_hp_txrx = 0x770; + reg_lp_txrx = 0x774; + + u32tmp = btcoexist->btc_read_4byte(btcoexist, reg_hp_txrx); + reg_hp_tx = u32tmp & MASKLWORD; + reg_hp_rx = (u32tmp & MASKHWORD) >> 16; + + u32tmp = btcoexist->btc_read_4byte(btcoexist, reg_lp_txrx); + reg_lp_tx = u32tmp & MASKLWORD; + reg_lp_rx = (u32tmp & MASKHWORD) >> 16; + + coex_sta->high_priority_tx = reg_hp_tx; + coex_sta->high_priority_rx = reg_hp_rx; + coex_sta->low_priority_tx = reg_lp_tx; + coex_sta->low_priority_rx = reg_lp_rx; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_MONITOR, + "[BTCoex], High Priority Tx/Rx(reg 0x%x)=0x%x(%d)/0x%x(%d)\n", + reg_hp_txrx, reg_hp_tx, reg_hp_tx, reg_hp_rx, reg_hp_rx); + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_BT_MONITOR, + "[BTCoex], Low Priority Tx/Rx(reg 0x%x)=0x%x(%d)/0x%x(%d)\n", + reg_lp_txrx, reg_lp_tx, reg_lp_tx, reg_lp_rx, reg_lp_rx); + + /* reset counter */ + btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); +} + +static bool btc8723b2ant_is_wifi_status_changed(struct btc_coexist *btcoexist) +{ + static bool pre_wifi_busy; + static bool pre_under_4way; + static bool pre_bt_hs_on; + bool wifi_busy = false, under_4way = false, bt_hs_on = false; + bool wifi_connected = false; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_4_WAY_PROGRESS, + &under_4way); + + if (wifi_connected) { + if (wifi_busy != pre_wifi_busy) { + pre_wifi_busy = wifi_busy; + return true; + } + + if (under_4way != pre_under_4way) { + pre_under_4way = under_4way; + return true; + } + + if (bt_hs_on != pre_bt_hs_on) { + pre_bt_hs_on = bt_hs_on; + return true; + } + } + + return false; +} + +static void btc8723b2ant_update_bt_link_info(struct btc_coexist *btcoexist) +{ + /*struct btc_stack_info *stack_info = &btcoexist->stack_info;*/ + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + bool bt_hs_on = false; + +#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 1) /* profile from bt patch */ + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + + bt_link_info->bt_link_exist = coex_sta->bt_link_exist; + bt_link_info->sco_exist = coex_sta->sco_exist; + bt_link_info->a2dp_exist = coex_sta->a2dp_exist; + bt_link_info->pan_exist = coex_sta->pan_exist; + bt_link_info->hid_exist = coex_sta->hid_exist; + + /* work around for HS mode. */ + if (bt_hs_on) { + bt_link_info->pan_exist = true; + bt_link_info->bt_link_exist = true; + } +#else /* profile from bt stack */ + bt_link_info->bt_link_exist = stack_info->bt_link_exist; + bt_link_info->sco_exist = stack_info->sco_exist; + bt_link_info->a2dp_exist = stack_info->a2dp_exist; + bt_link_info->pan_exist = stack_info->pan_exist; + bt_link_info->hid_exist = stack_info->hid_exist; + + /*for win-8 stack HID report error*/ + if (!stack_info->hid_exist) + stack_info->hid_exist = coex_sta->hid_exist; + /*sync BTInfo with BT firmware and stack*/ + /* when stack HID report error, here we use the info from bt fw.*/ + if (!stack_info->bt_link_exist) + stack_info->bt_link_exist = coex_sta->bt_link_exist; +#endif + /* check if Sco only */ + if (bt_link_info->sco_exist && !bt_link_info->a2dp_exist && + !bt_link_info->pan_exist && !bt_link_info->hid_exist) + bt_link_info->sco_only = true; + else + bt_link_info->sco_only = false; + + /* check if A2dp only */ + if (!bt_link_info->sco_exist && bt_link_info->a2dp_exist && + !bt_link_info->pan_exist && !bt_link_info->hid_exist) + bt_link_info->a2dp_only = true; + else + bt_link_info->a2dp_only = false; + + /* check if Pan only */ + if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist && + bt_link_info->pan_exist && !bt_link_info->hid_exist) + bt_link_info->pan_only = true; + else + bt_link_info->pan_only = false; + + /* check if Hid only */ + if (!bt_link_info->sco_exist && !bt_link_info->a2dp_exist && + !bt_link_info->pan_exist && bt_link_info->hid_exist) + bt_link_info->hid_only = true; + else + bt_link_info->hid_only = false; +} + +static u8 btc8723b2ant_action_algorithm(struct btc_coexist *btcoexist) +{ + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + bool bt_hs_on = false; + u8 algorithm = BT_8723B_2ANT_COEX_ALGO_UNDEFINED; + u8 num_of_diff_profile = 0; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + + if (!bt_link_info->bt_link_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], No BT link exists!!!\n"); + return algorithm; + } + + if (bt_link_info->sco_exist) + num_of_diff_profile++; + if (bt_link_info->hid_exist) + num_of_diff_profile++; + if (bt_link_info->pan_exist) + num_of_diff_profile++; + if (bt_link_info->a2dp_exist) + num_of_diff_profile++; + + if (num_of_diff_profile == 1) { + if (bt_link_info->sco_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO only\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_SCO; + } else { + if (bt_link_info->hid_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], HID only\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_HID; + } else if (bt_link_info->a2dp_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], A2DP only\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_A2DP; + } else if (bt_link_info->pan_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], PAN(HS) only\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANHS; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], PAN(EDR) only\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR; + } + } + } + } else if (num_of_diff_profile == 2) { + if (bt_link_info->sco_exist) { + if (bt_link_info->hid_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + HID\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } else if (bt_link_info->a2dp_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + A2DP ==> SCO\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } else if (bt_link_info->pan_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + PAN(HS)\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_SCO; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + PAN(EDR)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } + } + } else { + if (bt_link_info->hid_exist && + bt_link_info->a2dp_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], HID + A2DP\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_HID_A2DP; + } else if (bt_link_info->hid_exist && + bt_link_info->pan_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], HID + PAN(HS)\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_HID; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], HID + PAN(EDR)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } + } else if (bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], A2DP + PAN(HS)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_A2DP_PANHS; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex],A2DP + PAN(EDR)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_A2DP; + } + } + } + } else if (num_of_diff_profile == 3) { + if (bt_link_info->sco_exist) { + if (bt_link_info->hid_exist && + bt_link_info->a2dp_exist) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + HID + A2DP" + " ==> HID\n"); + algorithm = BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } else if (bt_link_info->hid_exist && + bt_link_info->pan_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + HID + " + "PAN(HS)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + HID + " + "PAN(EDR)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } + } else if (bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + A2DP + " + "PAN(HS)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + A2DP + " + "PAN(EDR) ==> HID\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } + } + } else { + if (bt_link_info->hid_exist && + bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], HID + A2DP + " + "PAN(HS)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_HID_A2DP; + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], HID + A2DP + " + "PAN(EDR)\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_HID_A2DP_PANEDR; + } + } + } + } else if (num_of_diff_profile >= 3) { + if (bt_link_info->sco_exist) { + if (bt_link_info->hid_exist && + bt_link_info->pan_exist && + bt_link_info->a2dp_exist) { + if (bt_hs_on) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Error!!! SCO + HID" + " + A2DP + PAN(HS)\n"); + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], SCO + HID + A2DP +" + " PAN(EDR)==>PAN(EDR)+HID\n"); + algorithm = + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID; + } + } + } + } + return algorithm; +} + +static bool btc8723b_need_dec_pwr(struct btc_coexist *btcoexist) +{ + bool ret = false; + bool bt_hs_on = false, wifi_connected = false; + s32 bt_hs_rssi = 0; + u8 bt_rssi_state; + + if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on)) + return false; + if (!btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected)) + return false; + if (!btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi)) + return false; + + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + if (wifi_connected) { + if (bt_hs_on) { + if (bt_hs_rssi > 37) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], Need to decrease bt " + "power for HS mode!!\n"); + ret = true; + } + } else { + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], Need to decrease bt " + "power for Wifi is connected!!\n"); + ret = true; + } + } + } + + return ret; +} + +static void btc8723b2ant_set_fw_dac_swing_level(struct btc_coexist *btcoexist, + u8 dac_swing_lvl) +{ + u8 h2c_parameter[1] = {0}; + + /* There are several type of dacswing + * 0x18/ 0x10/ 0xc/ 0x8/ 0x4/ 0x6 + */ + h2c_parameter[0] = dac_swing_lvl; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], Set Dac Swing Level=0x%x\n", dac_swing_lvl); + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], FW write 0x64=0x%x\n", h2c_parameter[0]); + + btcoexist->btc_fill_h2c(btcoexist, 0x64, 1, h2c_parameter); +} + +static void btc8723b2ant_set_fw_dec_bt_pwr(struct btc_coexist *btcoexist, + bool dec_bt_pwr) +{ + u8 h2c_parameter[1] = {0}; + + h2c_parameter[0] = 0; + + if (dec_bt_pwr) + h2c_parameter[0] |= BIT1; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], decrease Bt Power : %s, FW write 0x62=0x%x\n", + (dec_bt_pwr ? "Yes!!" : "No!!"), h2c_parameter[0]); + + btcoexist->btc_fill_h2c(btcoexist, 0x62, 1, h2c_parameter); +} + +static void btc8723b2ant_dec_bt_pwr(struct btc_coexist *btcoexist, + bool force_exec, bool dec_bt_pwr) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], %s Dec BT power = %s\n", + (force_exec ? "force to" : ""), (dec_bt_pwr ? "ON" : "OFF")); + coex_dm->cur_dec_bt_pwr = dec_bt_pwr; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], bPreDecBtPwr=%d, bCurDecBtPwr=%d\n", + coex_dm->pre_dec_bt_pwr, coex_dm->cur_dec_bt_pwr); + + if (coex_dm->pre_dec_bt_pwr == coex_dm->cur_dec_bt_pwr) + return; + } + btc8723b2ant_set_fw_dec_bt_pwr(btcoexist, coex_dm->cur_dec_bt_pwr); + + coex_dm->pre_dec_bt_pwr = coex_dm->cur_dec_bt_pwr; +} + +static void btc8723b2ant_fw_dac_swing_lvl(struct btc_coexist *btcoexist, + bool force_exec, u8 fw_dac_swing_lvl) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], %s set FW Dac Swing level = %d\n", + (force_exec ? "force to" : ""), fw_dac_swing_lvl); + coex_dm->cur_fw_dac_swing_lvl = fw_dac_swing_lvl; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], preFwDacSwingLvl=%d, " + "curFwDacSwingLvl=%d\n", + coex_dm->pre_fw_dac_swing_lvl, + coex_dm->cur_fw_dac_swing_lvl); + + if (coex_dm->pre_fw_dac_swing_lvl == + coex_dm->cur_fw_dac_swing_lvl) + return; + } + + btc8723b2ant_set_fw_dac_swing_level(btcoexist, + coex_dm->cur_fw_dac_swing_lvl); + coex_dm->pre_fw_dac_swing_lvl = coex_dm->cur_fw_dac_swing_lvl; +} + +static void btc8723b2ant_set_sw_rf_rx_lpf_corner(struct btc_coexist *btcoexist, + bool rx_rf_shrink_on) +{ + if (rx_rf_shrink_on) { + /* Shrink RF Rx LPF corner */ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Shrink RF Rx LPF corner!!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e, + 0xfffff, 0xffffc); + } else { + /* Resume RF Rx LPF corner */ + /* After initialized, we can use coex_dm->btRf0x1eBackup */ + if (btcoexist->initilized) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Resume RF Rx LPF corner!!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1e, + 0xfffff, + coex_dm->bt_rf0x1e_backup); + } + } +} + +static void btc8723b2ant_rf_shrink(struct btc_coexist *btcoexist, + bool force_exec, bool rx_rf_shrink_on) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW, + "[BTCoex], %s turn Rx RF Shrink = %s\n", + (force_exec ? "force to" : ""), (rx_rf_shrink_on ? + "ON" : "OFF")); + coex_dm->cur_rf_rx_lpf_shrink = rx_rf_shrink_on; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL, + "[BTCoex], bPreRfRxLpfShrink=%d, " + "bCurRfRxLpfShrink=%d\n", + coex_dm->pre_rf_rx_lpf_shrink, + coex_dm->cur_rf_rx_lpf_shrink); + + if (coex_dm->pre_rf_rx_lpf_shrink == + coex_dm->cur_rf_rx_lpf_shrink) + return; + } + btc8723b2ant_set_sw_rf_rx_lpf_corner(btcoexist, + coex_dm->cur_rf_rx_lpf_shrink); + + coex_dm->pre_rf_rx_lpf_shrink = coex_dm->cur_rf_rx_lpf_shrink; +} + +static void btc8723b_set_penalty_txrate(struct btc_coexist *btcoexist, + bool low_penalty_ra) +{ + u8 h2c_parameter[6] = {0}; + + h2c_parameter[0] = 0x6; /* opCode, 0x6= Retry_Penalty*/ + + if (low_penalty_ra) { + h2c_parameter[1] |= BIT0; + /*normal rate except MCS7/6/5, OFDM54/48/36*/ + h2c_parameter[2] = 0x00; + h2c_parameter[3] = 0xf7; /*MCS7 or OFDM54*/ + h2c_parameter[4] = 0xf8; /*MCS6 or OFDM48*/ + h2c_parameter[5] = 0xf9; /*MCS5 or OFDM36*/ + } + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], set WiFi Low-Penalty Retry: %s", + (low_penalty_ra ? "ON!!" : "OFF!!")); + + btcoexist->btc_fill_h2c(btcoexist, 0x69, 6, h2c_parameter); +} + +static void btc8723b2ant_low_penalty_ra(struct btc_coexist *btcoexist, + bool force_exec, bool low_penalty_ra) +{ + /*return; */ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW, + "[BTCoex], %s turn LowPenaltyRA = %s\n", + (force_exec ? "force to" : ""), (low_penalty_ra ? + "ON" : "OFF")); + coex_dm->cur_low_penalty_ra = low_penalty_ra; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL, + "[BTCoex], bPreLowPenaltyRa=%d, " + "bCurLowPenaltyRa=%d\n", + coex_dm->pre_low_penalty_ra, + coex_dm->cur_low_penalty_ra); + + if (coex_dm->pre_low_penalty_ra == coex_dm->cur_low_penalty_ra) + return; + } + btc8723b_set_penalty_txrate(btcoexist, coex_dm->cur_low_penalty_ra); + + coex_dm->pre_low_penalty_ra = coex_dm->cur_low_penalty_ra; +} + +static void btc8723b2ant_set_dac_swing_reg(struct btc_coexist *btcoexist, + u32 level) +{ + u8 val = (u8) level; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Write SwDacSwing = 0x%x\n", level); + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x883, 0x3e, val); +} + +static void btc8723b2ant_set_sw_fulltime_dac_swing(struct btc_coexist *btcoex, + bool sw_dac_swing_on, + u32 sw_dac_swing_lvl) +{ + if (sw_dac_swing_on) + btc8723b2ant_set_dac_swing_reg(btcoex, sw_dac_swing_lvl); + else + btc8723b2ant_set_dac_swing_reg(btcoex, 0x18); +} + + +static void btc8723b2ant_dac_swing(struct btc_coexist *btcoexist, + bool force_exec, bool dac_swing_on, + u32 dac_swing_lvl) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW, + "[BTCoex], %s turn DacSwing=%s, dac_swing_lvl=0x%x\n", + (force_exec ? "force to" : ""), + (dac_swing_on ? "ON" : "OFF"), dac_swing_lvl); + coex_dm->cur_dac_swing_on = dac_swing_on; + coex_dm->cur_dac_swing_lvl = dac_swing_lvl; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL, + "[BTCoex], bPreDacSwingOn=%d, preDacSwingLvl=0x%x," + " bCurDacSwingOn=%d, curDacSwingLvl=0x%x\n", + coex_dm->pre_dac_swing_on, coex_dm->pre_dac_swing_lvl, + coex_dm->cur_dac_swing_on, + coex_dm->cur_dac_swing_lvl); + + if ((coex_dm->pre_dac_swing_on == coex_dm->cur_dac_swing_on) && + (coex_dm->pre_dac_swing_lvl == coex_dm->cur_dac_swing_lvl)) + return; + } + mdelay(30); + btc8723b2ant_set_sw_fulltime_dac_swing(btcoexist, dac_swing_on, + dac_swing_lvl); + + coex_dm->pre_dac_swing_on = coex_dm->cur_dac_swing_on; + coex_dm->pre_dac_swing_lvl = coex_dm->cur_dac_swing_lvl; +} + +static void btc8723b2ant_set_agc_table(struct btc_coexist *btcoexist, + bool agc_table_en) +{ + u8 rssi_adjust_val = 0; + + /* BB AGC Gain Table */ + if (agc_table_en) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], BB Agc Table On!\n"); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6e1A0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6d1B0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6c1C0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6b1D0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x6a1E0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x691F0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0x68200001); + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], BB Agc Table Off!\n"); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xaa1A0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa91B0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa81C0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa71D0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa61E0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa51F0001); + btcoexist->btc_write_4byte(btcoexist, 0xc78, 0xa4200001); + } + + + /* RF Gain */ + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xef, 0xfffff, 0x02000); + if (agc_table_en) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Agc Table On!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, + 0xfffff, 0x38fff); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, + 0xfffff, 0x38ffe); + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Agc Table Off!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, + 0xfffff, 0x380c3); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x3b, + 0xfffff, 0x28ce6); + } + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xef, 0xfffff, 0x0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xed, 0xfffff, 0x1); + + if (agc_table_en) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Agc Table On!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, + 0xfffff, 0x38fff); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, + 0xfffff, 0x38ffe); + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], Agc Table Off!\n"); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, + 0xfffff, 0x380c3); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x40, + 0xfffff, 0x28ce6); + } + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0xed, 0xfffff, 0x0); + + /* set rssiAdjustVal for wifi module. */ + if (agc_table_en) + rssi_adjust_val = 8; + btcoexist->btc_set(btcoexist, BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON, + &rssi_adjust_val); +} + +static void btc8723b2ant_agc_table(struct btc_coexist *btcoexist, + bool force_exec, bool agc_table_en) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW, + "[BTCoex], %s %s Agc Table\n", + (force_exec ? "force to" : ""), + (agc_table_en ? "Enable" : "Disable")); + coex_dm->cur_agc_table_en = agc_table_en; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL, + "[BTCoex], bPreAgcTableEn=%d, bCurAgcTableEn=%d\n", + coex_dm->pre_agc_table_en, coex_dm->cur_agc_table_en); + + if (coex_dm->pre_agc_table_en == coex_dm->cur_agc_table_en) + return; + } + btc8723b2ant_set_agc_table(btcoexist, agc_table_en); + + coex_dm->pre_agc_table_en = coex_dm->cur_agc_table_en; +} + +static void btc8723b2ant_set_coex_table(struct btc_coexist *btcoexist, + u32 val0x6c0, u32 val0x6c4, + u32 val0x6c8, u8 val0x6cc) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], set coex table, set 0x6c0=0x%x\n", val0x6c0); + btcoexist->btc_write_4byte(btcoexist, 0x6c0, val0x6c0); + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], set coex table, set 0x6c4=0x%x\n", val0x6c4); + btcoexist->btc_write_4byte(btcoexist, 0x6c4, val0x6c4); + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], set coex table, set 0x6c8=0x%x\n", val0x6c8); + btcoexist->btc_write_4byte(btcoexist, 0x6c8, val0x6c8); + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_EXEC, + "[BTCoex], set coex table, set 0x6cc=0x%x\n", val0x6cc); + btcoexist->btc_write_1byte(btcoexist, 0x6cc, val0x6cc); +} + +static void btc8723b2ant_coex_table(struct btc_coexist *btcoexist, + bool force_exec, u32 val0x6c0, + u32 val0x6c4, u32 val0x6c8, + u8 val0x6cc) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW, + "[BTCoex], %s write Coex Table 0x6c0=0x%x," + " 0x6c4=0x%x, 0x6c8=0x%x, 0x6cc=0x%x\n", + (force_exec ? "force to" : ""), val0x6c0, + val0x6c4, val0x6c8, val0x6cc); + coex_dm->cur_val0x6c0 = val0x6c0; + coex_dm->cur_val0x6c4 = val0x6c4; + coex_dm->cur_val0x6c8 = val0x6c8; + coex_dm->cur_val0x6cc = val0x6cc; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL, + "[BTCoex], preVal0x6c0=0x%x, " + "preVal0x6c4=0x%x, preVal0x6c8=0x%x, " + "preVal0x6cc=0x%x !!\n", + coex_dm->pre_val0x6c0, coex_dm->pre_val0x6c4, + coex_dm->pre_val0x6c8, coex_dm->pre_val0x6cc); + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_SW_DETAIL, + "[BTCoex], curVal0x6c0=0x%x, " + "curVal0x6c4=0x%x, curVal0x6c8=0x%x, " + "curVal0x6cc=0x%x !!\n", + coex_dm->cur_val0x6c0, coex_dm->cur_val0x6c4, + coex_dm->cur_val0x6c8, coex_dm->cur_val0x6cc); + + if ((coex_dm->pre_val0x6c0 == coex_dm->cur_val0x6c0) && + (coex_dm->pre_val0x6c4 == coex_dm->cur_val0x6c4) && + (coex_dm->pre_val0x6c8 == coex_dm->cur_val0x6c8) && + (coex_dm->pre_val0x6cc == coex_dm->cur_val0x6cc)) + return; + } + btc8723b2ant_set_coex_table(btcoexist, val0x6c0, val0x6c4, + val0x6c8, val0x6cc); + + coex_dm->pre_val0x6c0 = coex_dm->cur_val0x6c0; + coex_dm->pre_val0x6c4 = coex_dm->cur_val0x6c4; + coex_dm->pre_val0x6c8 = coex_dm->cur_val0x6c8; + coex_dm->pre_val0x6cc = coex_dm->cur_val0x6cc; +} + +static void btc8723b_coex_tbl_type(struct btc_coexist *btcoexist, + bool force_exec, u8 type) +{ + switch (type) { + case 0: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x55555555, 0xffff, 0x3); + break; + case 1: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55555555, + 0x5afa5afa, 0xffff, 0x3); + break; + case 2: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x5a5a5a5a, + 0x5a5a5a5a, 0xffff, 0x3); + break; + case 3: + btc8723b2ant_coex_table(btcoexist, force_exec, 0xaaaaaaaa, + 0xaaaaaaaa, 0xffff, 0x3); + break; + case 4: + btc8723b2ant_coex_table(btcoexist, force_exec, 0xffffffff, + 0xffffffff, 0xffff, 0x3); + break; + case 5: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x5fff5fff, + 0x5fff5fff, 0xffff, 0x3); + break; + case 6: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5a5a5a5a, 0xffff, 0x3); + break; + case 7: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5afa5afa, 0xffff, 0x3); + break; + case 8: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x5aea5aea, + 0x5aea5aea, 0xffff, 0x3); + break; + case 9: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5aea5aea, 0xffff, 0x3); + break; + case 10: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5aff5aff, 0xffff, 0x3); + break; + case 11: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5a5f5a5f, 0xffff, 0x3); + break; + case 12: + btc8723b2ant_coex_table(btcoexist, force_exec, 0x55ff55ff, + 0x5f5f5f5f, 0xffff, 0x3); + break; + default: + break; + } +} + +static void btc8723b2ant_set_fw_ignore_wlan_act(struct btc_coexist *btcoexist, + bool enable) +{ + u8 h2c_parameter[1] = {0}; + + if (enable) + h2c_parameter[0] |= BIT0;/* function enable*/ + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], set FW for BT Ignore Wlan_Act, " + "FW write 0x63=0x%x\n", h2c_parameter[0]); + + btcoexist->btc_fill_h2c(btcoexist, 0x63, 1, h2c_parameter); +} + +static void btc8723b2ant_ignore_wlan_act(struct btc_coexist *btcoexist, + bool force_exec, bool enable) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], %s turn Ignore WlanAct %s\n", + (force_exec ? "force to" : ""), (enable ? "ON" : "OFF")); + coex_dm->cur_ignore_wlan_act = enable; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], bPreIgnoreWlanAct = %d, " + "bCurIgnoreWlanAct = %d!!\n", + coex_dm->pre_ignore_wlan_act, + coex_dm->cur_ignore_wlan_act); + + if (coex_dm->pre_ignore_wlan_act == + coex_dm->cur_ignore_wlan_act) + return; + } + btc8723b2ant_set_fw_ignore_wlan_act(btcoexist, enable); + + coex_dm->pre_ignore_wlan_act = coex_dm->cur_ignore_wlan_act; +} + +static void btc8723b2ant_set_fw_ps_tdma(struct btc_coexist *btcoexist, u8 byte1, + u8 byte2, u8 byte3, u8 byte4, u8 byte5) +{ + u8 h2c_parameter[5]; + + h2c_parameter[0] = byte1; + h2c_parameter[1] = byte2; + h2c_parameter[2] = byte3; + h2c_parameter[3] = byte4; + h2c_parameter[4] = byte5; + + coex_dm->ps_tdma_para[0] = byte1; + coex_dm->ps_tdma_para[1] = byte2; + coex_dm->ps_tdma_para[2] = byte3; + coex_dm->ps_tdma_para[3] = byte4; + coex_dm->ps_tdma_para[4] = byte5; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], FW write 0x60(5bytes)=0x%x%08x\n", + h2c_parameter[0], + h2c_parameter[1] << 24 | h2c_parameter[2] << 16 | + h2c_parameter[3] << 8 | h2c_parameter[4]); + + btcoexist->btc_fill_h2c(btcoexist, 0x60, 5, h2c_parameter); +} + +static void btc8723b2ant_sw_mechanism1(struct btc_coexist *btcoexist, + bool shrink_rx_lpf, bool low_penalty_ra, + bool limited_dig, bool bt_lna_constrain) +{ + btc8723b2ant_rf_shrink(btcoexist, NORMAL_EXEC, shrink_rx_lpf); + btc8723b2ant_low_penalty_ra(btcoexist, NORMAL_EXEC, low_penalty_ra); +} + +static void btc8723b2ant_sw_mechanism2(struct btc_coexist *btcoexist, + bool agc_table_shift, bool adc_backoff, + bool sw_dac_swing, u32 dac_swing_lvl) +{ + btc8723b2ant_agc_table(btcoexist, NORMAL_EXEC, agc_table_shift); + btc8723b2ant_dac_swing(btcoexist, NORMAL_EXEC, sw_dac_swing, + dac_swing_lvl); +} + +static void btc8723b2ant_ps_tdma(struct btc_coexist *btcoexist, bool force_exec, + bool turn_on, u8 type) +{ + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], %s turn %s PS TDMA, type=%d\n", + (force_exec ? "force to" : ""), + (turn_on ? "ON" : "OFF"), type); + coex_dm->cur_ps_tdma_on = turn_on; + coex_dm->cur_ps_tdma = type; + + if (!force_exec) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], bPrePsTdmaOn = %d, bCurPsTdmaOn = %d!!\n", + coex_dm->pre_ps_tdma_on, coex_dm->cur_ps_tdma_on); + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], prePsTdma = %d, curPsTdma = %d!!\n", + coex_dm->pre_ps_tdma, coex_dm->cur_ps_tdma); + + if ((coex_dm->pre_ps_tdma_on == coex_dm->cur_ps_tdma_on) && + (coex_dm->pre_ps_tdma == coex_dm->cur_ps_tdma)) + return; + } + if (turn_on) { + switch (type) { + case 1: + default: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0xe1, 0x90); + break; + case 2: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0xe1, 0x90); + break; + case 3: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0xf1, 0x90); + break; + case 4: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x10, + 0x03, 0xf1, 0x90); + break; + case 5: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0x60, 0x90); + break; + case 6: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0x60, 0x90); + break; + case 7: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1c, + 0x3, 0x70, 0x90); + break; + case 8: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x10, + 0x3, 0x70, 0x90); + break; + case 9: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0xe1, 0x90); + break; + case 10: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0xe1, 0x90); + break; + case 11: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, + 0xa, 0xe1, 0x90); + break; + case 12: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, + 0x5, 0xe1, 0x90); + break; + case 13: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0x60, 0x90); + break; + case 14: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x12, + 0x12, 0x60, 0x90); + break; + case 15: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0xa, + 0xa, 0x60, 0x90); + break; + case 16: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, + 0x5, 0x60, 0x90); + break; + case 17: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xa3, 0x2f, + 0x2f, 0x60, 0x90); + break; + case 18: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x5, + 0x5, 0xe1, 0x90); + break; + case 19: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, + 0x25, 0xe1, 0x90); + break; + case 20: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x25, + 0x25, 0x60, 0x90); + break; + case 21: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x15, + 0x03, 0x70, 0x90); + break; + case 71: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0xe3, 0x1a, + 0x1a, 0xe1, 0x90); + break; + } + } else { + /* disable PS tdma */ + switch (type) { + case 0: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x40, 0x0); + break; + case 1: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x48, 0x0); + break; + default: + btc8723b2ant_set_fw_ps_tdma(btcoexist, 0x0, 0x0, 0x0, + 0x40, 0x0); + break; + } + } + + /* update pre state */ + coex_dm->pre_ps_tdma_on = coex_dm->cur_ps_tdma_on; + coex_dm->pre_ps_tdma = coex_dm->cur_ps_tdma; +} + +static void btc8723b2ant_coex_alloff(struct btc_coexist *btcoexist) +{ + /* fw all off */ + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + /* sw all off */ + btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); + + /* hw all off */ + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); +} + +static void btc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) +{ + /* force to reset coex mechanism*/ + + btc8723b2ant_ps_tdma(btcoexist, FORCE_EXEC, false, 1); + btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); + btc8723b2ant_dec_bt_pwr(btcoexist, FORCE_EXEC, false); + + btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); +} + +static void btc8723b2ant_action_bt_inquiry(struct btc_coexist *btcoexist) +{ + bool wifi_connected = false; + bool low_pwr_disable = true; + + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + + if (wifi_connected) { + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + } else { + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + } + btc8723b2ant_fw_dac_swing_lvl(btcoexist, FORCE_EXEC, 6); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, 0x18); + + coex_dm->need_recover_0x948 = true; + coex_dm->backup_0x948 = btcoexist->btc_read_2byte(btcoexist, 0x948); + + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); +} + +static bool btc8723b2ant_is_common_action(struct btc_coexist *btcoexist) +{ + bool common = false, wifi_connected = false; + bool wifi_busy = false; + bool bt_hs_on = false, low_pwr_disable = false; + + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + + if (!wifi_connected) { + low_pwr_disable = false; + btcoexist->btc_set(btcoexist, BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Wifi non-connected idle!!\n"); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, + 0x0); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btc8723b2ant_sw_mechanism1(btcoexist, false, false, false, + false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, false, + 0x18); + + common = true; + } else { + if (BT_8723B_2ANT_BT_STATUS_NON_CONNECTED_IDLE == + coex_dm->bt_status) { + low_pwr_disable = false; + btcoexist->btc_set(btcoexist, + BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Wifi connected + " + "BT non connected-idle!!\n"); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, + 0xfffff, 0x0); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, + 0xb); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, + false); + + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + + common = true; + } else if (BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE == + coex_dm->bt_status) { + low_pwr_disable = true; + btcoexist->btc_set(btcoexist, + BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + + if (bt_hs_on) + return false; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Wifi connected + " + "BT connected-idle!!\n"); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, + 0xfffff, 0x0); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 0); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, + 0xb); + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, + false); + + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + + common = true; + } else { + low_pwr_disable = true; + btcoexist->btc_set(btcoexist, + BTC_SET_ACT_DISABLE_LOW_POWER, + &low_pwr_disable); + + if (wifi_busy) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Wifi Connected-Busy + " + "BT Busy!!\n"); + common = false; + } else { + if (bt_hs_on) + return false; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Wifi Connected-Idle + " + "BT Busy!!\n"); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, + 0x1, 0xfffff, 0x0); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, + 7); + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 21); + btc8723b2ant_fw_dac_swing_lvl(btcoexist, + NORMAL_EXEC, + 0xb); + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, + NORMAL_EXEC, + true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, + NORMAL_EXEC, + false); + btc8723b2ant_sw_mechanism1(btcoexist, false, + false, false, + false); + btc8723b2ant_sw_mechanism2(btcoexist, false, + false, false, + 0x18); + common = true; + } + } + } + + return common; +} + +static void set_tdma_int1(struct btc_coexist *btcoexist, bool tx_pause, + s32 result) +{ + /* Set PS TDMA for max interval == 1 */ + if (tx_pause) { + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], TxPause = 1\n"); + + if (coex_dm->cur_ps_tdma == 71) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 5); + coex_dm->tdma_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 5); + coex_dm->tdma_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->tdma_adj_type = 8; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 13); + coex_dm->tdma_adj_type = 13; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->tdma_adj_type = 16; + } + + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->tdma_adj_type = 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->tdma_adj_type = 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 5); + coex_dm->tdma_adj_type = 5; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 13); + coex_dm->tdma_adj_type = 13; + } + } + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], TxPause = 0\n"); + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 71); + coex_dm->tdma_adj_type = 71; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + coex_dm->tdma_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4); + coex_dm->tdma_adj_type = 4; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); + coex_dm->tdma_adj_type = 9; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); + coex_dm->tdma_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); + coex_dm->tdma_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12); + coex_dm->tdma_adj_type = 12; + } + + if (result == -1) { + if (coex_dm->cur_ps_tdma == 71) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 1); + coex_dm->tdma_adj_type = 1; + } else if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->tdma_adj_type = 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->tdma_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->tdma_adj_type = 12; + } + } else if (result == 1) { + int tmp = coex_dm->cur_ps_tdma; + switch (tmp) { + case 4: + case 3: + case 2: + case 12: + case 11: + case 10: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, tmp - 1); + coex_dm->tdma_adj_type = tmp - 1; + break; + case 1: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 71); + coex_dm->tdma_adj_type = 71; + break; + } + } + } +} + +static void set_tdma_int2(struct btc_coexist *btcoexist, bool tx_pause, + s32 result) +{ + /* Set PS TDMA for max interval == 2 */ + if (tx_pause) { + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], TxPause = 1\n"); + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 8); + coex_dm->tdma_adj_type = 8; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 16); + coex_dm->tdma_adj_type = 16; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->tdma_adj_type = 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->tdma_adj_type = 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } + } + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], TxPause = 0\n"); + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + coex_dm->tdma_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4); + coex_dm->tdma_adj_type = 4; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); + coex_dm->tdma_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 10); + coex_dm->tdma_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); + coex_dm->tdma_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12); + coex_dm->tdma_adj_type = 12; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->tdma_adj_type = 4; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->tdma_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->tdma_adj_type = 12; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 2); + coex_dm->tdma_adj_type = 2; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->tdma_adj_type = 10; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 10); + coex_dm->tdma_adj_type = 10; + } + } + } +} + +static void set_tdma_int3(struct btc_coexist *btcoexist, bool tx_pause, + s32 result) +{ + /* Set PS TDMA for max interval == 3 */ + if (tx_pause) { + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], TxPause = 1\n"); + if (coex_dm->cur_ps_tdma == 1) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 2) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 3) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 4) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 8); + coex_dm->tdma_adj_type = 8; + } else if (coex_dm->cur_ps_tdma == 9) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 10) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 11) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 12) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 16); + coex_dm->tdma_adj_type = 16; + } + if (result == -1) { + if (coex_dm->cur_ps_tdma == 5) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 8); + coex_dm->tdma_adj_type = 8; + } else if (coex_dm->cur_ps_tdma == 13) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 16); + coex_dm->tdma_adj_type = 16; + } + } else if (result == 1) { + if (coex_dm->cur_ps_tdma == 8) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 7) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 6) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else if (coex_dm->cur_ps_tdma == 16) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 15) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else if (coex_dm->cur_ps_tdma == 14) { + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } + } + } else { + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], TxPause = 0\n"); + switch (coex_dm->cur_ps_tdma) { + case 5: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 6: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 7: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 8: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 4); + coex_dm->tdma_adj_type = 4; + break; + case 13: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 14: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 15: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 16: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 12); + coex_dm->tdma_adj_type = 12; + break; + } + if (result == -1) { + switch (coex_dm->cur_ps_tdma) { + case 1: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 2: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 3: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 4); + coex_dm->tdma_adj_type = 4; + break; + case 9: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 10: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 11: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 12); + coex_dm->tdma_adj_type = 12; + break; + } + } else if (result == 1) { + switch (coex_dm->cur_ps_tdma) { + case 4: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 3: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 2: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + break; + case 12: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 11: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + break; + case 10: + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + } + } + } +} + +static void btc8723b2ant_tdma_duration_adjust(struct btc_coexist *btcoexist, + bool sco_hid, bool tx_pause, + u8 max_interval) +{ + static s32 up, dn, m, n, wait_count; + /*0: no change, +1: increase WiFi duration, -1: decrease WiFi duration*/ + s32 result; + u8 retry_count = 0; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW, + "[BTCoex], TdmaDurationAdjust()\n"); + + if (!coex_dm->auto_tdma_adjust) { + coex_dm->auto_tdma_adjust = true; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], first run TdmaDurationAdjust()!!\n"); + if (sco_hid) { + if (tx_pause) { + if (max_interval == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 13); + coex_dm->tdma_adj_type = 13; + } else if (max_interval == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 14); + coex_dm->tdma_adj_type = 14; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } else { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 15); + coex_dm->tdma_adj_type = 15; + } + } else { + if (max_interval == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 9); + coex_dm->tdma_adj_type = 9; + } else if (max_interval == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 10); + coex_dm->tdma_adj_type = 10; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + } else { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 11); + coex_dm->tdma_adj_type = 11; + } + } + } else { + if (tx_pause) { + if (max_interval == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 5); + coex_dm->tdma_adj_type = 5; + } else if (max_interval == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 6); + coex_dm->tdma_adj_type = 6; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } else { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 7); + coex_dm->tdma_adj_type = 7; + } + } else { + if (max_interval == 1) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 1); + coex_dm->tdma_adj_type = 1; + } else if (max_interval == 2) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 2); + coex_dm->tdma_adj_type = 2; + } else if (max_interval == 3) { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + } else { + btc8723b2ant_ps_tdma(btcoexist, + NORMAL_EXEC, + true, 3); + coex_dm->tdma_adj_type = 3; + } + } + } + + up = 0; + dn = 0; + m = 1; + n = 3; + result = 0; + wait_count = 0; + } else { + /*accquire the BT TRx retry count from BT_Info byte2*/ + retry_count = coex_sta->bt_retry_cnt; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], retry_count = %d\n", retry_count); + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], up=%d, dn=%d, m=%d, n=%d, wait_count=%d\n", + up, dn, m, n, wait_count); + result = 0; + wait_count++; + /* no retry in the last 2-second duration*/ + if (retry_count == 0) { + up++; + dn--; + + if (dn <= 0) + dn = 0; + + if (up >= n) { + wait_count = 0; + n = 3; + up = 0; + dn = 0; + result = 1; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], Increase wifi " + "duration!!\n"); + } /* <=3 retry in the last 2-second duration*/ + } else if (retry_count <= 3) { + up--; + dn++; + + if (up <= 0) + up = 0; + + if (dn == 2) { + if (wait_count <= 2) + m++; + else + m = 1; + + if (m >= 20) + m = 20; + + n = 3 * m; + up = 0; + dn = 0; + wait_count = 0; + result = -1; + BTC_PRINT(BTC_MSG_ALGORITHM, + ALGO_TRACE_FW_DETAIL, + "[BTCoex], Decrease wifi duration " + "for retry_counter<3!!\n"); + } + } else { + if (wait_count == 1) + m++; + else + m = 1; + + if (m >= 20) + m = 20; + + n = 3 * m; + up = 0; + dn = 0; + wait_count = 0; + result = -1; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], Decrease wifi duration " + "for retry_counter>3!!\n"); + } + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], max Interval = %d\n", max_interval); + if (max_interval == 1) + set_tdma_int1(btcoexist, tx_pause, result); + else if (max_interval == 2) + set_tdma_int2(btcoexist, tx_pause, result); + else if (max_interval == 3) + set_tdma_int3(btcoexist, tx_pause, result); + } + + /*if current PsTdma not match with the recorded one (when scan, dhcp..), + *then we have to adjust it back to the previous recorded one. + */ + if (coex_dm->cur_ps_tdma != coex_dm->tdma_adj_type) { + bool scan = false, link = false, roam = false; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], PsTdma type dismatch!!!, " + "curPsTdma=%d, recordPsTdma=%d\n", + coex_dm->cur_ps_tdma, coex_dm->tdma_adj_type); + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); + + if (!scan && !link && !roam) + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, + coex_dm->tdma_adj_type); + else + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_DETAIL, + "[BTCoex], roaming/link/scan is under" + " progress, will adjust next time!!!\n"); + } +} + +/* SCO only or SCO+PAN(HS) */ +static void btc8723b2ant_action_sco(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 4); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + + /*for SCO quality at 11b/g mode*/ + if (BTC_WIFI_BW_LEGACY == wifi_bw) + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 2); + else /*for SCO quality & wifi performance balance at 11n mode*/ + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 8); + + /*for voice quality */ + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 0); + + /* sw mechanism */ + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + true, 0x4); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + true, 0x4); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + true, 0x4); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + true, 0x4); + } + } +} + +static void btc8723b2ant_action_hid(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + + if (BTC_WIFI_BW_LEGACY == wifi_bw) /*/for HID at 11b/g mode*/ + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + else /*for HID quality & wifi performance balance at 11n mode*/ + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 9); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 9); + else + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 13); + + /* sw mechanism */ + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +/*A2DP only / PAN(EDR) only/ A2DP+PAN(HS)*/ +static void btc8723b2ant_action_a2dp(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) + btc8723b2ant_tdma_duration_adjust(btcoexist, false, + false, 1); + else + btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 1); + + /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +static void btc8723b2ant_action_a2dp_pan_hs(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + + btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 2); + + /* sw mechanism */ + btcoexist->btc_get(btcoexist, + BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +static void btc8723b2ant_action_pan_edr(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 10); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 1); + else + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, true, 5); + + /* sw mechanism */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +/*PAN(HS) only*/ +static void btc8723b2ant_action_pan_hs(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + + btc8723b2ant_ps_tdma(btcoexist, NORMAL_EXEC, false, 1); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +/*PAN(EDR)+A2DP*/ +static void btc8723b2ant_action_pan_edr_a2dp(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 12); + if (BTC_WIFI_BW_HT40 == wifi_bw) + btc8723b2ant_tdma_duration_adjust(btcoexist, false, + true, 3); + else + btc8723b2ant_tdma_duration_adjust(btcoexist, false, + false, 3); + } else { + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btc8723b2ant_tdma_duration_adjust(btcoexist, false, true, 3); + } + + /* sw mechanism */ + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, false, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +static void btc8723b2ant_action_pan_edr_hid(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (BTC_WIFI_BW_HT40 == wifi_bw) { + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, + 3); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 11); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, + 0xfffff, 0x780); + } else { + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, + 6); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, + 0xfffff, 0x0); + } + btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2); + } else { + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 11); + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, + 0x0); + btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2); + } + + /* sw mechanism */ + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +/* HID+A2DP+PAN(EDR) */ +static void btc8723b2ant_action_hid_a2dp_pan_edr(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + if (BTC_WIFI_BW_HT40 == wifi_bw) + btc8723b2ant_tdma_duration_adjust(btcoexist, true, + true, 2); + else + btc8723b2ant_tdma_duration_adjust(btcoexist, true, + false, 3); + } else { + btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 3); + } + + /* sw mechanism */ + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +static void btc8723b2ant_action_hid_a2dp(struct btc_coexist *btcoexist) +{ + u8 wifi_rssi_state, bt_rssi_state; + u32 wifi_bw; + + wifi_rssi_state = btc8723b2ant_wifi_rssi_state(btcoexist, + 0, 2, 15, 0); + bt_rssi_state = btc8723b2ant_bt_rssi_state(2, 35, 0); + + btcoexist->btc_set_rf_reg(btcoexist, BTC_RF_A, 0x1, 0xfffff, 0x0); + + btc8723b2ant_fw_dac_swing_lvl(btcoexist, NORMAL_EXEC, 6); + + if (btc8723b_need_dec_pwr(btcoexist)) + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, true); + else + btc8723b2ant_dec_bt_pwr(btcoexist, NORMAL_EXEC, false); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + + btc8723b_coex_tbl_type(btcoexist, NORMAL_EXEC, 7); + + if ((bt_rssi_state == BTC_RSSI_STATE_HIGH) || + (bt_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) + btc8723b2ant_tdma_duration_adjust(btcoexist, true, false, 2); + else + btc8723b2ant_tdma_duration_adjust(btcoexist, true, true, 2); + + /* sw mechanism */ + if (BTC_WIFI_BW_HT40 == wifi_bw) { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, true, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } else { + if ((wifi_rssi_state == BTC_RSSI_STATE_HIGH) || + (wifi_rssi_state == BTC_RSSI_STATE_STAY_HIGH)) { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, true, false, + false, 0x18); + } else { + btc8723b2ant_sw_mechanism1(btcoexist, false, true, + false, false); + btc8723b2ant_sw_mechanism2(btcoexist, false, false, + false, 0x18); + } + } +} + +static void btc8723b2ant_run_coexist_mechanism(struct btc_coexist *btcoexist) +{ + u8 algorithm = 0; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], RunCoexistMechanism()===>\n"); + + if (btcoexist->manual_control) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], RunCoexistMechanism(), " + "return for Manual CTRL <===\n"); + return; + } + + if (coex_sta->under_ips) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], wifi is under IPS !!!\n"); + return; + } + + algorithm = btc8723b2ant_action_algorithm(btcoexist); + if (coex_sta->c2h_bt_inquiry_page && + (BT_8723B_2ANT_COEX_ALGO_PANHS != algorithm)) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BT is under inquiry/page scan !!\n"); + btc8723b2ant_action_bt_inquiry(btcoexist); + return; + } else { + if (coex_dm->need_recover_0x948) { + coex_dm->need_recover_0x948 = false; + btcoexist->btc_write_2byte(btcoexist, 0x948, + coex_dm->backup_0x948); + } + } + + coex_dm->cur_algorithm = algorithm; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, "[BTCoex], Algorithm = %d\n", + coex_dm->cur_algorithm); + + if (btc8723b2ant_is_common_action(btcoexist)) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant common.\n"); + coex_dm->auto_tdma_adjust = false; + } else { + if (coex_dm->cur_algorithm != coex_dm->pre_algorithm) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], preAlgorithm=%d, " + "curAlgorithm=%d\n", coex_dm->pre_algorithm, + coex_dm->cur_algorithm); + coex_dm->auto_tdma_adjust = false; + } + switch (coex_dm->cur_algorithm) { + case BT_8723B_2ANT_COEX_ALGO_SCO: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, algorithm = SCO.\n"); + btc8723b2ant_action_sco(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_HID: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, algorithm = HID.\n"); + btc8723b2ant_action_hid(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_A2DP: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = A2DP.\n"); + btc8723b2ant_action_a2dp(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_A2DP_PANHS: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = A2DP+PAN(HS).\n"); + btc8723b2ant_action_a2dp_pan_hs(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_PANEDR: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = PAN(EDR).\n"); + btc8723b2ant_action_pan_edr(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_PANHS: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = HS mode.\n"); + btc8723b2ant_action_pan_hs(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_PANEDR_A2DP: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = PAN+A2DP.\n"); + btc8723b2ant_action_pan_edr_a2dp(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_PANEDR_HID: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = PAN(EDR)+HID.\n"); + btc8723b2ant_action_pan_edr_hid(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_HID_A2DP_PANEDR: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = HID+A2DP+PAN.\n"); + btc8723b2ant_action_hid_a2dp_pan_edr(btcoexist); + break; + case BT_8723B_2ANT_COEX_ALGO_HID_A2DP: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = HID+A2DP.\n"); + btc8723b2ant_action_hid_a2dp(btcoexist); + break; + default: + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], Action 2-Ant, " + "algorithm = coexist All Off!!\n"); + btc8723b2ant_coex_alloff(btcoexist); + break; + } + coex_dm->pre_algorithm = coex_dm->cur_algorithm; + } +} + + + +/********************************************************************* + * work around function start with wa_btc8723b2ant_ + *********************************************************************/ +/********************************************************************* + * extern function start with EXbtc8723b2ant_ + *********************************************************************/ +void ex_halbtc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist) +{ + struct btc_board_info *board_info = &btcoexist->board_info; + u32 u32tmp = 0, fw_ver; + u8 u8tmp = 0; + u8 h2c_parameter[2] = {0}; + + + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], 2Ant Init HW Config!!\n"); + + /* backup rf 0x1e value */ + coex_dm->bt_rf0x1e_backup = btcoexist->btc_get_rf_reg(btcoexist, + BTC_RF_A, 0x1e, + 0xfffff); + + /* 0x4c[23]=0, 0x4c[24]=1 Antenna control by WL/BT */ + u32tmp = btcoexist->btc_read_4byte(btcoexist, 0x4c); + u32tmp &= ~BIT23; + u32tmp |= BIT24; + btcoexist->btc_write_4byte(btcoexist, 0x4c, u32tmp); + + btcoexist->btc_write_1byte(btcoexist, 0x974, 0xff); + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x944, 0x3, 0x3); + btcoexist->btc_write_1byte(btcoexist, 0x930, 0x77); + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x67, 0x20, 0x1); + + /* Antenna switch control parameter */ + /* btcoexist->btc_write_4byte(btcoexist, 0x858, 0x55555555);*/ + + /*Force GNT_BT to low*/ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0); + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + + /* 0x790[5:0]=0x5 */ + u8tmp = btcoexist->btc_read_1byte(btcoexist, 0x790); + u8tmp &= 0xc0; + u8tmp |= 0x5; + btcoexist->btc_write_1byte(btcoexist, 0x790, u8tmp); + + + /*Antenna config */ + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver); + + /*ext switch for fw ver < 0xc */ + if (fw_ver < 0xc00) { + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, + 0x3, 0x1); + /*Main Ant to BT for IPS case 0x4c[23]=1*/ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, + 0x1); + + /*tell firmware "no antenna inverse"*/ + h2c_parameter[0] = 0; + h2c_parameter[1] = 1; /* ext switch type */ + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + h2c_parameter); + } else { + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, + 0x3, 0x2); + /*Aux Ant to BT for IPS case 0x4c[23]=1*/ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, + 0x0); + + /*tell firmware "antenna inverse"*/ + h2c_parameter[0] = 1; + h2c_parameter[1] = 1; /*ext switch type*/ + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + h2c_parameter); + } + } else { + /*ext switch always at s1 (if exist) */ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x92c, 0x3, 0x1); + /*Main Ant to BT for IPS case 0x4c[23]=1*/ + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x64, 0x1, 0x1); + + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { + /*tell firmware "no antenna inverse"*/ + h2c_parameter[0] = 0; + h2c_parameter[1] = 0; /*ext switch type*/ + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + h2c_parameter); + } else { + /*tell firmware "antenna inverse"*/ + h2c_parameter[0] = 1; + h2c_parameter[1] = 0; /*ext switch type*/ + btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, + h2c_parameter); + } + } + + /* PTA parameter */ + btc8723b_coex_tbl_type(btcoexist, FORCE_EXEC, 0); + + /* Enable counter statistics */ + /*0x76e[3] =1, WLAN_Act control by PTA*/ + btcoexist->btc_write_1byte(btcoexist, 0x76e, 0xc); + btcoexist->btc_write_1byte(btcoexist, 0x778, 0x3); + btcoexist->btc_write_1byte_bitmask(btcoexist, 0x40, 0x20, 0x1); +} + +void ex_halbtc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist) +{ + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], Coex Mechanism Init!!\n"); + btc8723b2ant_init_coex_dm(btcoexist); +} + +void ex_halbtc8723b2ant_display_coex_info(struct btc_coexist *btcoexist) +{ + struct btc_board_info *board_info = &btcoexist->board_info; + struct btc_stack_info *stack_info = &btcoexist->stack_info; + struct btc_bt_link_info *bt_link_info = &btcoexist->bt_link_info; + u8 *cli_buf = btcoexist->cli_buf; + u8 u8tmp[4], i, bt_info_ext, ps_tdma_case = 0; + u32 u32tmp[4]; + bool roam = false, scan = false; + bool link = false, wifi_under_5g = false; + bool bt_hs_on = false, wifi_busy = false; + s32 wifi_rssi = 0, bt_hs_rssi = 0; + u32 wifi_bw, wifi_traffic_dir, fa_ofdm, fa_cck; + u8 wifi_dot11_chnl, wifi_hs_chnl; + u32 fw_ver = 0, bt_patch_ver = 0; + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n ============[BT Coexist info]============"); + CL_PRINTF(cli_buf); + + if (btcoexist->manual_control) { + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n ==========[Under Manual Control]============"); + CL_PRINTF(cli_buf); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n =========================================="); + CL_PRINTF(cli_buf); + } + + if (!board_info->bt_exist) { + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n BT not exists !!!"); + CL_PRINTF(cli_buf); + return; + } + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d ", + "Ant PG number/ Ant mechanism:", + board_info->pg_ant_num, board_info->btdm_ant_num); + CL_PRINTF(cli_buf); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s / %d", + "BT stack/ hci ext ver", + ((stack_info->profile_notified) ? "Yes" : "No"), + stack_info->hci_version); + CL_PRINTF(cli_buf); + + btcoexist->btc_get(btcoexist, BTC_GET_U4_BT_PATCH_VER, &bt_patch_ver); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n %-35s = %d_%x/ 0x%x/ 0x%x(%d)", + "CoexVer/ FwVer/ PatchVer", + glcoex_ver_date_8723b_2ant, glcoex_ver_8723b_2ant, + fw_ver, bt_patch_ver, bt_patch_ver); + CL_PRINTF(cli_buf); + + btcoexist->btc_get(btcoexist, BTC_GET_BL_HS_OPERATION, &bt_hs_on); + btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_DOT11_CHNL, + &wifi_dot11_chnl); + btcoexist->btc_get(btcoexist, BTC_GET_U1_WIFI_HS_CHNL, &wifi_hs_chnl); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d(%d)", + "Dot11 channel / HsChnl(HsMode)", + wifi_dot11_chnl, wifi_hs_chnl, bt_hs_on); + CL_PRINTF(cli_buf); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %02x %02x %02x ", + "H2C Wifi inform bt chnl Info", coex_dm->wifi_chnl_info[0], + coex_dm->wifi_chnl_info[1], coex_dm->wifi_chnl_info[2]); + CL_PRINTF(cli_buf); + + btcoexist->btc_get(btcoexist, BTC_GET_S4_WIFI_RSSI, &wifi_rssi); + btcoexist->btc_get(btcoexist, BTC_GET_S4_HS_RSSI, &bt_hs_rssi); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d", + "Wifi rssi/ HS rssi", wifi_rssi, bt_hs_rssi); + CL_PRINTF(cli_buf); + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_SCAN, &scan); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_LINK, &link); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_ROAM, &roam); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d/ %d ", + "Wifi link/ roam/ scan", link, roam, scan); + CL_PRINTF(cli_buf); + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_UNDER_5G, &wifi_under_5g); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_BW, &wifi_bw); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_BUSY, &wifi_busy); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_TRAFFIC_DIRECTION, + &wifi_traffic_dir); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s / %s/ %s ", + "Wifi status", (wifi_under_5g ? "5G" : "2.4G"), + ((BTC_WIFI_BW_LEGACY == wifi_bw) ? "Legacy" : + (((BTC_WIFI_BW_HT40 == wifi_bw) ? "HT40" : "HT20"))), + ((!wifi_busy) ? "idle" : + ((BTC_WIFI_TRAFFIC_TX == wifi_traffic_dir) ? + "uplink" : "downlink"))); + CL_PRINTF(cli_buf); + + CL_PRINTF(cli_buf); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d / %d / %d / %d", + "SCO/HID/PAN/A2DP", + bt_link_info->sco_exist, bt_link_info->hid_exist, + bt_link_info->pan_exist, bt_link_info->a2dp_exist); + CL_PRINTF(cli_buf); + btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_BT_LINK_INFO); + + bt_info_ext = coex_sta->bt_info_ext; + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s", + "BT Info A2DP rate", + (bt_info_ext&BIT0) ? "Basic rate" : "EDR rate"); + CL_PRINTF(cli_buf); + + for (i = 0; i < BT_INFO_SRC_8723B_2ANT_MAX; i++) { + if (coex_sta->bt_info_c2h_cnt[i]) { + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n %-35s = %02x %02x %02x " + "%02x %02x %02x %02x(%d)", + glbt_info_src_8723b_2ant[i], + coex_sta->bt_info_c2h[i][0], + coex_sta->bt_info_c2h[i][1], + coex_sta->bt_info_c2h[i][2], + coex_sta->bt_info_c2h[i][3], + coex_sta->bt_info_c2h[i][4], + coex_sta->bt_info_c2h[i][5], + coex_sta->bt_info_c2h[i][6], + coex_sta->bt_info_c2h_cnt[i]); + CL_PRINTF(cli_buf); + } + } + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %s/%s", + "PS state, IPS/LPS", + ((coex_sta->under_ips ? "IPS ON" : "IPS OFF")), + ((coex_sta->under_lps ? "LPS ON" : "LPS OFF"))); + CL_PRINTF(cli_buf); + btcoexist->btc_disp_dbg_msg(btcoexist, BTC_DBG_DISP_FW_PWR_MODE_CMD); + + /* Sw mechanism */ + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n %-35s", "============[Sw mechanism]============"); + CL_PRINTF(cli_buf); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d/ %d ", + "SM1[ShRf/ LpRA/ LimDig]", coex_dm->cur_rf_rx_lpf_shrink, + coex_dm->cur_low_penalty_ra, coex_dm->limited_dig); + CL_PRINTF(cli_buf); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d/ %d(0x%x) ", + "SM2[AgcT/ AdcB/ SwDacSwing(lvl)]", + coex_dm->cur_agc_table_en, coex_dm->cur_adc_back_off, + coex_dm->cur_dac_swing_on, coex_dm->cur_dac_swing_lvl); + CL_PRINTF(cli_buf); + + /* Fw mechanism */ + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s", + "============[Fw mechanism]============"); + CL_PRINTF(cli_buf); + + ps_tdma_case = coex_dm->cur_ps_tdma; + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n %-35s = %02x %02x %02x %02x %02x case-%d (auto:%d)", + "PS TDMA", coex_dm->ps_tdma_para[0], + coex_dm->ps_tdma_para[1], coex_dm->ps_tdma_para[2], + coex_dm->ps_tdma_para[3], coex_dm->ps_tdma_para[4], + ps_tdma_case, coex_dm->auto_tdma_adjust); + CL_PRINTF(cli_buf); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d ", + "DecBtPwr/ IgnWlanAct", coex_dm->cur_dec_bt_pwr, + coex_dm->cur_ignore_wlan_act); + CL_PRINTF(cli_buf); + + /* Hw setting */ + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s", + "============[Hw setting]============"); + CL_PRINTF(cli_buf); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x", + "RF-A, 0x1e initVal", coex_dm->bt_rf0x1e_backup); + CL_PRINTF(cli_buf); + + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x778); + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x880); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x", + "0x778/0x880[29:25]", u8tmp[0], + (u32tmp[0]&0x3e000000) >> 25); + CL_PRINTF(cli_buf); + + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x948); + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x67); + u8tmp[1] = btcoexist->btc_read_1byte(btcoexist, 0x765); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x", + "0x948/ 0x67[5] / 0x765", + u32tmp[0], ((u8tmp[0]&0x20) >> 5), u8tmp[1]); + CL_PRINTF(cli_buf); + + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x92c); + u32tmp[1] = btcoexist->btc_read_4byte(btcoexist, 0x930); + u32tmp[2] = btcoexist->btc_read_4byte(btcoexist, 0x944); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x", + "0x92c[1:0]/ 0x930[7:0]/0x944[1:0]", + u32tmp[0]&0x3, u32tmp[1]&0xff, u32tmp[2]&0x3); + CL_PRINTF(cli_buf); + + + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x39); + u8tmp[1] = btcoexist->btc_read_1byte(btcoexist, 0x40); + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x4c); + u8tmp[2] = btcoexist->btc_read_1byte(btcoexist, 0x64); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n %-35s = 0x%x/ 0x%x/ 0x%x/ 0x%x", + "0x38[11]/0x40/0x4c[24:23]/0x64[0]", + ((u8tmp[0] & 0x8)>>3), u8tmp[1], + ((u32tmp[0]&0x01800000)>>23), u8tmp[2]&0x1); + CL_PRINTF(cli_buf); + + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x550); + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x522); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x", + "0x550(bcn ctrl)/0x522", u32tmp[0], u8tmp[0]); + CL_PRINTF(cli_buf); + + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0xc50); + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x49c); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x", + "0xc50(dig)/0x49c(null-drop)", u32tmp[0]&0xff, u8tmp[0]); + CL_PRINTF(cli_buf); + + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0xda0); + u32tmp[1] = btcoexist->btc_read_4byte(btcoexist, 0xda4); + u32tmp[2] = btcoexist->btc_read_4byte(btcoexist, 0xda8); + u32tmp[3] = btcoexist->btc_read_4byte(btcoexist, 0xcf0); + + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0xa5b); + u8tmp[1] = btcoexist->btc_read_1byte(btcoexist, 0xa5c); + + fa_ofdm = ((u32tmp[0]&0xffff0000) >> 16) + + ((u32tmp[1]&0xffff0000) >> 16) + + (u32tmp[1] & 0xffff) + + (u32tmp[2] & 0xffff) + + ((u32tmp[3]&0xffff0000) >> 16) + + (u32tmp[3] & 0xffff); + fa_cck = (u8tmp[0] << 8) + u8tmp[1]; + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = 0x%x/ 0x%x/ 0x%x", + "OFDM-CCA/OFDM-FA/CCK-FA", + u32tmp[0]&0xffff, fa_ofdm, fa_cck); + CL_PRINTF(cli_buf); + + u32tmp[0] = btcoexist->btc_read_4byte(btcoexist, 0x6c0); + u32tmp[1] = btcoexist->btc_read_4byte(btcoexist, 0x6c4); + u32tmp[2] = btcoexist->btc_read_4byte(btcoexist, 0x6c8); + u8tmp[0] = btcoexist->btc_read_1byte(btcoexist, 0x6cc); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, + "\r\n %-35s = 0x%x/ 0x%x/ 0x%x/ 0x%x", + "0x6c0/0x6c4/0x6c8/0x6cc(coexTable)", + u32tmp[0], u32tmp[1], u32tmp[2], u8tmp[0]); + CL_PRINTF(cli_buf); + + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d", + "0x770(high-pri rx/tx)", + coex_sta->high_priority_rx, coex_sta->high_priority_tx); + CL_PRINTF(cli_buf); + CL_SPRINTF(cli_buf, BT_TMP_BUF_SIZE, "\r\n %-35s = %d/ %d", + "0x774(low-pri rx/tx)", coex_sta->low_priority_rx, + coex_sta->low_priority_tx); + CL_PRINTF(cli_buf); +#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 1) + btc8723b2ant_monitor_bt_ctr(btcoexist); +#endif + btcoexist->btc_disp_dbg_msg(btcoexist, + BTC_DBG_DISP_COEX_STATISTICS); +} + + +void ex_halbtc8723b2ant_ips_notify(struct btc_coexist *btcoexist, u8 type) +{ + if (BTC_IPS_ENTER == type) { + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], IPS ENTER notify\n"); + coex_sta->under_ips = true; + btc8723b2ant_coex_alloff(btcoexist); + } else if (BTC_IPS_LEAVE == type) { + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], IPS LEAVE notify\n"); + coex_sta->under_ips = false; + } +} + +void ex_halbtc8723b2ant_lps_notify(struct btc_coexist *btcoexist, u8 type) +{ + if (BTC_LPS_ENABLE == type) { + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], LPS ENABLE notify\n"); + coex_sta->under_lps = true; + } else if (BTC_LPS_DISABLE == type) { + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], LPS DISABLE notify\n"); + coex_sta->under_lps = false; + } +} + +void ex_halbtc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type) +{ + if (BTC_SCAN_START == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], SCAN START notify\n"); + else if (BTC_SCAN_FINISH == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], SCAN FINISH notify\n"); +} + +void ex_halbtc8723b2ant_connect_notify(struct btc_coexist *btcoexist, u8 type) +{ + if (BTC_ASSOCIATE_START == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], CONNECT START notify\n"); + else if (BTC_ASSOCIATE_FINISH == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], CONNECT FINISH notify\n"); +} + +void btc8723b_med_stat_notify(struct btc_coexist *btcoexist, + u8 type) +{ + u8 h2c_parameter[3] = {0}; + u32 wifi_bw; + u8 wifi_central_chnl; + + if (BTC_MEDIA_CONNECT == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], MEDIA connect notify\n"); + else + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], MEDIA disconnect notify\n"); + + /* only 2.4G we need to inform bt the chnl mask */ + btcoexist->btc_get(btcoexist, + BTC_GET_U1_WIFI_CENTRAL_CHNL, &wifi_central_chnl); + if ((BTC_MEDIA_CONNECT == type) && + (wifi_central_chnl <= 14)) { + h2c_parameter[0] = 0x1; + h2c_parameter[1] = wifi_central_chnl; + btcoexist->btc_get(btcoexist, + BTC_GET_U4_WIFI_BW, &wifi_bw); + if (BTC_WIFI_BW_HT40 == wifi_bw) + h2c_parameter[2] = 0x30; + else + h2c_parameter[2] = 0x20; + } + + coex_dm->wifi_chnl_info[0] = h2c_parameter[0]; + coex_dm->wifi_chnl_info[1] = h2c_parameter[1]; + coex_dm->wifi_chnl_info[2] = h2c_parameter[2]; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE_FW_EXEC, + "[BTCoex], FW write 0x66=0x%x\n", + h2c_parameter[0] << 16 | h2c_parameter[1] << 8 | + h2c_parameter[2]); + + btcoexist->btc_fill_h2c(btcoexist, 0x66, 3, h2c_parameter); +} + +void ex_halbtc8723b2ant_special_packet_notify(struct btc_coexist *btcoexist, + u8 type) +{ + if (type == BTC_PACKET_DHCP) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], DHCP Packet notify\n"); +} + +void ex_halbtc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, + u8 *tmpbuf, u8 length) +{ + u8 bt_info = 0; + u8 i, rsp_source = 0; + bool bt_busy = false, limited_dig = false; + bool wifi_connected = false; + + coex_sta->c2h_bt_info_req_sent = false; + + rsp_source = tmpbuf[0]&0xf; + if (rsp_source >= BT_INFO_SRC_8723B_2ANT_MAX) + rsp_source = BT_INFO_SRC_8723B_2ANT_WIFI_FW; + coex_sta->bt_info_c2h_cnt[rsp_source]++; + + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex], Bt info[%d], length=%d, hex data=[", + rsp_source, length); + for (i = 0; i < length; i++) { + coex_sta->bt_info_c2h[rsp_source][i] = tmpbuf[i]; + if (i == 1) + bt_info = tmpbuf[i]; + if (i == length-1) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "0x%02x]\n", tmpbuf[i]); + else + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "0x%02x, ", tmpbuf[i]); + } + + if (btcoexist->manual_control) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BtInfoNotify(), " + "return for Manual CTRL<===\n"); + return; + } + + if (BT_INFO_SRC_8723B_2ANT_WIFI_FW != rsp_source) { + coex_sta->bt_retry_cnt = /* [3:0]*/ + coex_sta->bt_info_c2h[rsp_source][2] & 0xf; + + coex_sta->bt_rssi = + coex_sta->bt_info_c2h[rsp_source][3] * 2 + 10; + + coex_sta->bt_info_ext = + coex_sta->bt_info_c2h[rsp_source][4]; + + /* Here we need to resend some wifi info to BT + * because bt is reset and loss of the info. + */ + if ((coex_sta->bt_info_ext & BIT1)) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BT ext info bit1 check," + " send wifi BW&Chnl to BT!!\n"); + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_CONNECTED, + &wifi_connected); + if (wifi_connected) + btc8723b_med_stat_notify(btcoexist, + BTC_MEDIA_CONNECT); + else + btc8723b_med_stat_notify(btcoexist, + BTC_MEDIA_DISCONNECT); + } + + if ((coex_sta->bt_info_ext & BIT3)) { + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BT ext info bit3 check, " + "set BT NOT to ignore Wlan active!!\n"); + btc8723b2ant_ignore_wlan_act(btcoexist, FORCE_EXEC, + false); + } else { + /* BT already NOT ignore Wlan active, do nothing here.*/ + } +#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0) + if ((coex_sta->bt_info_ext & BIT4)) { + /* BT auto report already enabled, do nothing*/ + } else { + btc8723b2ant_bt_auto_report(btcoexist, FORCE_EXEC, + true); + } +#endif + } + + /* check BIT2 first ==> check if bt is under inquiry or page scan*/ + if (bt_info & BT_INFO_8723B_2ANT_B_INQ_PAGE) + coex_sta->c2h_bt_inquiry_page = true; + else + coex_sta->c2h_bt_inquiry_page = false; + + /* set link exist status*/ + if (!(bt_info & BT_INFO_8723B_2ANT_B_CONNECTION)) { + coex_sta->bt_link_exist = false; + coex_sta->pan_exist = false; + coex_sta->a2dp_exist = false; + coex_sta->hid_exist = false; + coex_sta->sco_exist = false; + } else { /* connection exists */ + coex_sta->bt_link_exist = true; + if (bt_info & BT_INFO_8723B_2ANT_B_FTP) + coex_sta->pan_exist = true; + else + coex_sta->pan_exist = false; + if (bt_info & BT_INFO_8723B_2ANT_B_A2DP) + coex_sta->a2dp_exist = true; + else + coex_sta->a2dp_exist = false; + if (bt_info & BT_INFO_8723B_2ANT_B_HID) + coex_sta->hid_exist = true; + else + coex_sta->hid_exist = false; + if (bt_info & BT_INFO_8723B_2ANT_B_SCO_ESCO) + coex_sta->sco_exist = true; + else + coex_sta->sco_exist = false; + } + + btc8723b2ant_update_bt_link_info(btcoexist); + + if (!(bt_info & BT_INFO_8723B_2ANT_B_CONNECTION)) { + coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_NON_CONNECTED_IDLE; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BtInfoNotify(), " + "BT Non-Connected idle!!!\n"); + /* connection exists but no busy */ + } else if (bt_info == BT_INFO_8723B_2ANT_B_CONNECTION) { + coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BtInfoNotify(), BT Connected-idle!!!\n"); + } else if ((bt_info & BT_INFO_8723B_2ANT_B_SCO_ESCO) || + (bt_info & BT_INFO_8723B_2ANT_B_SCO_BUSY)) { + coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_SCO_BUSY; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BtInfoNotify(), BT SCO busy!!!\n"); + } else if (bt_info & BT_INFO_8723B_2ANT_B_ACL_BUSY) { + coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_ACL_BUSY; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BtInfoNotify(), BT ACL busy!!!\n"); + } else { + coex_dm->bt_status = BT_8723B_2ANT_BT_STATUS_MAX; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], BtInfoNotify(), " + "BT Non-Defined state!!!\n"); + } + + if ((BT_8723B_2ANT_BT_STATUS_ACL_BUSY == coex_dm->bt_status) || + (BT_8723B_2ANT_BT_STATUS_SCO_BUSY == coex_dm->bt_status) || + (BT_8723B_2ANT_BT_STATUS_ACL_SCO_BUSY == coex_dm->bt_status)) { + bt_busy = true; + limited_dig = true; + } else { + bt_busy = false; + limited_dig = false; + } + + btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_TRAFFIC_BUSY, &bt_busy); + + coex_dm->limited_dig = limited_dig; + btcoexist->btc_set(btcoexist, BTC_SET_BL_BT_LIMITED_DIG, &limited_dig); + + btc8723b2ant_run_coexist_mechanism(btcoexist); +} + +void ex_halbtc8723b2ant_stack_operation_notify(struct btc_coexist *btcoexist, + u8 type) +{ + if (BTC_STACK_OP_INQ_PAGE_PAIR_START == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex],StackOP Inquiry/page/pair start notify\n"); + else if (BTC_STACK_OP_INQ_PAGE_PAIR_FINISH == type) + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, + "[BTCoex],StackOP Inquiry/page/pair finish notify\n"); +} + +void ex_halbtc8723b2ant_halt_notify(struct btc_coexist *btcoexist) +{ + BTC_PRINT(BTC_MSG_INTERFACE, INTF_NOTIFY, "[BTCoex], Halt notify\n"); + + btc8723b2ant_ignore_wlan_act(btcoexist, FORCE_EXEC, true); + btc8723b_med_stat_notify(btcoexist, BTC_MEDIA_DISCONNECT); +} + +void ex_halbtc8723b2ant_periodical(struct btc_coexist *btcoexist) +{ + struct btc_board_info *board_info = &btcoexist->board_info; + struct btc_stack_info *stack_info = &btcoexist->stack_info; + static u8 dis_ver_info_cnt; + u32 fw_ver = 0, bt_patch_ver = 0; + + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "[BTCoex], ==========================" + "Periodical===========================\n"); + + if (dis_ver_info_cnt <= 5) { + dis_ver_info_cnt += 1; + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], ****************************" + "************************************\n"); + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], Ant PG Num/ Ant Mech/ " + "Ant Pos = %d/ %d/ %d\n", board_info->pg_ant_num, + board_info->btdm_ant_num, board_info->btdm_ant_pos); + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], BT stack/ hci ext ver = %s / %d\n", + ((stack_info->profile_notified) ? "Yes" : "No"), + stack_info->hci_version); + btcoexist->btc_get(btcoexist, BTC_GET_U4_BT_PATCH_VER, + &bt_patch_ver); + btcoexist->btc_get(btcoexist, BTC_GET_U4_WIFI_FW_VER, &fw_ver); + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], CoexVer/ FwVer/ PatchVer = " + "%d_%x/ 0x%x/ 0x%x(%d)\n", + glcoex_ver_date_8723b_2ant, glcoex_ver_8723b_2ant, + fw_ver, bt_patch_ver, bt_patch_ver); + BTC_PRINT(BTC_MSG_INTERFACE, INTF_INIT, + "[BTCoex], *****************************" + "***********************************\n"); + } + +#if (BT_AUTO_REPORT_ONLY_8723B_2ANT == 0) + btc8723b2ant_query_bt_info(btcoexist); + btc8723b2ant_monitor_bt_ctr(btcoexist); + btc8723b2ant_monitor_bt_enable_disable(btcoexist); +#else + if (btc8723b2ant_is_wifi_status_changed(btcoexist) || + coex_dm->auto_tdma_adjust) + btc8723b2ant_run_coexist_mechanism(btcoexist); +#endif +} diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h new file mode 100644 index 00000000000..e0ad8e545f8 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtc8723b2ant.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * + * Copyright(c) 2012 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ +#ifndef _HAL8723B_2_ANT +#define _HAL8723B_2_ANT + +/************************************************************************ + * The following is for 8723B 2Ant BT Co-exist definition + ************************************************************************/ +#define BT_AUTO_REPORT_ONLY_8723B_2ANT 1 + +#define BT_INFO_8723B_2ANT_B_FTP BIT7 +#define BT_INFO_8723B_2ANT_B_A2DP BIT6 +#define BT_INFO_8723B_2ANT_B_HID BIT5 +#define BT_INFO_8723B_2ANT_B_SCO_BUSY BIT4 +#define BT_INFO_8723B_2ANT_B_ACL_BUSY BIT3 +#define BT_INFO_8723B_2ANT_B_INQ_PAGE BIT2 +#define BT_INFO_8723B_2ANT_B_SCO_ESCO BIT1 +#define BT_INFO_8723B_2ANT_B_CONNECTION BIT0 + +#define BTC_RSSI_COEX_THRESH_TOL_8723B_2ANT 2 + +enum BT_INFO_SRC_8723B_2ANT { + BT_INFO_SRC_8723B_2ANT_WIFI_FW = 0x0, + BT_INFO_SRC_8723B_2ANT_BT_RSP = 0x1, + BT_INFO_SRC_8723B_2ANT_BT_ACTIVE_SEND = 0x2, + BT_INFO_SRC_8723B_2ANT_MAX +}; + +enum BT_8723B_2ANT_BT_STATUS { + BT_8723B_2ANT_BT_STATUS_NON_CONNECTED_IDLE = 0x0, + BT_8723B_2ANT_BT_STATUS_CONNECTED_IDLE = 0x1, + BT_8723B_2ANT_BT_STATUS_INQ_PAGE = 0x2, + BT_8723B_2ANT_BT_STATUS_ACL_BUSY = 0x3, + BT_8723B_2ANT_BT_STATUS_SCO_BUSY = 0x4, + BT_8723B_2ANT_BT_STATUS_ACL_SCO_BUSY = 0x5, + BT_8723B_2ANT_BT_STATUS_MAX +}; + +enum BT_8723B_2ANT_COEX_ALGO { + BT_8723B_2ANT_COEX_ALGO_UNDEFINED = 0x0, + BT_8723B_2ANT_COEX_ALGO_SCO = 0x1, + BT_8723B_2ANT_COEX_ALGO_HID = 0x2, + BT_8723B_2ANT_COEX_ALGO_A2DP = 0x3, + BT_8723B_2ANT_COEX_ALGO_A2DP_PANHS = 0x4, + BT_8723B_2ANT_COEX_ALGO_PANEDR = 0x5, + BT_8723B_2ANT_COEX_ALGO_PANHS = 0x6, + BT_8723B_2ANT_COEX_ALGO_PANEDR_A2DP = 0x7, + BT_8723B_2ANT_COEX_ALGO_PANEDR_HID = 0x8, + BT_8723B_2ANT_COEX_ALGO_HID_A2DP_PANEDR = 0x9, + BT_8723B_2ANT_COEX_ALGO_HID_A2DP = 0xa, + BT_8723B_2ANT_COEX_ALGO_MAX = 0xb, +}; + +struct coex_dm_8723b_2ant { + /* fw mechanism */ + bool pre_dec_bt_pwr; + bool cur_dec_bt_pwr; + u8 pre_fw_dac_swing_lvl; + u8 cur_fw_dac_swing_lvl; + bool cur_ignore_wlan_act; + bool pre_ignore_wlan_act; + u8 pre_ps_tdma; + u8 cur_ps_tdma; + u8 ps_tdma_para[5]; + u8 tdma_adj_type; + bool reset_tdma_adjust; + bool auto_tdma_adjust; + bool pre_ps_tdma_on; + bool cur_ps_tdma_on; + bool pre_bt_auto_report; + bool cur_bt_auto_report; + + /* sw mechanism */ + bool pre_rf_rx_lpf_shrink; + bool cur_rf_rx_lpf_shrink; + u32 bt_rf0x1e_backup; + bool pre_low_penalty_ra; + bool cur_low_penalty_ra; + bool pre_dac_swing_on; + u32 pre_dac_swing_lvl; + bool cur_dac_swing_on; + u32 cur_dac_swing_lvl; + bool pre_adc_back_off; + bool cur_adc_back_off; + bool pre_agc_table_en; + bool cur_agc_table_en; + u32 pre_val0x6c0; + u32 cur_val0x6c0; + u32 pre_val0x6c4; + u32 cur_val0x6c4; + u32 pre_val0x6c8; + u32 cur_val0x6c8; + u8 pre_val0x6cc; + u8 cur_val0x6cc; + bool limited_dig; + + /* algorithm related */ + u8 pre_algorithm; + u8 cur_algorithm; + u8 bt_status; + u8 wifi_chnl_info[3]; + + bool need_recover_0x948; + u16 backup_0x948; +}; + +struct coex_sta_8723b_2ant { + bool bt_link_exist; + bool sco_exist; + bool a2dp_exist; + bool hid_exist; + bool pan_exist; + + bool under_lps; + bool under_ips; + u32 high_priority_tx; + u32 high_priority_rx; + u32 low_priority_tx; + u32 low_priority_rx; + u8 bt_rssi; + u8 pre_bt_rssi_state; + u8 pre_wifi_rssi_state[4]; + bool c2h_bt_info_req_sent; + u8 bt_info_c2h[BT_INFO_SRC_8723B_2ANT_MAX][10]; + u32 bt_info_c2h_cnt[BT_INFO_SRC_8723B_2ANT_MAX]; + bool c2h_bt_inquiry_page; + u8 bt_retry_cnt; + u8 bt_info_ext; +}; + +/********************************************************************* + * The following is interface which will notify coex module. + *********************************************************************/ +void ex_halbtc8723b2ant_init_hwconfig(struct btc_coexist *btcoexist); +void ex_halbtc8723b2ant_init_coex_dm(struct btc_coexist *btcoexist); +void ex_halbtc8723b2ant_ips_notify(struct btc_coexist *btcoexist, u8 type); +void ex_halbtc8723b2ant_lps_notify(struct btc_coexist *btcoexist, u8 type); +void ex_halbtc8723b2ant_scan_notify(struct btc_coexist *btcoexist, u8 type); +void ex_halbtc8723b2ant_connect_notify(struct btc_coexist *btcoexist, u8 type); +void btc8723b_med_stat_notify(struct btc_coexist *btcoexist, u8 type); +void ex_halbtc8723b2ant_special_packet_notify(struct btc_coexist *btcoexist, + u8 type); +void ex_halbtc8723b2ant_bt_info_notify(struct btc_coexist *btcoexist, + u8 *tmpbuf, u8 length); +void ex_halbtc8723b2ant_stack_operation_notify(struct btc_coexist *btcoexist, + u8 type); +void ex_halbtc8723b2ant_halt_notify(struct btc_coexist *btcoexist); +void ex_halbtc8723b2ant_periodical(struct btc_coexist *btcoexist); +void ex_halbtc8723b2ant_display_coex_info(struct btc_coexist *btcoexist); + +#endif diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c new file mode 100644 index 00000000000..33da3dfcfa4 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.c @@ -0,0 +1,1001 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2013 Realtek 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + ******************************************************************************/ + +#include "halbt_precomp.h" + +/*********************************************** + * Global variables + ***********************************************/ + +struct btc_coexist gl_bt_coexist; + +u32 btc_dbg_type[BTC_MSG_MAX]; +static u8 btc_dbg_buf[100]; + +/*************************************************** + * Debug related function + ***************************************************/ +static bool halbtc_is_bt_coexist_available(struct btc_coexist *btcoexist) +{ + if (!btcoexist->binded || NULL == btcoexist->adapter) + return false; + + return true; +} + +static bool halbtc_is_wifi_busy(struct rtl_priv *rtlpriv) +{ + if (rtlpriv->link_info.busytraffic) + return true; + else + return false; +} + +static void halbtc_dbg_init(void) +{ + u8 i; + + for (i = 0; i < BTC_MSG_MAX; i++) + btc_dbg_type[i] = 0; + + btc_dbg_type[BTC_MSG_INTERFACE] = +/* INTF_INIT | */ +/* INTF_NOTIFY | */ + 0; + + btc_dbg_type[BTC_MSG_ALGORITHM] = +/* ALGO_BT_RSSI_STATE | */ +/* ALGO_WIFI_RSSI_STATE | */ +/* ALGO_BT_MONITOR | */ +/* ALGO_TRACE | */ +/* ALGO_TRACE_FW | */ +/* ALGO_TRACE_FW_DETAIL | */ +/* ALGO_TRACE_FW_EXEC | */ +/* ALGO_TRACE_SW | */ +/* ALGO_TRACE_SW_DETAIL | */ +/* ALGO_TRACE_SW_EXEC | */ + 0; +} + +static bool halbtc_is_bt40(struct rtl_priv *adapter) +{ + struct rtl_priv *rtlpriv = adapter; + struct rtl_phy *rtlphy = &(rtlpriv->phy); + bool is_ht40 = true; + enum ht_channel_width bw = rtlphy->current_chan_bw; + + if (bw == HT_CHANNEL_WIDTH_20) + is_ht40 = false; + else if (bw == HT_CHANNEL_WIDTH_20_40) + is_ht40 = true; + + return is_ht40; +} + +static bool halbtc_legacy(struct rtl_priv *adapter) +{ + struct rtl_priv *rtlpriv = adapter; + struct rtl_mac *mac = rtl_mac(rtlpriv); + + bool is_legacy = false; + + if ((mac->mode == WIRELESS_MODE_B) || (mac->mode == WIRELESS_MODE_B)) + is_legacy = true; + + return is_legacy; +} + +bool halbtc_is_wifi_uplink(struct rtl_priv *adapter) +{ + struct rtl_priv *rtlpriv = adapter; + + if (rtlpriv->link_info.tx_busy_traffic) + return true; + else + return false; +} + +static u32 halbtc_get_wifi_bw(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = + (struct rtl_priv *)btcoexist->adapter; + u32 wifi_bw = BTC_WIFI_BW_HT20; + + if (halbtc_is_bt40(rtlpriv)) { + wifi_bw = BTC_WIFI_BW_HT40; + } else { + if (halbtc_legacy(rtlpriv)) + wifi_bw = BTC_WIFI_BW_LEGACY; + else + wifi_bw = BTC_WIFI_BW_HT20; + } + return wifi_bw; +} + +static u8 halbtc_get_wifi_central_chnl(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 chnl = 1; + + if (rtlphy->current_channel != 0) + chnl = rtlphy->current_channel; + BTC_PRINT(BTC_MSG_ALGORITHM, ALGO_TRACE, + "static halbtc_get_wifi_central_chnl:%d\n", chnl); + return chnl; +} + +static void halbtc_leave_lps(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv; + struct rtl_ps_ctl *ppsc; + bool ap_enable = false; + + rtlpriv = btcoexist->adapter; + ppsc = rtl_psc(rtlpriv); + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE, + &ap_enable); + + if (ap_enable) { + pr_info("halbtc_leave_lps()<--dont leave lps under AP mode\n"); + return; + } + + btcoexist->bt_info.bt_ctrl_lps = true; + btcoexist->bt_info.bt_lps_on = false; +} + +static void halbtc_enter_lps(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv; + struct rtl_ps_ctl *ppsc; + bool ap_enable = false; + + rtlpriv = btcoexist->adapter; + ppsc = rtl_psc(rtlpriv); + + btcoexist->btc_get(btcoexist, BTC_GET_BL_WIFI_AP_MODE_ENABLE, + &ap_enable); + + if (ap_enable) { + pr_info("halbtc_enter_lps()<--dont enter lps under AP mode\n"); + return; + } + + btcoexist->bt_info.bt_ctrl_lps = true; + btcoexist->bt_info.bt_lps_on = false; +} + +static void halbtc_normal_lps(struct btc_coexist *btcoexist) +{ + if (btcoexist->bt_info.bt_ctrl_lps) { + btcoexist->bt_info.bt_lps_on = false; + btcoexist->bt_info.bt_ctrl_lps = false; + } +} + +static void halbtc_leave_low_power(void) +{ +} + +static void halbtc_nomal_low_power(void) +{ +} + +static void halbtc_disable_low_power(void) +{ +} + +static void halbtc_aggregation_check(void) +{ +} + +static u32 halbtc_get_bt_patch_version(struct btc_coexist *btcoexist) +{ + return 0; +} + +static s32 halbtc_get_wifi_rssi(struct rtl_priv *adapter) +{ + struct rtl_priv *rtlpriv = adapter; + s32 undec_sm_pwdb = 0; + + if (rtlpriv->mac80211.link_state >= MAC80211_LINKED) + undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; + else /* associated entry pwdb */ + undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; + return undec_sm_pwdb; +} + +static bool halbtc_get(void *void_btcoexist, u8 get_type, void *out_buf) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)void_btcoexist; + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtlpriv); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + bool *bool_tmp = (bool *)out_buf; + int *s32_tmp = (int *)out_buf; + u32 *u32_tmp = (u32 *)out_buf; + u8 *u8_tmp = (u8 *)out_buf; + bool tmp = false; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return false; + + switch (get_type) { + case BTC_GET_BL_HS_OPERATION: + *bool_tmp = false; + break; + case BTC_GET_BL_HS_CONNECTING: + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_CONNECTED: + if (rtlpriv->mac80211.link_state >= MAC80211_LINKED) + tmp = true; + *bool_tmp = tmp; + break; + case BTC_GET_BL_WIFI_BUSY: + if (halbtc_is_wifi_busy(rtlpriv)) + *bool_tmp = true; + else + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_SCAN: + if (mac->act_scanning) + *bool_tmp = true; + else + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_LINK: + if (mac->link_state == MAC80211_LINKING) + *bool_tmp = true; + else + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_ROAM: /*TODO*/ + if (mac->link_state == MAC80211_LINKING) + *bool_tmp = true; + else + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_4_WAY_PROGRESS: /*TODO*/ + *bool_tmp = false; + + break; + case BTC_GET_BL_WIFI_UNDER_5G: + *bool_tmp = false; /*TODO*/ + + case BTC_GET_BL_WIFI_DHCP: /*TODO*/ + break; + case BTC_GET_BL_WIFI_SOFTAP_IDLE: + *bool_tmp = true; + break; + case BTC_GET_BL_WIFI_SOFTAP_LINKING: + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_IN_EARLY_SUSPEND: + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_AP_MODE_ENABLE: + *bool_tmp = false; + break; + case BTC_GET_BL_WIFI_ENABLE_ENCRYPTION: + if (NO_ENCRYPTION == rtlpriv->sec.pairwise_enc_algorithm) + *bool_tmp = false; + else + *bool_tmp = true; + break; + case BTC_GET_BL_WIFI_UNDER_B_MODE: + *bool_tmp = false; /*TODO*/ + break; + case BTC_GET_BL_EXT_SWITCH: + *bool_tmp = false; + break; + case BTC_GET_S4_WIFI_RSSI: + *s32_tmp = halbtc_get_wifi_rssi(rtlpriv); + break; + case BTC_GET_S4_HS_RSSI: /*TODO*/ + *s32_tmp = halbtc_get_wifi_rssi(rtlpriv); + break; + case BTC_GET_U4_WIFI_BW: + *u32_tmp = halbtc_get_wifi_bw(btcoexist); + break; + case BTC_GET_U4_WIFI_TRAFFIC_DIRECTION: + if (halbtc_is_wifi_uplink(rtlpriv)) + *u32_tmp = BTC_WIFI_TRAFFIC_TX; + else + *u32_tmp = BTC_WIFI_TRAFFIC_RX; + break; + case BTC_GET_U4_WIFI_FW_VER: + *u32_tmp = rtlhal->fw_version; + break; + case BTC_GET_U4_BT_PATCH_VER: + *u32_tmp = halbtc_get_bt_patch_version(btcoexist); + break; + case BTC_GET_U1_WIFI_DOT11_CHNL: + *u8_tmp = rtlphy->current_channel; + break; + case BTC_GET_U1_WIFI_CENTRAL_CHNL: + *u8_tmp = halbtc_get_wifi_central_chnl(btcoexist); + break; + case BTC_GET_U1_WIFI_HS_CHNL: + *u8_tmp = 1;/*BT_OperateChnl(rtlpriv);*/ + break; + case BTC_GET_U1_MAC_PHY_MODE: + *u8_tmp = BTC_MP_UNKNOWN; + break; + + /************* 1Ant **************/ + case BTC_GET_U1_LPS_MODE: + *u8_tmp = btcoexist->pwr_mode_val[0]; + break; + + default: + break; + } + + return true; +} + +static bool halbtc_set(void *void_btcoexist, u8 set_type, void *in_buf) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)void_btcoexist; + bool *bool_tmp = (bool *)in_buf; + u8 *u8_tmp = (u8 *)in_buf; + u32 *u32_tmp = (u32 *)in_buf; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return false; + + switch (set_type) { + /* set some bool type variables. */ + case BTC_SET_BL_BT_DISABLE: + btcoexist->bt_info.bt_disabled = *bool_tmp; + break; + case BTC_SET_BL_BT_TRAFFIC_BUSY: + btcoexist->bt_info.bt_busy = *bool_tmp; + break; + case BTC_SET_BL_BT_LIMITED_DIG: + btcoexist->bt_info.limited_dig = *bool_tmp; + break; + case BTC_SET_BL_FORCE_TO_ROAM: + btcoexist->bt_info.force_to_roam = *bool_tmp; + break; + case BTC_SET_BL_TO_REJ_AP_AGG_PKT: + btcoexist->bt_info.reject_agg_pkt = *bool_tmp; + break; + case BTC_SET_BL_BT_CTRL_AGG_SIZE: + btcoexist->bt_info.b_bt_ctrl_buf_size = *bool_tmp; + break; + case BTC_SET_BL_INC_SCAN_DEV_NUM: + btcoexist->bt_info.increase_scan_dev_num = *bool_tmp; + break; + /* set some u1Byte type variables. */ + case BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON: + btcoexist->bt_info.rssi_adjust_for_agc_table_on = *u8_tmp; + break; + case BTC_SET_U1_AGG_BUF_SIZE: + btcoexist->bt_info.agg_buf_size = *u8_tmp; + break; + /* the following are some action which will be triggered */ + case BTC_SET_ACT_GET_BT_RSSI: + /*BTHCI_SendGetBtRssiEvent(rtlpriv);*/ + break; + case BTC_SET_ACT_AGGREGATE_CTRL: + halbtc_aggregation_check(); + break; + + /* 1Ant */ + case BTC_SET_U1_RSSI_ADJ_VAL_FOR_1ANT_COEX_TYPE: + btcoexist->bt_info.rssi_adjust_for_1ant_coex_type = *u8_tmp; + break; + case BTC_SET_UI_SCAN_SIG_COMPENSATION: + /* rtlpriv->mlmepriv.scan_compensation = *u8_tmp; */ + break; + case BTC_SET_U1_1ANT_LPS: + btcoexist->bt_info.lps_1ant = *u8_tmp; + break; + case BTC_SET_U1_1ANT_RPWM: + btcoexist->bt_info.rpwm_1ant = *u8_tmp; + break; + /* the following are some action which will be triggered */ + case BTC_SET_ACT_LEAVE_LPS: + halbtc_leave_lps(btcoexist); + break; + case BTC_SET_ACT_ENTER_LPS: + halbtc_enter_lps(btcoexist); + break; + case BTC_SET_ACT_NORMAL_LPS: + halbtc_normal_lps(btcoexist); + break; + case BTC_SET_ACT_DISABLE_LOW_POWER: + halbtc_disable_low_power(); + break; + case BTC_SET_ACT_UPDATE_ra_mask: + btcoexist->bt_info.ra_mask = *u32_tmp; + break; + case BTC_SET_ACT_SEND_MIMO_PS: + break; + case BTC_SET_ACT_INC_FORCE_EXEC_PWR_CMD_CNT: + btcoexist->bt_info.force_exec_pwr_cmd_cnt++; + break; + case BTC_SET_ACT_CTRL_BT_INFO: /*wait for 8812/8821*/ + break; + case BTC_SET_ACT_CTRL_BT_COEX: + break; + default: + break; + } + + return true; +} + +static void halbtc_display_coex_statistics(struct btc_coexist *btcoexist) +{ +} + +static void halbtc_display_bt_link_info(struct btc_coexist *btcoexist) +{ +} + +static void halbtc_display_bt_fw_info(struct btc_coexist *btcoexist) +{ +} + +static void halbtc_display_fw_pwr_mode_cmd(struct btc_coexist *btcoexist) +{ +} + +/************************************************************ + * IO related function + ************************************************************/ +static u8 halbtc_read_1byte(void *bt_context, u32 reg_addr) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + return rtl_read_byte(rtlpriv, reg_addr); +} + +static u16 halbtc_read_2byte(void *bt_context, u32 reg_addr) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + return rtl_read_word(rtlpriv, reg_addr); +} + +static u32 halbtc_read_4byte(void *bt_context, u32 reg_addr) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + return rtl_read_dword(rtlpriv, reg_addr); +} + +static void halbtc_write_1byte(void *bt_context, u32 reg_addr, u8 data) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + rtl_write_byte(rtlpriv, reg_addr, data); +} + +static void halbtc_bitmask_write_1byte(void *bt_context, u32 reg_addr, + u32 bit_mask, u8 data) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + u8 original_value, bit_shift = 0; + u8 i; + + if (bit_mask != MASKDWORD) {/*if not "double word" write*/ + original_value = rtl_read_byte(rtlpriv, reg_addr); + for (i = 0; i <= 7; i++) { + if ((bit_mask>>i) & 0x1) + break; + } + bit_shift = i; + data = (original_value & (~bit_mask)) | + ((data << bit_shift) & bit_mask); + } + rtl_write_byte(rtlpriv, reg_addr, data); +} + +static void halbtc_write_2byte(void *bt_context, u32 reg_addr, u16 data) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + rtl_write_word(rtlpriv, reg_addr, data); +} + +static void halbtc_write_4byte(void *bt_context, u32 reg_addr, u32 data) +{ + struct btc_coexist *btcoexist = + (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + rtl_write_dword(rtlpriv, reg_addr, data); +} + +static void halbtc_set_bbreg(void *bt_context, u32 reg_addr, u32 bit_mask, + u32 data) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + rtl_set_bbreg(rtlpriv->mac80211.hw, reg_addr, bit_mask, data); +} + +static u32 halbtc_get_bbreg(void *bt_context, u32 reg_addr, u32 bit_mask) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + return rtl_get_bbreg(rtlpriv->mac80211.hw, reg_addr, bit_mask); +} + +static void halbtc_set_rfreg(void *bt_context, u8 rf_path, u32 reg_addr, + u32 bit_mask, u32 data) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + rtl_set_rfreg(rtlpriv->mac80211.hw, rf_path, reg_addr, bit_mask, data); +} + +static u32 halbtc_get_rfreg(void *bt_context, u8 rf_path, u32 reg_addr, + u32 bit_mask) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + return rtl_get_rfreg(rtlpriv->mac80211.hw, rf_path, reg_addr, bit_mask); +} + +static void halbtc_fill_h2c_cmd(void *bt_context, u8 element_id, + u32 cmd_len, u8 *cmd_buf) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + struct rtl_priv *rtlpriv = btcoexist->adapter; + + rtlpriv->cfg->ops->fill_h2c_cmd(rtlpriv->mac80211.hw, element_id, + cmd_len, cmd_buf); +} + +static void halbtc_display_dbg_msg(void *bt_context, u8 disp_type) +{ + struct btc_coexist *btcoexist = (struct btc_coexist *)bt_context; + switch (disp_type) { + case BTC_DBG_DISP_COEX_STATISTICS: + halbtc_display_coex_statistics(btcoexist); + break; + case BTC_DBG_DISP_BT_LINK_INFO: + halbtc_display_bt_link_info(btcoexist); + break; + case BTC_DBG_DISP_BT_FW_VER: + halbtc_display_bt_fw_info(btcoexist); + break; + case BTC_DBG_DISP_FW_PWR_MODE_CMD: + halbtc_display_fw_pwr_mode_cmd(btcoexist); + break; + default: + break; + } +} + +/***************************************************************** + * Extern functions called by other module + *****************************************************************/ +bool exhalbtc_initlize_variables(struct rtl_priv *adapter) +{ + struct btc_coexist *btcoexist = &gl_bt_coexist; + + btcoexist->statistics.cnt_bind++; + + halbtc_dbg_init(); + + if (btcoexist->binded) + return false; + else + btcoexist->binded = true; + + btcoexist->chip_interface = BTC_INTF_UNKNOWN; + + if (NULL == btcoexist->adapter) + btcoexist->adapter = adapter; + + btcoexist->stack_info.profile_notified = false; + + btcoexist->btc_read_1byte = halbtc_read_1byte; + btcoexist->btc_write_1byte = halbtc_write_1byte; + btcoexist->btc_write_1byte_bitmask = halbtc_bitmask_write_1byte; + btcoexist->btc_read_2byte = halbtc_read_2byte; + btcoexist->btc_write_2byte = halbtc_write_2byte; + btcoexist->btc_read_4byte = halbtc_read_4byte; + btcoexist->btc_write_4byte = halbtc_write_4byte; + + btcoexist->btc_set_bb_reg = halbtc_set_bbreg; + btcoexist->btc_get_bb_reg = halbtc_get_bbreg; + + btcoexist->btc_set_rf_reg = halbtc_set_rfreg; + btcoexist->btc_get_rf_reg = halbtc_get_rfreg; + + btcoexist->btc_fill_h2c = halbtc_fill_h2c_cmd; + btcoexist->btc_disp_dbg_msg = halbtc_display_dbg_msg; + + btcoexist->btc_get = halbtc_get; + btcoexist->btc_set = halbtc_set; + + btcoexist->cli_buf = &btc_dbg_buf[0]; + + btcoexist->bt_info.b_bt_ctrl_buf_size = false; + btcoexist->bt_info.agg_buf_size = 5; + + btcoexist->bt_info.increase_scan_dev_num = false; + return true; +} + +void exhalbtc_init_hw_config(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + btcoexist->statistics.cnt_init_hw_config++; + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_init_hwconfig(btcoexist); +} + +void exhalbtc_init_coex_dm(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + btcoexist->statistics.cnt_init_coex_dm++; + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_init_coex_dm(btcoexist); + + btcoexist->initilized = true; +} + +void exhalbtc_ips_notify(struct btc_coexist *btcoexist, u8 type) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 ips_type; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_ips_notify++; + if (btcoexist->manual_control) + return; + + if (ERFOFF == type) + ips_type = BTC_IPS_ENTER; + else + ips_type = BTC_IPS_LEAVE; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_ips_notify(btcoexist, ips_type); + + halbtc_nomal_low_power(); +} + +void exhalbtc_lps_notify(struct btc_coexist *btcoexist, u8 type) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 lps_type; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_lps_notify++; + if (btcoexist->manual_control) + return; + + if (EACTIVE == type) + lps_type = BTC_LPS_DISABLE; + else + lps_type = BTC_LPS_ENABLE; + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_lps_notify(btcoexist, lps_type); +} + +void exhalbtc_scan_notify(struct btc_coexist *btcoexist, u8 type) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 scan_type; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_scan_notify++; + if (btcoexist->manual_control) + return; + + if (type) + scan_type = BTC_SCAN_START; + else + scan_type = BTC_SCAN_FINISH; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_scan_notify(btcoexist, scan_type); + + halbtc_nomal_low_power(); +} + +void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 asso_type; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_connect_notify++; + if (btcoexist->manual_control) + return; + + if (action) + asso_type = BTC_ASSOCIATE_START; + else + asso_type = BTC_ASSOCIATE_FINISH; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_connect_notify(btcoexist, asso_type); +} + +void exhalbtc_mediastatus_notify(struct btc_coexist *btcoexist, + enum _RT_MEDIA_STATUS media_status) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 status; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_media_status_notify++; + if (btcoexist->manual_control) + return; + + if (RT_MEDIA_CONNECT == media_status) + status = BTC_MEDIA_CONNECT; + else + status = BTC_MEDIA_DISCONNECT; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + btc8723b_med_stat_notify(btcoexist, status); + + halbtc_nomal_low_power(); +} + +void exhalbtc_special_packet_notify(struct btc_coexist *btcoexist, u8 pkt_type) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 packet_type; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_special_packet_notify++; + if (btcoexist->manual_control) + return; + + packet_type = BTC_PACKET_DHCP; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_special_packet_notify(btcoexist, + packet_type); + + halbtc_nomal_low_power(); +} + +void exhalbtc_bt_info_notify(struct btc_coexist *btcoexist, + u8 *tmp_buf, u8 length) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_bt_info_notify++; + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_bt_info_notify(btcoexist, tmp_buf, length); +} + +void exhalbtc_stack_operation_notify(struct btc_coexist *btcoexist, u8 type) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 stack_op_type; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_stack_operation_notify++; + if (btcoexist->manual_control) + return; + + stack_op_type = BTC_STACK_OP_NONE; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_stack_operation_notify(btcoexist, + stack_op_type); + + halbtc_nomal_low_power(); +} + +void exhalbtc_halt_notify(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_halt_notify(btcoexist); +} + +void exhalbtc_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state) +{ + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; +} + +void exhalbtc_periodical(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_periodical++; + + halbtc_leave_low_power(); + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_periodical(btcoexist); + + halbtc_nomal_low_power(); +} + +void exhalbtc_dbg_control(struct btc_coexist *btcoexist, + u8 code, u8 len, u8 *data) +{ + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + btcoexist->statistics.cnt_dbg_ctrl++; +} + +void exhalbtc_stack_update_profile_info(void) +{ +} + +void exhalbtc_update_min_bt_rssi(char bt_rssi) +{ + struct btc_coexist *btcoexist = &gl_bt_coexist; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + btcoexist->stack_info.min_bt_rssi = bt_rssi; +} + +void exhalbtc_set_hci_version(u16 hci_version) +{ + struct btc_coexist *btcoexist = &gl_bt_coexist; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + btcoexist->stack_info.hci_version = hci_version; +} + +void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version) +{ + struct btc_coexist *btcoexist = &gl_bt_coexist; + + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + btcoexist->bt_info.bt_real_fw_ver = bt_patch_version; + btcoexist->bt_info.bt_hci_ver = bt_hci_version; +} + +void exhalbtc_set_bt_exist(bool bt_exist) +{ + gl_bt_coexist.board_info.bt_exist = bt_exist; +} + +void exhalbtc_set_chip_type(u8 chip_type) +{ + switch (chip_type) { + default: + case BT_2WIRE: + case BT_ISSC_3WIRE: + case BT_ACCEL: + case BT_RTL8756: + gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_UNDEF; + break; + case BT_CSR_BC4: + gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_CSR_BC4; + break; + case BT_CSR_BC8: + gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_CSR_BC8; + break; + case BT_RTL8723A: + gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_RTL8723A; + break; + case BT_RTL8821A: + gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_RTL8821; + break; + case BT_RTL8723B: + gl_bt_coexist.board_info.bt_chip_type = BTC_CHIP_RTL8723B; + break; + } +} + +void exhalbtc_set_ant_num(u8 type, u8 ant_num) +{ + if (BT_COEX_ANT_TYPE_PG == type) { + gl_bt_coexist.board_info.pg_ant_num = ant_num; + gl_bt_coexist.board_info.btdm_ant_num = ant_num; + } else if (BT_COEX_ANT_TYPE_ANTDIV == type) { + gl_bt_coexist.board_info.btdm_ant_num = ant_num; + } +} + +void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist) +{ + struct rtl_priv *rtlpriv = btcoexist->adapter; + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + if (!halbtc_is_bt_coexist_available(btcoexist)) + return; + + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8723BE) + ex_halbtc8723b2ant_display_coex_info(btcoexist); +} diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h new file mode 100644 index 00000000000..871fc3c6d55 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h @@ -0,0 +1,559 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ +#ifndef __HALBTC_OUT_SRC_H__ +#define __HALBTC_OUT_SRC_H__ + +#include "../wifi.h" + +#define NORMAL_EXEC false +#define FORCE_EXEC true + +#define BTC_RF_A RF90_PATH_A +#define BTC_RF_B RF90_PATH_B +#define BTC_RF_C RF90_PATH_C +#define BTC_RF_D RF90_PATH_D + +#define BTC_SMSP SINGLEMAC_SINGLEPHY +#define BTC_DMDP DUALMAC_DUALPHY +#define BTC_DMSP DUALMAC_SINGLEPHY +#define BTC_MP_UNKNOWN 0xff + +#define IN +#define OUT + +#define BT_TMP_BUF_SIZE 100 + +#define BT_COEX_ANT_TYPE_PG 0 +#define BT_COEX_ANT_TYPE_ANTDIV 1 +#define BT_COEX_ANT_TYPE_DETECTED 2 + +#define BTC_MIMO_PS_STATIC 0 +#define BTC_MIMO_PS_DYNAMIC 1 + +#define BTC_RATE_DISABLE 0 +#define BTC_RATE_ENABLE 1 + +#define BTC_ANT_PATH_WIFI 0 +#define BTC_ANT_PATH_BT 1 +#define BTC_ANT_PATH_PTA 2 + +enum btc_chip_interface { + BTC_INTF_UNKNOWN = 0, + BTC_INTF_PCI = 1, + BTC_INTF_USB = 2, + BTC_INTF_SDIO = 3, + BTC_INTF_GSPI = 4, + BTC_INTF_MAX +}; + +enum BTC_CHIP_TYPE { + BTC_CHIP_UNDEF = 0, + BTC_CHIP_CSR_BC4 = 1, + BTC_CHIP_CSR_BC8 = 2, + BTC_CHIP_RTL8723A = 3, + BTC_CHIP_RTL8821 = 4, + BTC_CHIP_RTL8723B = 5, + BTC_CHIP_MAX +}; + +enum BTC_MSG_TYPE { + BTC_MSG_INTERFACE = 0x0, + BTC_MSG_ALGORITHM = 0x1, + BTC_MSG_MAX +}; +extern u32 btc_dbg_type[]; + +/* following is for BTC_MSG_INTERFACE */ +#define INTF_INIT BIT0 +#define INTF_NOTIFY BIT2 + +/* following is for BTC_ALGORITHM */ +#define ALGO_BT_RSSI_STATE BIT0 +#define ALGO_WIFI_RSSI_STATE BIT1 +#define ALGO_BT_MONITOR BIT2 +#define ALGO_TRACE BIT3 +#define ALGO_TRACE_FW BIT4 +#define ALGO_TRACE_FW_DETAIL BIT5 +#define ALGO_TRACE_FW_EXEC BIT6 +#define ALGO_TRACE_SW BIT7 +#define ALGO_TRACE_SW_DETAIL BIT8 +#define ALGO_TRACE_SW_EXEC BIT9 + +#define BT_COEX_ANT_TYPE_PG 0 +#define BT_COEX_ANT_TYPE_ANTDIV 1 +#define BT_COEX_ANT_TYPE_DETECTED 2 +#define BTC_MIMO_PS_STATIC 0 +#define BTC_MIMO_PS_DYNAMIC 1 +#define BTC_RATE_DISABLE 0 +#define BTC_RATE_ENABLE 1 +#define BTC_ANT_PATH_WIFI 0 +#define BTC_ANT_PATH_BT 1 +#define BTC_ANT_PATH_PTA 2 + + +#define CL_SPRINTF snprintf +#define CL_PRINTF printk + +#define BTC_PRINT(dbgtype, dbgflag, printstr, ...) \ + do { \ + if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) {\ + printk(printstr, ##__VA_ARGS__); \ + } \ + } while (0) + +#define BTC_PRINT_F(dbgtype, dbgflag, printstr, ...) \ + do { \ + if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) {\ + pr_info("%s: ", __func__); \ + printk(printstr, ##__VA_ARGS__); \ + } \ + } while (0) + +#define BTC_PRINT_ADDR(dbgtype, dbgflag, printstr, _ptr) \ + do { \ + if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) { \ + int __i; \ + u8 *__ptr = (u8 *)_ptr; \ + printk printstr; \ + for (__i = 0; __i < 6; __i++) \ + printk("%02X%s", __ptr[__i], (__i == 5) ? \ + "" : "-"); \ + pr_info("\n"); \ + } \ + } while (0) + +#define BTC_PRINT_DATA(dbgtype, dbgflag, _titlestring, _hexdata, _hexdatalen) \ + do { \ + if (unlikely(btc_dbg_type[dbgtype] & dbgflag)) { \ + int __i; \ + u8 *__ptr = (u8 *)_hexdata; \ + printk(_titlestring); \ + for (__i = 0; __i < (int)_hexdatalen; __i++) { \ + printk("%02X%s", __ptr[__i], (((__i + 1) % 4) \ + == 0) ? " " : " ");\ + if (((__i + 1) % 16) == 0) \ + printk("\n"); \ + } \ + pr_debug("\n"); \ + } \ + } while (0) + +#define BTC_ANT_PATH_WIFI 0 +#define BTC_ANT_PATH_BT 1 +#define BTC_ANT_PATH_PTA 2 + +enum btc_power_save_type { + BTC_PS_WIFI_NATIVE = 0, + BTC_PS_LPS_ON = 1, + BTC_PS_LPS_OFF = 2, + BTC_PS_LPS_MAX +}; + +struct btc_board_info { + /* The following is some board information */ + u8 bt_chip_type; + u8 pg_ant_num; /* pg ant number */ + u8 btdm_ant_num; /* ant number for btdm */ + u8 btdm_ant_pos; + bool bt_exist; +}; + +enum btc_dbg_opcode { + BTC_DBG_SET_COEX_NORMAL = 0x0, + BTC_DBG_SET_COEX_WIFI_ONLY = 0x1, + BTC_DBG_SET_COEX_BT_ONLY = 0x2, + BTC_DBG_MAX +}; + +enum btc_rssi_state { + BTC_RSSI_STATE_HIGH = 0x0, + BTC_RSSI_STATE_MEDIUM = 0x1, + BTC_RSSI_STATE_LOW = 0x2, + BTC_RSSI_STATE_STAY_HIGH = 0x3, + BTC_RSSI_STATE_STAY_MEDIUM = 0x4, + BTC_RSSI_STATE_STAY_LOW = 0x5, + BTC_RSSI_MAX +}; + +enum btc_wifi_role { + BTC_ROLE_STATION = 0x0, + BTC_ROLE_AP = 0x1, + BTC_ROLE_IBSS = 0x2, + BTC_ROLE_HS_MODE = 0x3, + BTC_ROLE_MAX +}; + +enum btc_wifi_bw_mode { + BTC_WIFI_BW_LEGACY = 0x0, + BTC_WIFI_BW_HT20 = 0x1, + BTC_WIFI_BW_HT40 = 0x2, + BTC_WIFI_BW_MAX +}; + +enum btc_wifi_traffic_dir { + BTC_WIFI_TRAFFIC_TX = 0x0, + BTC_WIFI_TRAFFIC_RX = 0x1, + BTC_WIFI_TRAFFIC_MAX +}; + +enum btc_wifi_pnp { + BTC_WIFI_PNP_WAKE_UP = 0x0, + BTC_WIFI_PNP_SLEEP = 0x1, + BTC_WIFI_PNP_MAX +}; + + +enum btc_get_type { + /* type bool */ + BTC_GET_BL_HS_OPERATION, + BTC_GET_BL_HS_CONNECTING, + BTC_GET_BL_WIFI_CONNECTED, + BTC_GET_BL_WIFI_BUSY, + BTC_GET_BL_WIFI_SCAN, + BTC_GET_BL_WIFI_LINK, + BTC_GET_BL_WIFI_DHCP, + BTC_GET_BL_WIFI_SOFTAP_IDLE, + BTC_GET_BL_WIFI_SOFTAP_LINKING, + BTC_GET_BL_WIFI_IN_EARLY_SUSPEND, + BTC_GET_BL_WIFI_ROAM, + BTC_GET_BL_WIFI_4_WAY_PROGRESS, + BTC_GET_BL_WIFI_UNDER_5G, + BTC_GET_BL_WIFI_AP_MODE_ENABLE, + BTC_GET_BL_WIFI_ENABLE_ENCRYPTION, + BTC_GET_BL_WIFI_UNDER_B_MODE, + BTC_GET_BL_EXT_SWITCH, + + /* type s4Byte */ + BTC_GET_S4_WIFI_RSSI, + BTC_GET_S4_HS_RSSI, + + /* type u32 */ + BTC_GET_U4_WIFI_BW, + BTC_GET_U4_WIFI_TRAFFIC_DIRECTION, + BTC_GET_U4_WIFI_FW_VER, + BTC_GET_U4_BT_PATCH_VER, + + /* type u1Byte */ + BTC_GET_U1_WIFI_DOT11_CHNL, + BTC_GET_U1_WIFI_CENTRAL_CHNL, + BTC_GET_U1_WIFI_HS_CHNL, + BTC_GET_U1_MAC_PHY_MODE, + + /* for 1Ant */ + BTC_GET_U1_LPS_MODE, + BTC_GET_BL_BT_SCO_BUSY, + + /* for test mode */ + BTC_GET_DRIVER_TEST_CFG, + BTC_GET_MAX +}; + + +enum btc_set_type { + /* type bool */ + BTC_SET_BL_BT_DISABLE, + BTC_SET_BL_BT_TRAFFIC_BUSY, + BTC_SET_BL_BT_LIMITED_DIG, + BTC_SET_BL_FORCE_TO_ROAM, + BTC_SET_BL_TO_REJ_AP_AGG_PKT, + BTC_SET_BL_BT_CTRL_AGG_SIZE, + BTC_SET_BL_INC_SCAN_DEV_NUM, + + /* type u1Byte */ + BTC_SET_U1_RSSI_ADJ_VAL_FOR_AGC_TABLE_ON, + BTC_SET_U1_RSSI_ADJ_VAL_FOR_1ANT_COEX_TYPE, + BTC_SET_UI_SCAN_SIG_COMPENSATION, + BTC_SET_U1_AGG_BUF_SIZE, + + /* type trigger some action */ + BTC_SET_ACT_GET_BT_RSSI, + BTC_SET_ACT_AGGREGATE_CTRL, + + /********* for 1Ant **********/ + /* type bool */ + BTC_SET_BL_BT_SCO_BUSY, + /* type u1Byte */ + BTC_SET_U1_1ANT_LPS, + BTC_SET_U1_1ANT_RPWM, + /* type trigger some action */ + BTC_SET_ACT_LEAVE_LPS, + BTC_SET_ACT_ENTER_LPS, + BTC_SET_ACT_NORMAL_LPS, + BTC_SET_ACT_INC_FORCE_EXEC_PWR_CMD_CNT, + BTC_SET_ACT_DISABLE_LOW_POWER, + BTC_SET_ACT_UPDATE_ra_mask, + BTC_SET_ACT_SEND_MIMO_PS, + /* BT Coex related */ + BTC_SET_ACT_CTRL_BT_INFO, + BTC_SET_ACT_CTRL_BT_COEX, + /***************************/ + BTC_SET_MAX +}; + +enum btc_dbg_disp_type { + BTC_DBG_DISP_COEX_STATISTICS = 0x0, + BTC_DBG_DISP_BT_LINK_INFO = 0x1, + BTC_DBG_DISP_BT_FW_VER = 0x2, + BTC_DBG_DISP_FW_PWR_MODE_CMD = 0x3, + BTC_DBG_DISP_MAX +}; + +enum btc_notify_type_ips { + BTC_IPS_LEAVE = 0x0, + BTC_IPS_ENTER = 0x1, + BTC_IPS_MAX +}; + +enum btc_notify_type_lps { + BTC_LPS_DISABLE = 0x0, + BTC_LPS_ENABLE = 0x1, + BTC_LPS_MAX +}; + +enum btc_notify_type_scan { + BTC_SCAN_FINISH = 0x0, + BTC_SCAN_START = 0x1, + BTC_SCAN_MAX +}; + +enum btc_notify_type_associate { + BTC_ASSOCIATE_FINISH = 0x0, + BTC_ASSOCIATE_START = 0x1, + BTC_ASSOCIATE_MAX +}; + +enum btc_notify_type_media_status { + BTC_MEDIA_DISCONNECT = 0x0, + BTC_MEDIA_CONNECT = 0x1, + BTC_MEDIA_MAX +}; + +enum btc_notify_type_special_packet { + BTC_PACKET_UNKNOWN = 0x0, + BTC_PACKET_DHCP = 0x1, + BTC_PACKET_ARP = 0x2, + BTC_PACKET_EAPOL = 0x3, + BTC_PACKET_MAX +}; + +enum btc_notify_type_stack_operation { + BTC_STACK_OP_NONE = 0x0, + BTC_STACK_OP_INQ_PAGE_PAIR_START = 0x1, + BTC_STACK_OP_INQ_PAGE_PAIR_FINISH = 0x2, + BTC_STACK_OP_MAX +}; + + +typedef u8 (*bfp_btc_r1)(void *btc_context, u32 reg_addr); + +typedef u16 (*bfp_btc_r2)(void *btc_context, u32 reg_addr); + +typedef u32 (*bfp_btc_r4)(void *btc_context, u32 reg_addr); + +typedef void (*bfp_btc_w1)(void *btc_context, u32 reg_addr, u8 data); + +typedef void (*bfp_btc_w1_bit_mak)(void *btc_context, u32 reg_addr, + u32 bit_mask, u8 data1b); + +typedef void (*bfp_btc_w2)(void *btc_context, u32 reg_addr, u16 data); + +typedef void (*bfp_btc_w4)(void *btc_context, u32 reg_addr, u32 data); + +typedef void (*bfp_btc_wr_1byte_bit_mask)(void *btc_context, u32 reg_addr, + u8 bit_mask, u8 data); + +typedef void (*bfp_btc_set_bb_reg)(void *btc_context, u32 reg_addr, + u32 bit_mask, u32 data); + +typedef u32 (*bfp_btc_get_bb_reg)(void *btc_context, u32 reg_addr, + u32 bit_mask); + +typedef void (*bfp_btc_set_rf_reg)(void *btc_context, u8 rf_path, u32 reg_addr, + u32 bit_mask, u32 data); + +typedef u32 (*bfp_btc_get_rf_reg)(void *btc_context, u8 rf_path, + u32 reg_addr, u32 bit_mask); + +typedef void (*bfp_btc_fill_h2c)(void *btc_context, u8 element_id, + u32 cmd_len, u8 *cmd_buffer); + +typedef bool (*bfp_btc_get)(void *btcoexist, u8 get_type, void *out_buf); + +typedef bool (*bfp_btc_set)(void *btcoexist, u8 set_type, void *in_buf); + +typedef void (*bfp_btc_disp_dbg_msg)(void *btcoexist, u8 disp_type); + +struct btc_bt_info { + bool bt_disabled; + u8 rssi_adjust_for_agc_table_on; + u8 rssi_adjust_for_1ant_coex_type; + bool bt_busy; + u8 agg_buf_size; + bool limited_dig; + bool reject_agg_pkt; + bool b_bt_ctrl_buf_size; + bool increase_scan_dev_num; + u16 bt_hci_ver; + u16 bt_real_fw_ver; + u8 bt_fw_ver; + + /* the following is for 1Ant solution */ + bool bt_ctrl_lps; + bool bt_pwr_save_mode; + bool bt_lps_on; + bool force_to_roam; + u8 force_exec_pwr_cmd_cnt; + u8 lps_1ant; + u8 rpwm_1ant; + u32 ra_mask; +}; + +struct btc_stack_info { + bool profile_notified; + u16 hci_version; /* stack hci version */ + u8 num_of_link; + bool bt_link_exist; + bool sco_exist; + bool acl_exist; + bool a2dp_exist; + bool hid_exist; + u8 num_of_hid; + bool pan_exist; + bool unknown_acl_exist; + char min_bt_rssi; +}; + +struct btc_statistics { + u32 cnt_bind; + u32 cnt_init_hw_config; + u32 cnt_init_coex_dm; + u32 cnt_ips_notify; + u32 cnt_lps_notify; + u32 cnt_scan_notify; + u32 cnt_connect_notify; + u32 cnt_media_status_notify; + u32 cnt_special_packet_notify; + u32 cnt_bt_info_notify; + u32 cnt_periodical; + u32 cnt_stack_operation_notify; + u32 cnt_dbg_ctrl; +}; + +struct btc_bt_link_info { + bool bt_link_exist; + bool sco_exist; + bool sco_only; + bool a2dp_exist; + bool a2dp_only; + bool hid_exist; + bool hid_only; + bool pan_exist; + bool pan_only; +}; + +enum btc_antenna_pos { + BTC_ANTENNA_AT_MAIN_PORT = 0x1, + BTC_ANTENNA_AT_AUX_PORT = 0x2, +}; + +struct btc_coexist { + /* make sure only one adapter can bind the data context */ + bool binded; + /* default adapter */ + void *adapter; + struct btc_board_info board_info; + /* some bt info referenced by non-bt module */ + struct btc_bt_info bt_info; + struct btc_stack_info stack_info; + enum btc_chip_interface chip_interface; + struct btc_bt_link_info bt_link_info; + + bool initilized; + bool stop_coex_dm; + bool manual_control; + u8 *cli_buf; + struct btc_statistics statistics; + u8 pwr_mode_val[10]; + + /* function pointers - io related */ + bfp_btc_r1 btc_read_1byte; + bfp_btc_w1 btc_write_1byte; + bfp_btc_w1_bit_mak btc_write_1byte_bitmask; + bfp_btc_r2 btc_read_2byte; + bfp_btc_w2 btc_write_2byte; + bfp_btc_r4 btc_read_4byte; + bfp_btc_w4 btc_write_4byte; + + bfp_btc_set_bb_reg btc_set_bb_reg; + bfp_btc_get_bb_reg btc_get_bb_reg; + + + bfp_btc_set_rf_reg btc_set_rf_reg; + bfp_btc_get_rf_reg btc_get_rf_reg; + + bfp_btc_fill_h2c btc_fill_h2c; + + bfp_btc_disp_dbg_msg btc_disp_dbg_msg; + + bfp_btc_get btc_get; + bfp_btc_set btc_set; +}; + +bool halbtc_is_wifi_uplink(struct rtl_priv *adapter); + +extern struct btc_coexist gl_bt_coexist; + +bool exhalbtc_initlize_variables(struct rtl_priv *adapter); +void exhalbtc_init_hw_config(struct btc_coexist *btcoexist); +void exhalbtc_init_coex_dm(struct btc_coexist *btcoexist); +void exhalbtc_ips_notify(struct btc_coexist *btcoexist, u8 type); +void exhalbtc_lps_notify(struct btc_coexist *btcoexist, u8 type); +void exhalbtc_scan_notify(struct btc_coexist *btcoexist, u8 type); +void exhalbtc_connect_notify(struct btc_coexist *btcoexist, u8 action); +void exhalbtc_mediastatus_notify(struct btc_coexist *btcoexist, + enum _RT_MEDIA_STATUS media_status); +void exhalbtc_special_packet_notify(struct btc_coexist *btcoexist, u8 pkt_type); +void exhalbtc_bt_info_notify(struct btc_coexist *btcoexist, u8 *tmp_buf, + u8 length); +void exhalbtc_stack_operation_notify(struct btc_coexist *btcoexist, u8 type); +void exhalbtc_halt_notify(struct btc_coexist *btcoexist); +void exhalbtc_pnp_notify(struct btc_coexist *btcoexist, u8 pnp_state); +void exhalbtc_periodical(struct btc_coexist *btcoexist); +void exhalbtc_dbg_control(struct btc_coexist *btcoexist, u8 code, u8 len, + u8 *data); +void exhalbtc_stack_update_profile_info(void); +void exhalbtc_set_hci_version(u16 hci_version); +void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version); +void exhalbtc_update_min_bt_rssi(char bt_rssi); +void exhalbtc_set_bt_exist(bool bt_exist); +void exhalbtc_set_chip_type(u8 chip_type); +void exhalbtc_set_ant_num(u8 type, u8 ant_num); +void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist); +void exhalbtc_signal_compensation(struct btc_coexist *btcoexist, + u8 *rssi_wifi, u8 *rssi_bt); +void exhalbtc_lps_leave(struct btc_coexist *btcoexist); +void exhalbtc_low_wifi_traffic_notify(struct btc_coexist *btcoexist); + +#endif diff --git a/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c new file mode 100644 index 00000000000..0ab94fe4cbb --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.c @@ -0,0 +1,218 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2013 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "rtl_btc.h" +#include "halbt_precomp.h" + +#include <linux/vmalloc.h> +#include <linux/module.h> + +static struct rtl_btc_ops rtl_btc_operation = { + .btc_init_variables = rtl_btc_init_variables, + .btc_init_hal_vars = rtl_btc_init_hal_vars, + .btc_init_hw_config = rtl_btc_init_hw_config, + .btc_ips_notify = rtl_btc_ips_notify, + .btc_scan_notify = rtl_btc_scan_notify, + .btc_connect_notify = rtl_btc_connect_notify, + .btc_mediastatus_notify = rtl_btc_mediastatus_notify, + .btc_periodical = rtl_btc_periodical, + .btc_halt_notify = rtl_btc_halt_notify, + .btc_btinfo_notify = rtl_btc_btinfo_notify, + .btc_is_limited_dig = rtl_btc_is_limited_dig, + .btc_is_disable_edca_turbo = rtl_btc_is_disable_edca_turbo, + .btc_is_bt_disabled = rtl_btc_is_bt_disabled, +}; + +void rtl_btc_init_variables(struct rtl_priv *rtlpriv) +{ + exhalbtc_initlize_variables(rtlpriv); +} + +void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv) +{ + u8 ant_num; + u8 bt_exist; + u8 bt_type; + + ant_num = rtl_get_hwpg_ant_num(rtlpriv); + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "%s, antNum is %d\n", __func__, ant_num); + + bt_exist = rtl_get_hwpg_bt_exist(rtlpriv); + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "%s, bt_exist is %d\n", __func__, bt_exist); + exhalbtc_set_bt_exist(bt_exist); + + bt_type = rtl_get_hwpg_bt_type(rtlpriv); + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%s, bt_type is %d\n", + __func__, bt_type); + exhalbtc_set_chip_type(bt_type); + + exhalbtc_set_ant_num(BT_COEX_ANT_TYPE_PG, ant_num); +} + +void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv) +{ + exhalbtc_init_hw_config(&gl_bt_coexist); + exhalbtc_init_coex_dm(&gl_bt_coexist); +} + +void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type) +{ + exhalbtc_ips_notify(&gl_bt_coexist, type); +} + +void rtl_btc_scan_notify(struct rtl_priv *rtlpriv, u8 scantype) +{ + exhalbtc_scan_notify(&gl_bt_coexist, scantype); +} + +void rtl_btc_connect_notify(struct rtl_priv *rtlpriv, u8 action) +{ + exhalbtc_connect_notify(&gl_bt_coexist, action); +} + +void rtl_btc_mediastatus_notify(struct rtl_priv *rtlpriv, + enum _RT_MEDIA_STATUS mstatus) +{ + exhalbtc_mediastatus_notify(&gl_bt_coexist, mstatus); +} + +void rtl_btc_periodical(struct rtl_priv *rtlpriv) +{ + exhalbtc_periodical(&gl_bt_coexist); +} + +void rtl_btc_halt_notify(void) +{ + exhalbtc_halt_notify(&gl_bt_coexist); +} + +void rtl_btc_btinfo_notify(struct rtl_priv *rtlpriv, u8 *tmp_buf, u8 length) +{ + exhalbtc_bt_info_notify(&gl_bt_coexist, tmp_buf, length); +} + +bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv) +{ + return gl_bt_coexist.bt_info.limited_dig; +} + +bool rtl_btc_is_disable_edca_turbo(struct rtl_priv *rtlpriv) +{ + bool bt_change_edca = false; + u32 cur_edca_val; + u32 edca_bt_hs_uplink = 0x5ea42b, edca_bt_hs_downlink = 0x5ea42b; + u32 edca_hs; + u32 edca_addr = 0x504; + + cur_edca_val = rtl_read_dword(rtlpriv, edca_addr); + if (halbtc_is_wifi_uplink(rtlpriv)) { + if (cur_edca_val != edca_bt_hs_uplink) { + edca_hs = edca_bt_hs_uplink; + bt_change_edca = true; + } + } else { + if (cur_edca_val != edca_bt_hs_downlink) { + edca_hs = edca_bt_hs_downlink; + bt_change_edca = true; + } + } + + if (bt_change_edca) + rtl_write_dword(rtlpriv, edca_addr, edca_hs); + + return true; +} + +bool rtl_btc_is_bt_disabled(struct rtl_priv *rtlpriv) +{ + if (gl_bt_coexist.bt_info.bt_disabled) + return true; + else + return false; +} + +struct rtl_btc_ops *rtl_btc_get_ops_pointer(void) +{ + return &rtl_btc_operation; +} +EXPORT_SYMBOL(rtl_btc_get_ops_pointer); + +u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv) +{ + u8 num; + + if (rtlpriv->btcoexist.btc_info.ant_num == ANT_X2) + num = 2; + else + num = 1; + + return num; +} + +enum _RT_MEDIA_STATUS mgnt_link_status_query(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + enum _RT_MEDIA_STATUS m_status = RT_MEDIA_DISCONNECT; + + u8 bibss = (mac->opmode == NL80211_IFTYPE_ADHOC) ? 1 : 0; + + if (bibss || rtlpriv->mac80211.link_state >= MAC80211_LINKED) + m_status = RT_MEDIA_CONNECT; + + return m_status; +} + +u8 rtl_get_hwpg_bt_exist(struct rtl_priv *rtlpriv) +{ + return rtlpriv->btcoexist.btc_info.btcoexist; +} + +u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv) +{ + return rtlpriv->btcoexist.btc_info.bt_type; +} + +MODULE_AUTHOR("Page He <page_he@realsil.com.cn>"); +MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_AUTHOR("Larry Finger <Larry.FInger@lwfinger.net>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core"); + +static int __init rtl_btcoexist_module_init(void) +{ + return 0; +} + +static void __exit rtl_btcoexist_module_exit(void) +{ + return; +} + +module_init(rtl_btcoexist_module_init); +module_exit(rtl_btcoexist_module_exit); diff --git a/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h new file mode 100644 index 00000000000..805b22cc8fc --- /dev/null +++ b/drivers/net/wireless/rtlwifi/btcoexist/rtl_btc.h @@ -0,0 +1,52 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2010 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL_BTC_H__ +#define __RTL_BTC_H__ + +#include "halbt_precomp.h" + +void rtl_btc_init_variables(struct rtl_priv *rtlpriv); +void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv); +void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv); +void rtl_btc_ips_notify(struct rtl_priv *rtlpriv, u8 type); +void rtl_btc_scan_notify(struct rtl_priv *rtlpriv, u8 scantype); +void rtl_btc_connect_notify(struct rtl_priv *rtlpriv, u8 action); +void rtl_btc_mediastatus_notify(struct rtl_priv *rtlpriv, + enum _RT_MEDIA_STATUS mstatus); +void rtl_btc_periodical(struct rtl_priv *rtlpriv); +void rtl_btc_halt_notify(void); +void rtl_btc_btinfo_notify(struct rtl_priv *rtlpriv, u8 *tmpbuf, u8 length); +bool rtl_btc_is_limited_dig(struct rtl_priv *rtlpriv); +bool rtl_btc_is_disable_edca_turbo(struct rtl_priv *rtlpriv); +bool rtl_btc_is_bt_disabled(struct rtl_priv *rtlpriv); + +struct rtl_btc_ops *rtl_btc_get_ops_pointer(void); + +u8 rtl_get_hwpg_ant_num(struct rtl_priv *rtlpriv); +u8 rtl_get_hwpg_bt_exist(struct rtl_priv *rtlpriv); +u8 rtl_get_hwpg_bt_type(struct rtl_priv *rtlpriv); +enum _RT_MEDIA_STATUS mgnt_link_status_query(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c index 0e510f73041..0276153c72c 100644 --- a/drivers/net/wireless/rtlwifi/cam.c +++ b/drivers/net/wireless/rtlwifi/cam.c @@ -295,7 +295,7 @@ u8 rtl_cam_get_free_entry(struct ieee80211_hw *hw, u8 *sta_addr) /* Does STA already exist? */ for (i = 4; i < TOTAL_CAM_ENTRY; i++) { addr = rtlpriv->sec.hwsec_cam_sta_addr[i]; - if (memcmp(addr, sta_addr, ETH_ALEN) == 0) + if (ether_addr_equal_unaligned(addr, sta_addr)) return i; } /* Get a free CAM entry. */ @@ -335,7 +335,7 @@ void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr) addr = rtlpriv->sec.hwsec_cam_sta_addr[i]; bitmap = (rtlpriv->sec.hwsec_cam_bitmap) >> i; if (((bitmap & BIT(0)) == BIT(0)) && - (memcmp(addr, sta_addr, ETH_ALEN) == 0)) { + (ether_addr_equal_unaligned(addr, sta_addr))) { /* Remove from HW Security CAM */ eth_zero_addr(rtlpriv->sec.hwsec_cam_sta_addr[i]); rtlpriv->sec.hwsec_cam_bitmap &= ~(BIT(0) << i); diff --git a/drivers/net/wireless/rtlwifi/cam.h b/drivers/net/wireless/rtlwifi/cam.h index 35e00086a52..0105e6c1901 100644 --- a/drivers/net/wireless/rtlwifi/cam.h +++ b/drivers/net/wireless/rtlwifi/cam.h @@ -41,12 +41,12 @@ #define CAM_CONFIG_USEDK 1 #define CAM_CONFIG_NO_USEDK 0 -extern void rtl_cam_reset_all_entry(struct ieee80211_hw *hw); -extern u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, - u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg, - u32 ul_default_key, u8 *key_content); +void rtl_cam_reset_all_entry(struct ieee80211_hw *hw); +u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, + u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg, + u32 ul_default_key, u8 *key_content); int rtl_cam_delete_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, - u32 ul_key_id); + u32 ul_key_id); void rtl_cam_mark_invalid(struct ieee80211_hw *hw, u8 uc_index); void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index); void rtl_cam_reset_sec_info(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 733b7ce7f0e..b1ed6d0796f 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -36,6 +36,66 @@ #include <linux/export.h> +void rtl_addr_delay(u32 addr) +{ + if (addr == 0xfe) + mdelay(50); + else if (addr == 0xfd) + mdelay(5); + else if (addr == 0xfc) + mdelay(1); + else if (addr == 0xfb) + udelay(50); + else if (addr == 0xfa) + udelay(5); + else if (addr == 0xf9) + udelay(1); +} +EXPORT_SYMBOL(rtl_addr_delay); + +void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, + u32 mask, u32 data) +{ + if (addr == 0xfe) { + mdelay(50); + } else if (addr == 0xfd) { + mdelay(5); + } else if (addr == 0xfc) { + mdelay(1); + } else if (addr == 0xfb) { + udelay(50); + } else if (addr == 0xfa) { + udelay(5); + } else if (addr == 0xf9) { + udelay(1); + } else { + rtl_set_rfreg(hw, rfpath, addr, mask, data); + udelay(1); + } +} +EXPORT_SYMBOL(rtl_rfreg_delay); + +void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data) +{ + if (addr == 0xfe) { + mdelay(50); + } else if (addr == 0xfd) { + mdelay(5); + } else if (addr == 0xfc) { + mdelay(1); + } else if (addr == 0xfb) { + udelay(50); + } else if (addr == 0xfa) { + udelay(5); + } else if (addr == 0xf9) { + udelay(1); + } else { + rtl_set_bbreg(hw, addr, MASKDWORD, data); + udelay(1); + } +} +EXPORT_SYMBOL(rtl_bb_delay); + void rtl_fw_cb(const struct firmware *firmware, void *context) { struct ieee80211_hw *hw = context; @@ -46,10 +106,20 @@ void rtl_fw_cb(const struct firmware *firmware, void *context) "Firmware callback routine entered!\n"); complete(&rtlpriv->firmware_loading_complete); if (!firmware) { + if (rtlpriv->cfg->alt_fw_name) { + err = request_firmware(&firmware, + rtlpriv->cfg->alt_fw_name, + rtlpriv->io.dev); + pr_info("Loading alternative firmware %s\n", + rtlpriv->cfg->alt_fw_name); + if (!err) + goto found_alt; + } pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name); rtlpriv->max_fw_size = 0; return; } +found_alt: if (firmware->size > rtlpriv->max_fw_size) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Firmware is too big!\n"); @@ -115,7 +185,7 @@ static void rtl_op_stop(struct ieee80211_hw *hw) mutex_lock(&rtlpriv->locks.conf_mutex); mac->link_state = MAC80211_NOLINK; - memset(mac->bssid, 0, 6); + memset(mac->bssid, 0, ETH_ALEN); mac->vendor = PEER_UNKNOWN; /*reset sec info */ @@ -184,6 +254,7 @@ static int rtl_op_add_interface(struct ieee80211_hw *hw, rtlpriv->cfg->maps [RTL_IBSS_INT_MASKS]); } + mac->link_state = MAC80211_LINKED; break; case NL80211_IFTYPE_ADHOC: RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, @@ -280,7 +351,7 @@ static void rtl_op_remove_interface(struct ieee80211_hw *hw, mac->p2p = 0; mac->vif = NULL; mac->link_state = MAC80211_NOLINK; - memset(mac->bssid, 0, 6); + memset(mac->bssid, 0, ETH_ALEN); mac->vendor = PEER_UNKNOWN; mac->opmode = NL80211_IFTYPE_UNSPECIFIED; rtlpriv->cfg->ops->set_network_type(hw, mac->opmode); @@ -464,20 +535,40 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u32 rx_conf; *new_flags &= RTL_SUPPORTED_FILTERS; if (!changed_flags) return; + /* if ssid not set to hw don't check bssid + * here just used for linked scanning, & linked + * and nolink check bssid is set in set network_type */ + if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) && + (mac->link_state >= MAC80211_LINKED)) { + if (mac->opmode != NL80211_IFTYPE_AP && + mac->opmode != NL80211_IFTYPE_MESH_POINT) { + if (*new_flags & FIF_BCN_PRBRESP_PROMISC) { + rtlpriv->cfg->ops->set_chk_bssid(hw, false); + } else { + rtlpriv->cfg->ops->set_chk_bssid(hw, true); + } + } + } + + /* must be called after set_chk_bssid since that function modifies the + * RCR register too. */ + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(&rx_conf)); + /*TODO: we disable broadcase now, so enable here */ if (changed_flags & FIF_ALLMULTI) { if (*new_flags & FIF_ALLMULTI) { - mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AM] | + rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AM] | rtlpriv->cfg->maps[MAC_RCR_AB]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive multicast frame\n"); } else { - mac->rx_conf &= ~(rtlpriv->cfg->maps[MAC_RCR_AM] | + rx_conf &= ~(rtlpriv->cfg->maps[MAC_RCR_AM] | rtlpriv->cfg->maps[MAC_RCR_AB]); RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive multicast frame\n"); @@ -486,39 +577,25 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw, if (changed_flags & FIF_FCSFAIL) { if (*new_flags & FIF_FCSFAIL) { - mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACRC32]; + rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACRC32]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive FCS error frame\n"); } else { - mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACRC32]; + rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACRC32]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive FCS error frame\n"); } } - /* if ssid not set to hw don't check bssid - * here just used for linked scanning, & linked - * and nolink check bssid is set in set network_type */ - if ((changed_flags & FIF_BCN_PRBRESP_PROMISC) && - (mac->link_state >= MAC80211_LINKED)) { - if (mac->opmode != NL80211_IFTYPE_AP && - mac->opmode != NL80211_IFTYPE_MESH_POINT) { - if (*new_flags & FIF_BCN_PRBRESP_PROMISC) { - rtlpriv->cfg->ops->set_chk_bssid(hw, false); - } else { - rtlpriv->cfg->ops->set_chk_bssid(hw, true); - } - } - } if (changed_flags & FIF_CONTROL) { if (*new_flags & FIF_CONTROL) { - mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACF]; + rx_conf |= rtlpriv->cfg->maps[MAC_RCR_ACF]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive control frame\n"); } else { - mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACF]; + rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_ACF]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive control frame\n"); } @@ -526,15 +603,17 @@ static void rtl_op_configure_filter(struct ieee80211_hw *hw, if (changed_flags & FIF_OTHER_BSS) { if (*new_flags & FIF_OTHER_BSS) { - mac->rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AAP]; + rx_conf |= rtlpriv->cfg->maps[MAC_RCR_AAP]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Enable receive other BSS's frame\n"); } else { - mac->rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_AAP]; + rx_conf &= ~rtlpriv->cfg->maps[MAC_RCR_AAP]; RT_TRACE(rtlpriv, COMP_MAC80211, DBG_LOUD, "Disable receive other BSS's frame\n"); } } + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(&rx_conf)); } static int rtl_op_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -721,12 +800,17 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, mac->link_state = MAC80211_LINKED; mac->cnt_after_linked = 0; mac->assoc_id = bss_conf->aid; - memcpy(mac->bssid, bss_conf->bssid, 6); + memcpy(mac->bssid, bss_conf->bssid, ETH_ALEN); if (rtlpriv->cfg->ops->linked_set_reg) rtlpriv->cfg->ops->linked_set_reg(hw); rcu_read_lock(); sta = ieee80211_find_sta(vif, (u8 *)bss_conf->bssid); + if (!sta) { + pr_err("ieee80211_find_sta returned NULL\n"); + rcu_read_unlock(); + goto out; + } if (vif->type == NL80211_IFTYPE_STATION && sta) rtlpriv->cfg->ops->update_rate_tbl(hw, sta, 0); @@ -750,7 +834,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); mac->link_state = MAC80211_NOLINK; - memset(mac->bssid, 0, 6); + memset(mac->bssid, 0, ETH_ALEN); mac->vendor = PEER_UNKNOWN; if (rtlpriv->dm.supp_phymode_switch) { @@ -826,7 +910,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid); mac->vendor = PEER_UNKNOWN; - memcpy(mac->bssid, bss_conf->bssid, 6); + memcpy(mac->bssid, bss_conf->bssid, ETH_ALEN); rtlpriv->cfg->ops->set_network_type(hw, vif->type); rcu_read_lock(); @@ -881,7 +965,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, mac->basic_rates = basic_rates; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_BASIC_RATE, - (u8 *) (&basic_rates)); + (u8 *)(&basic_rates)); } rcu_read_unlock(); } @@ -895,6 +979,11 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, if (bss_conf->assoc) { if (ppsc->fwctrl_lps) { u8 mstatus = RT_MEDIA_CONNECT; + u8 keep_alive = 10; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_KEEP_ALIVE, + &keep_alive); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_JOINBSSRPT, &mstatus); @@ -1298,7 +1387,8 @@ static void rtl_op_rfkill_poll(struct ieee80211_hw *hw) * before switch channel or power save, or tx buffer packet * maybe send after offchannel or rf sleep, this may cause * dis-association by AP */ -static void rtl_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void rtl_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h index 2fe46a1b4f1..027e75374dc 100644 --- a/drivers/net/wireless/rtlwifi/core.h +++ b/drivers/net/wireless/rtlwifi/core.h @@ -41,5 +41,9 @@ extern const struct ieee80211_ops rtl_ops; void rtl_fw_cb(const struct firmware *firmware, void *context); +void rtl_addr_delay(u32 addr); +void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, + u32 mask, u32 data); +void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data); #endif diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c index 838a1ed3f19..2ffc7298f68 100644 --- a/drivers/net/wireless/rtlwifi/efuse.c +++ b/drivers/net/wireless/rtlwifi/efuse.c @@ -262,9 +262,9 @@ void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf) sizeof(u8), GFP_ATOMIC); if (!efuse_tbl) return; - efuse_word = kmalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC); + efuse_word = kzalloc(EFUSE_MAX_WORD_UNIT * sizeof(u16 *), GFP_ATOMIC); if (!efuse_word) - goto done; + goto out; for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) { efuse_word[i] = kmalloc(efuse_max_section * sizeof(u16), GFP_ATOMIC); @@ -378,6 +378,7 @@ done: for (i = 0; i < EFUSE_MAX_WORD_UNIT; i++) kfree(efuse_word[i]); kfree(efuse_word); +out: kfree(efuse_tbl); } @@ -1203,20 +1204,18 @@ static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate) static u16 efuse_get_current_size(struct ieee80211_hw *hw) { - int continual = true; u16 efuse_addr = 0; u8 hworden; u8 efuse_data, word_cnts; - while (continual && efuse_one_byte_read(hw, efuse_addr, &efuse_data) - && (efuse_addr < EFUSE_MAX_SIZE)) { - if (efuse_data != 0xFF) { - hworden = efuse_data & 0x0F; - word_cnts = efuse_calculate_word_cnts(hworden); - efuse_addr = efuse_addr + (word_cnts * 2) + 1; - } else { - continual = false; - } + while (efuse_one_byte_read(hw, efuse_addr, &efuse_data) && + efuse_addr < EFUSE_MAX_SIZE) { + if (efuse_data == 0xFF) + break; + + hworden = efuse_data & 0x0F; + word_cnts = efuse_calculate_word_cnts(hworden); + efuse_addr = efuse_addr + (word_cnts * 2) + 1; } return efuse_addr; diff --git a/drivers/net/wireless/rtlwifi/efuse.h b/drivers/net/wireless/rtlwifi/efuse.h index 395a326acfb..1663b3afd41 100644 --- a/drivers/net/wireless/rtlwifi/efuse.h +++ b/drivers/net/wireless/rtlwifi/efuse.h @@ -104,20 +104,19 @@ struct efuse_priv { u8 tx_power_g[14]; }; -extern void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf); -extern void efuse_initialize(struct ieee80211_hw *hw); -extern u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address); -extern void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value); -extern void read_efuse(struct ieee80211_hw *hw, u16 _offset, - u16 _size_byte, u8 *pbuf); -extern void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, - u16 offset, u32 *value); -extern void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, - u16 offset, u32 value); -extern bool efuse_shadow_update(struct ieee80211_hw *hw); -extern bool efuse_shadow_update_chk(struct ieee80211_hw *hw); -extern void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw); -extern void efuse_force_write_vendor_Id(struct ieee80211_hw *hw); -extern void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx); +void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf); +void efuse_initialize(struct ieee80211_hw *hw); +u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address); +void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value); +void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf); +void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, u16 offset, + u32 *value); +void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, u16 offset, + u32 value); +bool efuse_shadow_update(struct ieee80211_hw *hw); +bool efuse_shadow_update_chk(struct ieee80211_hw *hw); +void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw); +void efuse_force_write_vendor_Id(struct ieee80211_hw *hw); +void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx); #endif diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 703f839af6c..dae55257f0e 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -688,8 +688,6 @@ static void _rtl_receive_one(struct ieee80211_hw *hw, struct sk_buff *skb, rtlpriv->stats.rxbytesunicast += skb->len; } - rtl_is_special_data(hw, skb, false); - if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); @@ -736,11 +734,12 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; int index = rtlpci->rx_ring[rx_queue_idx].idx; + if (rtlpci->driver_is_goingto_unload) + return; /*RX NORMAL PKT */ while (count--) { /*rx descriptor */ @@ -812,19 +811,19 @@ done: if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) return; tmp_one = 1; - rtlpriv->cfg->ops->set_desc((u8 *) pdesc, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false, HW_DESC_RXBUFF_ADDR, (u8 *)&bufferaddress); - rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false, HW_DESC_RXPKT_LEN, (u8 *)&rtlpci->rxbuffersize); if (index == rtlpci->rxringcount - 1) - rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false, HW_DESC_RXERO, &tmp_one); - rtlpriv->cfg->ops->set_desc((u8 *)pdesc, false, HW_DESC_RXOWN, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, false, HW_DESC_RXOWN, &tmp_one); index = (index + 1) % rtlpci->rxringcount; @@ -984,6 +983,8 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) struct sk_buff *pskb = NULL; struct rtl_tx_desc *pdesc = NULL; struct rtl_tcb_desc tcb_desc; + /*This is for new trx flow*/ + struct rtl_tx_buffer_desc *pbuffer_desc = NULL; u8 temp_one = 1; memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); @@ -1005,11 +1006,12 @@ static void _rtl_pci_prepare_bcn_tasklet(struct ieee80211_hw *hw) info = IEEE80211_SKB_CB(pskb); pdesc = &ring->desc[0]; rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *) pdesc, - info, NULL, pskb, BEACON_QUEUE, &tcb_desc); + (u8 *)pbuffer_desc, info, NULL, pskb, + BEACON_QUEUE, &tcb_desc); __skb_queue_tail(&ring->queue, pskb); - rtlpriv->cfg->ops->set_desc((u8 *) pdesc, true, HW_DESC_OWN, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, HW_DESC_OWN, &temp_one); return; @@ -1067,7 +1069,7 @@ static void _rtl_pci_init_struct(struct ieee80211_hw *hw, mac->current_ampdu_factor = 3; /*QOS*/ - rtlpci->acm_method = eAcmWay2_SW; + rtlpci->acm_method = EACMWAY2_SW; /*task */ tasklet_init(&rtlpriv->works.irq_tasklet, @@ -1114,7 +1116,7 @@ static int _rtl_pci_init_tx_ring(struct ieee80211_hw *hw, ((i + 1) % entries) * sizeof(*ring); - rtlpriv->cfg->ops->set_desc((u8 *)&(ring[i]), + rtlpriv->cfg->ops->set_desc(hw, (u8 *)&(ring[i]), true, HW_DESC_TX_NEXTDESC_ADDR, (u8 *)&nextdescaddress); } @@ -1189,19 +1191,19 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw) dev_kfree_skb_any(skb); return 1; } - rtlpriv->cfg->ops->set_desc((u8 *)entry, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, HW_DESC_RXBUFF_ADDR, (u8 *)&bufferaddress); - rtlpriv->cfg->ops->set_desc((u8 *)entry, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, HW_DESC_RXPKT_LEN, (u8 *)&rtlpci-> rxbuffersize); - rtlpriv->cfg->ops->set_desc((u8 *) entry, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, HW_DESC_RXOWN, &tmp_one); } - rtlpriv->cfg->ops->set_desc((u8 *) entry, false, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, HW_DESC_RXERO, &tmp_one); } return 0; @@ -1332,7 +1334,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) for (i = 0; i < rtlpci->rxringcount; i++) { entry = &rtlpci->rx_ring[rx_queue_idx].desc[i]; - rtlpriv->cfg->ops->set_desc((u8 *) entry, + rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, HW_DESC_RXOWN, &tmp_one); @@ -1425,6 +1427,7 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct rtl8192_tx_ring *ring; struct rtl_tx_desc *pdesc; + struct rtl_tx_buffer_desc *ptx_bd_desc = NULL; u8 idx; u8 hw_queue = _rtl_mac_to_hwqueue(hw, skb); unsigned long flags; @@ -1465,17 +1468,22 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, idx = 0; pdesc = &ring->desc[idx]; - own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, - true, HW_DESC_OWN); + if (rtlpriv->use_new_trx_flow) { + ptx_bd_desc = &ring->buffer_desc[idx]; + } else { + own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, + true, HW_DESC_OWN); - if ((own == 1) && (hw_queue != BEACON_QUEUE)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, - "No more TX desc@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n", - hw_queue, ring->idx, idx, - skb_queue_len(&ring->queue)); + if ((own == 1) && (hw_queue != BEACON_QUEUE)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "No more TX desc@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n", + hw_queue, ring->idx, idx, + skb_queue_len(&ring->queue)); - spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); - return skb->len; + spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, + flags); + return skb->len; + } } if (ieee80211_is_data_qos(fc)) { @@ -1495,17 +1503,20 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, rtlpriv->cfg->ops->led_control(hw, LED_CTL_TX); rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, - info, sta, skb, hw_queue, ptcb_desc); + (u8 *)ptx_bd_desc, info, sta, skb, hw_queue, ptcb_desc); __skb_queue_tail(&ring->queue, skb); - rtlpriv->cfg->ops->set_desc((u8 *)pdesc, true, - HW_DESC_OWN, &temp_one); - + if (rtlpriv->use_new_trx_flow) { + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, + HW_DESC_OWN, &hw_queue); + } else { + rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, true, + HW_DESC_OWN, &temp_one); + } if ((ring->entries - skb_queue_len(&ring->queue)) < 2 && hw_queue != BEACON_QUEUE) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, "less desc left, stop skb_queue@%d, ring->idx = %d, idx = %d, skb_queue_len = 0x%d\n", hw_queue, ring->idx, idx, @@ -1637,6 +1648,7 @@ static void rtl_pci_stop(struct ieee80211_hw *hw) */ set_hal_stop(rtlhal); + rtlpci->driver_is_goingto_unload = true; rtlpriv->cfg->ops->disable_interrupt(hw); cancel_work_sync(&rtlpriv->works.lps_change_work); @@ -1654,7 +1666,6 @@ static void rtl_pci_stop(struct ieee80211_hw *hw) ppsc->rfchange_inprogress = true; spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flags); - rtlpci->driver_is_goingto_unload = true; rtlpriv->cfg->ops->hw_disable(hw); /* some things are not needed if firmware not available */ if (!rtlpriv->max_fw_size) @@ -1842,6 +1853,65 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev, return true; } +static int rtl_pci_intr_mode_msi(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(pcipriv); + int ret; + + ret = pci_enable_msi(rtlpci->pdev); + if (ret < 0) + return ret; + + ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt, + IRQF_SHARED, KBUILD_MODNAME, hw); + if (ret < 0) { + pci_disable_msi(rtlpci->pdev); + return ret; + } + + rtlpci->using_msi = true; + + RT_TRACE(rtlpriv, COMP_INIT|COMP_INTR, DBG_DMESG, + "MSI Interrupt Mode!\n"); + return 0; +} + +static int rtl_pci_intr_mode_legacy(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(pcipriv); + int ret; + + ret = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt, + IRQF_SHARED, KBUILD_MODNAME, hw); + if (ret < 0) + return ret; + + rtlpci->using_msi = false; + RT_TRACE(rtlpriv, COMP_INIT|COMP_INTR, DBG_DMESG, + "Pin-based Interrupt Mode!\n"); + return 0; +} + +static int rtl_pci_intr_mode_decide(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(pcipriv); + int ret; + + if (rtlpci->msi_support) { + ret = rtl_pci_intr_mode_msi(hw); + if (ret < 0) + ret = rtl_pci_intr_mode_legacy(hw); + } else { + ret = rtl_pci_intr_mode_legacy(hw); + } + return ret; +} + int rtl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1984,8 +2054,7 @@ int rtl_pci_probe(struct pci_dev *pdev, } rtlpci = rtl_pcidev(pcipriv); - err = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt, - IRQF_SHARED, KBUILD_MODNAME, hw); + err = rtl_pci_intr_mode_decide(hw); if (err) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "%s: failed to register IRQ handler\n", @@ -2009,7 +2078,6 @@ fail2: fail1: if (hw) ieee80211_free_hw(hw); - pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; @@ -2054,6 +2122,9 @@ void rtl_pci_disconnect(struct pci_dev *pdev) rtlpci->irq_alloc = 0; } + if (rtlpci->using_msi) + pci_disable_msi(rtlpci->pdev); + list_del(&rtlpriv->list); if (rtlpriv->io.pci_mem_start != 0) { pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start); @@ -2064,8 +2135,6 @@ void rtl_pci_disconnect(struct pci_dev *pdev) rtl_pci_disable_aspm(hw); - pci_set_drvdata(pdev, NULL); - ieee80211_free_hw(hw); } EXPORT_SYMBOL(rtl_pci_disconnect); diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h index d3262ec45d2..90174a814a6 100644 --- a/drivers/net/wireless/rtlwifi/pci.h +++ b/drivers/net/wireless/rtlwifi/pci.h @@ -137,12 +137,22 @@ struct rtl_tx_cmd_desc { u32 dword[16]; } __packed; +/* In new TRX flow, Buffer_desc is new concept + * But TX wifi info == TX descriptor in old flow + * RX wifi info == RX descriptor in old flow + */ +struct rtl_tx_buffer_desc { + u32 dword[8]; /*seg = 4*/ +} __packed; + struct rtl8192_tx_ring { struct rtl_tx_desc *desc; dma_addr_t dma; unsigned int idx; unsigned int entries; struct sk_buff_head queue; + /*add for new trx flow*/ + struct rtl_tx_buffer_desc *buffer_desc; /*tx buffer descriptor*/ }; struct rtl8192_rx_ring { @@ -199,6 +209,10 @@ struct rtl_pci { u16 shortretry_limit; u16 longretry_limit; + + /* MSI support */ + bool msi_support; + bool using_msi; }; struct mp_adapter { diff --git a/drivers/net/wireless/rtlwifi/ps.c b/drivers/net/wireless/rtlwifi/ps.c index 0d81f766fd0..50504942ded 100644 --- a/drivers/net/wireless/rtlwifi/ps.c +++ b/drivers/net/wireless/rtlwifi/ps.c @@ -32,6 +32,106 @@ #include "base.h" #include "ps.h" +/* Description: + * This routine deals with the Power Configuration CMD + * parsing for RTL8723/RTL8188E Series IC. + * Assumption: + * We should follow specific format that was released from HW SD. + */ +bool rtl_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version, + u8 faversion, u8 interface_type, + struct wlan_pwr_cfg pwrcfgcmd[]) +{ + struct wlan_pwr_cfg cfg_cmd = {0}; + bool polling_bit = false; + u32 ary_idx = 0; + u8 value = 0; + u32 offset = 0; + u32 polling_count = 0; + u32 max_polling_cnt = 5000; + + do { + cfg_cmd = pwrcfgcmd[ary_idx]; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): offset(%#x),cut_msk(%#x), famsk(%#x)," + "interface_msk(%#x), base(%#x), cmd(%#x), msk(%#x), value(%#x)\n", + GET_PWR_CFG_OFFSET(cfg_cmd), + GET_PWR_CFG_CUT_MASK(cfg_cmd), + GET_PWR_CFG_FAB_MASK(cfg_cmd), + GET_PWR_CFG_INTF_MASK(cfg_cmd), + GET_PWR_CFG_BASE(cfg_cmd), GET_PWR_CFG_CMD(cfg_cmd), + GET_PWR_CFG_MASK(cfg_cmd), GET_PWR_CFG_VALUE(cfg_cmd)); + + if ((GET_PWR_CFG_FAB_MASK(cfg_cmd)&faversion) && + (GET_PWR_CFG_CUT_MASK(cfg_cmd)&cut_version) && + (GET_PWR_CFG_INTF_MASK(cfg_cmd)&interface_type)) { + switch (GET_PWR_CFG_CMD(cfg_cmd)) { + case PWR_CMD_READ: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_READ\n"); + break; + case PWR_CMD_WRITE: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_WRITE\n"); + offset = GET_PWR_CFG_OFFSET(cfg_cmd); + + /*Read the value from system register*/ + value = rtl_read_byte(rtlpriv, offset); + value &= (~(GET_PWR_CFG_MASK(cfg_cmd))); + value |= (GET_PWR_CFG_VALUE(cfg_cmd) & + GET_PWR_CFG_MASK(cfg_cmd)); + + /*Write the value back to sytem register*/ + rtl_write_byte(rtlpriv, offset, value); + break; + case PWR_CMD_POLLING: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_POLLING\n"); + polling_bit = false; + offset = GET_PWR_CFG_OFFSET(cfg_cmd); + + do { + value = rtl_read_byte(rtlpriv, offset); + + value &= GET_PWR_CFG_MASK(cfg_cmd); + if (value == + (GET_PWR_CFG_VALUE(cfg_cmd) + & GET_PWR_CFG_MASK(cfg_cmd))) + polling_bit = true; + else + udelay(10); + + if (polling_count++ > max_polling_cnt) + return false; + } while (!polling_bit); + break; + case PWR_CMD_DELAY: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_DELAY\n"); + if (GET_PWR_CFG_VALUE(cfg_cmd) == + PWRSEQ_DELAY_US) + udelay(GET_PWR_CFG_OFFSET(cfg_cmd)); + else + mdelay(GET_PWR_CFG_OFFSET(cfg_cmd)); + break; + case PWR_CMD_END: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_END\n"); + return true; + default: + RT_ASSERT(false, + "rtl_hal_pwrseqcmdparsing(): Unknown CMD!!\n"); + break; + } + + } + ary_idx++; + } while (1); + + return true; +} +EXPORT_SYMBOL(rtl_hal_pwrseqcmdparsing); + bool rtl_ps_enable_nic(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -48,7 +148,7 @@ bool rtl_ps_enable_nic(struct ieee80211_hw *hw) /*<2> Enable Adapter */ if (rtlpriv->cfg->ops->hw_init(hw)) - return 1; + return false; RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); /*<3> Enable Interrupt */ @@ -478,7 +578,7 @@ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len) return; /* and only beacons from the associated BSSID, please */ - if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) return; rtlpriv->psc.last_beacon = jiffies; @@ -659,7 +759,7 @@ static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data, unsigned int len) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct ieee80211_mgmt *mgmt = (void *)data; + struct ieee80211_mgmt *mgmt = data; struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); u8 *pos, *end, *ie; u16 noa_len; @@ -758,7 +858,7 @@ static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data, unsigned int len) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct ieee80211_mgmt *mgmt = (void *)data; + struct ieee80211_mgmt *mgmt = data; struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info); u8 noa_num, index, i, noa_index = 0; u8 *pos, *end, *ie; @@ -850,9 +950,8 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) switch (p2p_ps_state) { case P2P_PS_DISABLE: p2pinfo->p2p_ps_state = p2p_ps_state; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_H2C_FW_P2P_PS_OFFLOAD, - (u8 *)(&p2p_ps_state)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD, + &p2p_ps_state); p2pinfo->noa_index = 0; p2pinfo->ctwindow = 0; @@ -864,7 +963,7 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) rtlps->smart_ps = 2; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&rtlps->pwr_mode)); + &rtlps->pwr_mode); } } break; @@ -877,12 +976,12 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) rtlps->smart_ps = 0; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&rtlps->pwr_mode)); + &rtlps->pwr_mode); } } rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD, - (u8 *)(&p2p_ps_state)); + &p2p_ps_state); } break; case P2P_PS_SCAN: @@ -892,7 +991,7 @@ void rtl_p2p_ps_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) p2pinfo->p2p_ps_state = p2p_ps_state; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD, - (u8 *)(&p2p_ps_state)); + &p2p_ps_state); } break; default: @@ -912,7 +1011,7 @@ void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - struct ieee80211_hdr *hdr = (void *)data; + struct ieee80211_hdr *hdr = data; if (!mac->p2p) return; @@ -923,7 +1022,7 @@ void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len) return; /* and only beacons from the associated BSSID, please */ - if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid)) + if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid)) return; /* check if this really is a beacon */ diff --git a/drivers/net/wireless/rtlwifi/ps.h b/drivers/net/wireless/rtlwifi/ps.h index 88bd76ea88f..3bd41f95897 100644 --- a/drivers/net/wireless/rtlwifi/ps.h +++ b/drivers/net/wireless/rtlwifi/ps.h @@ -32,6 +32,66 @@ #define MAX_SW_LPS_SLEEP_INTV 5 +/*--------------------------------------------- + * 3 The value of cmd: 4 bits + *--------------------------------------------- + */ +#define PWR_CMD_READ 0x00 +#define PWR_CMD_WRITE 0x01 +#define PWR_CMD_POLLING 0x02 +#define PWR_CMD_DELAY 0x03 +#define PWR_CMD_END 0x04 + +/* define the base address of each block */ +#define PWR_BASEADDR_MAC 0x00 +#define PWR_BASEADDR_USB 0x01 +#define PWR_BASEADDR_PCIE 0x02 +#define PWR_BASEADDR_SDIO 0x03 + +#define PWR_FAB_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3)) +#define PWR_CUT_TESTCHIP_MSK BIT(0) +#define PWR_CUT_A_MSK BIT(1) +#define PWR_CUT_B_MSK BIT(2) +#define PWR_CUT_C_MSK BIT(3) +#define PWR_CUT_D_MSK BIT(4) +#define PWR_CUT_E_MSK BIT(5) +#define PWR_CUT_F_MSK BIT(6) +#define PWR_CUT_G_MSK BIT(7) +#define PWR_CUT_ALL_MSK 0xFF +#define PWR_INTF_SDIO_MSK BIT(0) +#define PWR_INTF_USB_MSK BIT(1) +#define PWR_INTF_PCI_MSK BIT(2) +#define PWR_INTF_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3)) + +enum pwrseq_delay_unit { + PWRSEQ_DELAY_US, + PWRSEQ_DELAY_MS, +}; + +struct wlan_pwr_cfg { + u16 offset; + u8 cut_msk; + u8 fab_msk:4; + u8 interface_msk:4; + u8 base:4; + u8 cmd:4; + u8 msk; + u8 value; +}; + +#define GET_PWR_CFG_OFFSET(__PWR_CMD) (__PWR_CMD.offset) +#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) (__PWR_CMD.cut_msk) +#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) (__PWR_CMD.fab_msk) +#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) (__PWR_CMD.interface_msk) +#define GET_PWR_CFG_BASE(__PWR_CMD) (__PWR_CMD.base) +#define GET_PWR_CFG_CMD(__PWR_CMD) (__PWR_CMD.cmd) +#define GET_PWR_CFG_MASK(__PWR_CMD) (__PWR_CMD.msk) +#define GET_PWR_CFG_VALUE(__PWR_CMD) (__PWR_CMD.value) + +bool rtl_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version, + u8 fab_version, u8 interface_type, + struct wlan_pwr_cfg pwrcfgcmd[]); + bool rtl_ps_set_rf_state(struct ieee80211_hw *hw, enum rf_pwrstate state_toset, u32 changesource); bool rtl_ps_enable_nic(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index a98acefb8c0..ee28a1a3d01 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -260,8 +260,7 @@ static void rtl_rate_free_sta(void *rtlpriv, kfree(rate_priv); } -static struct rate_control_ops rtl_rate_ops = { - .module = NULL, +static const struct rate_control_ops rtl_rate_ops = { .name = "rtl_rc", .alloc = rtl_rate_alloc, .free = rtl_rate_free, diff --git a/drivers/net/wireless/rtlwifi/regd.c b/drivers/net/wireless/rtlwifi/regd.c index d7d0d4948b0..a4eb9b27143 100644 --- a/drivers/net/wireless/rtlwifi/regd.c +++ b/drivers/net/wireless/rtlwifi/regd.c @@ -59,30 +59,26 @@ static struct country_code_to_enum_rd allCountries[] = { */ #define RTL819x_2GHZ_CH12_13 \ REG_RULE(2467-10, 2472+10, 40, 0, 20,\ - NL80211_RRF_PASSIVE_SCAN) + NL80211_RRF_NO_IR) #define RTL819x_2GHZ_CH14 \ REG_RULE(2484-10, 2484+10, 40, 0, 20, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_OFDM) + NL80211_RRF_NO_IR | NL80211_RRF_NO_OFDM) /* 5G chan 36 - chan 64*/ #define RTL819x_5GHZ_5150_5350 \ REG_RULE(5150-10, 5350+10, 40, 0, 30, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) /* 5G chan 100 - chan 165*/ #define RTL819x_5GHZ_5470_5850 \ REG_RULE(5470-10, 5850+10, 40, 0, 30, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) /* 5G chan 149 - chan 165*/ #define RTL819x_5GHZ_5725_5850 \ REG_RULE(5725-10, 5850+10, 40, 0, 30, \ - NL80211_RRF_PASSIVE_SCAN | \ - NL80211_RRF_NO_IBSS) + NL80211_RRF_NO_IR) #define RTL819x_5GHZ_ALL \ (RTL819x_5GHZ_5150_5350, RTL819x_5GHZ_5470_5850) @@ -172,7 +168,8 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, (ch->flags & IEEE80211_CHAN_RADAR)) continue; if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { - reg_rule = freq_reg_info(wiphy, ch->center_freq); + reg_rule = freq_reg_info(wiphy, + MHZ_TO_KHZ(ch->center_freq)); if (IS_ERR(reg_rule)) continue; @@ -185,16 +182,11 @@ static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, *regulatory_hint(). */ - if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) - ch->flags &= ~IEEE80211_CHAN_NO_IBSS; - if (!(reg_rule-> - flags & NL80211_RRF_PASSIVE_SCAN)) - ch->flags &= - ~IEEE80211_CHAN_PASSIVE_SCAN; + if (!(reg_rule->flags & NL80211_RRF_NO_IR)) + ch->flags &= ~IEEE80211_CHAN_NO_IR; } else { if (ch->beacon_found) - ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN); + ch->flags &= ~IEEE80211_CHAN_NO_IR; } } } @@ -219,11 +211,11 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { ch = &sband->channels[11]; /* CH 12 */ - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + if (ch->flags & IEEE80211_CHAN_NO_IR) + ch->flags &= ~IEEE80211_CHAN_NO_IR; ch = &sband->channels[12]; /* CH 13 */ - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + if (ch->flags & IEEE80211_CHAN_NO_IR) + ch->flags &= ~IEEE80211_CHAN_NO_IR; return; } @@ -235,19 +227,19 @@ static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, */ ch = &sband->channels[11]; /* CH 12 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); + reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq)); if (!IS_ERR(reg_rule)) { - if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + if (!(reg_rule->flags & NL80211_RRF_NO_IR)) + if (ch->flags & IEEE80211_CHAN_NO_IR) + ch->flags &= ~IEEE80211_CHAN_NO_IR; } ch = &sband->channels[12]; /* CH 13 */ - reg_rule = freq_reg_info(wiphy, ch->center_freq); + reg_rule = freq_reg_info(wiphy, MHZ_TO_KHZ(ch->center_freq)); if (!IS_ERR(reg_rule)) { - if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) - if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) - ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; + if (!(reg_rule->flags & NL80211_RRF_NO_IR)) + if (ch->flags & IEEE80211_CHAN_NO_IR) + ch->flags &= ~IEEE80211_CHAN_NO_IR; } } @@ -284,8 +276,7 @@ static void _rtl_reg_apply_radar_flags(struct wiphy *wiphy) */ if (!(ch->flags & IEEE80211_CHAN_DISABLED)) ch->flags |= IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN; + IEEE80211_CHAN_NO_IR; } } @@ -354,9 +345,9 @@ static int _rtl_regd_init_wiphy(struct rtl_regulatory *reg, wiphy->reg_notifier = reg_notifier; - wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; - wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY; - wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG; + wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS; regd = _rtl_regdomain_select(reg); wiphy_apply_custom_regulatory(wiphy, regd); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile b/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile index 5b194e97f4b..a85419a3765 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/Makefile @@ -5,7 +5,6 @@ rtl8188ee-objs := \ led.o \ phy.o \ pwrseq.o \ - pwrseqcmd.o \ rf.o \ sw.o \ table.o \ diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c index 21a5cf06067..f8daa61cf1c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c @@ -235,7 +235,7 @@ void rtl88e_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 pwr_val = 0; u8 cck_base = rtldm->swing_idx_cck_base; u8 cck_val = rtldm->swing_idx_cck; - u8 ofdm_base = rtldm->swing_idx_ofdm_base; + u8 ofdm_base = rtldm->swing_idx_ofdm_base[0]; u8 ofdm_val = rtlpriv->dm.swing_idx_ofdm[RF90_PATH_A]; if (type == 0) { @@ -726,7 +726,7 @@ static void rtl88e_dm_pwdb_monitor(struct ieee80211_hw *hw) static u64 last_rx; long tmp_entry_max_pwdb = 0, tmp_entry_min_pwdb = 0xff; - if (rtlhal->oem_id == RT_CID_819x_HP) { + if (rtlhal->oem_id == RT_CID_819X_HP) { u64 cur_txok_cnt = 0; u64 cur_rxok_cnt = 0; cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok; @@ -851,9 +851,8 @@ static void rtl88e_dm_check_edca_turbo(struct ieee80211_hw *hw) } else { if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_AC_PARAM, - (u8 *)(&tmp)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, + &tmp); rtlpriv->dm.current_turbo_edca = false; } } @@ -912,7 +911,7 @@ static void rtl88e_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw for (i = 0; i < OFDM_TABLE_LENGTH; i++) { if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) { ofdm_old[0] = (u8) i; - rtldm->swing_idx_ofdm_base = (u8)i; + rtldm->swing_idx_ofdm_base[0] = (u8)i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "Initial pathA ele_d reg0x%x = 0x%lx, ofdm_index = 0x%x\n", ROFDM0_XATXIQIMBAL, @@ -1078,7 +1077,7 @@ static void rtl88e_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw rtldm->swing_flag_ofdm = true; } - if (rtldm->swing_idx_cck != rtldm->swing_idx_cck) { + if (rtldm->swing_idx_cck_cur != rtldm->swing_idx_cck) { rtldm->swing_idx_cck_cur = rtldm->swing_idx_cck; rtldm->swing_flag_cck = true; } diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c index 557bc5b8327..4f9376ad473 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/fw.c @@ -119,7 +119,7 @@ static void _rtl88e_write_fw(struct ieee80211_hw *hw, enum version_8188e version, u8 *buffer, u32 size) { struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 *buf_ptr = (u8 *)buffer; + u8 *buf_ptr = buffer; u32 page_no, remain; u32 page, offset; @@ -213,7 +213,7 @@ int rtl88e_download_fw(struct ieee80211_hw *hw, bool buse_wake_on_wlan_fw) return 1; pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware; - pfwdata = (u8 *)rtlhal->pfirmware; + pfwdata = rtlhal->pfirmware; fwsize = rtlhal->fwsize; RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, "normal Firmware SIZE %d\n", fwsize); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c index b68cae3024f..b14cf5a10f4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c @@ -41,7 +41,6 @@ #include "fw.h" #include "led.h" #include "hw.h" -#include "pwrseqcmd.h" #include "pwrseq.h" #define LLT_CONFIG 5 @@ -143,12 +142,12 @@ static void _rtl88ee_set_fw_clock_on(struct ieee80211_hw *hw, } else { rtlhal->fw_clk_change_in_progress = false; spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + break; } } if (IS_IN_LOW_POWER_STATE_88E(rtlhal->fw_ps_state)) { - rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); if (FW_PS_IS_ACK(rpwm_val)) { isr_regaddr = REG_HISR; content = rtl_read_dword(rtlpriv, isr_regaddr); @@ -225,7 +224,7 @@ static void _rtl88ee_set_fw_clock_off(struct ieee80211_hw *hw, rtlhal->fw_ps_state = FW_PS_STATE(rpwm_val); rtl_write_word(rtlpriv, REG_HISR, 0x0100); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + &rpwm_val); spin_lock_bh(&rtlpriv->locks.fw_ps_lock); rtlhal->fw_clk_change_in_progress = false; spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); @@ -273,15 +272,14 @@ static void _rtl88ee_fwlps_leave(struct ieee80211_hw *hw) _rtl88ee_set_fw_clock_on(hw, rpwm_val, false); rtlhal->allow_sw_to_change_hwclc = false; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&fw_pwrmode)); + &fw_pwrmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *)(&fw_current_inps)); } else { rpwm_val = FW_PS_STATE_ALL_ON_88E; /* RF on */ - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&fw_pwrmode)); + &fw_pwrmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *)(&fw_current_inps)); } @@ -300,7 +298,7 @@ static void _rtl88ee_fwlps_enter(struct ieee80211_hw *hw) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *)(&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&ppsc->fwctrl_psmode)); + &ppsc->fwctrl_psmode); rtlhal->allow_sw_to_change_hwclc = true; _rtl88ee_set_fw_clock_off(hw, rpwm_val); } else { @@ -308,9 +306,8 @@ static void _rtl88ee_fwlps_enter(struct ieee80211_hw *hw) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *)(&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&ppsc->fwctrl_psmode)); - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + &ppsc->fwctrl_psmode); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); } } @@ -419,12 +416,12 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) for (e_aci = 0; e_aci < AC_MAX; e_aci++) { rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, - (u8 *)(&e_aci)); + &e_aci); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; - u8 short_preamble = (bool) (*(u8 *)val); + u8 short_preamble = (bool)*val; reg_tmp = rtl_read_byte(rtlpriv, REG_TRXPTCL_CTL+2); if (short_preamble) { reg_tmp |= 0x02; @@ -435,13 +432,13 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) } break; } case HW_VAR_WPA_CONFIG: - rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *)val)); + rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_AMPDU_MIN_SPACE:{ u8 min_spacing_to_set; u8 sec_min_space; - min_spacing_to_set = *((u8 *)val); + min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { sec_min_space = 0; @@ -464,7 +461,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; - density_to_set = *((u8 *)val); + density_to_set = *val; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, @@ -482,7 +479,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) reg = regtoset_normal; - factor = *((u8 *)val); + factor = *val; if (factor <= 3) { factor = (1 << (factor + 2)); if (factor > 0xf) @@ -505,15 +502,15 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) } break; } case HW_VAR_AC_PARAM:{ - u8 e_aci = *((u8 *)val); + u8 e_aci = *val; rtl88e_dm_init_edca_turbo(hw); - if (rtlpci->acm_method != eAcmWay2_SW) + if (rtlpci->acm_method != EACMWAY2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, - (u8 *)(&e_aci)); + &e_aci); break; } case HW_VAR_ACM_CTRL:{ - u8 e_aci = *((u8 *)val); + u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; @@ -566,7 +563,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlpci->receive_config = ((u32 *)(val))[0]; break; case HW_VAR_RETRY_LIMIT:{ - u8 retry_limit = ((u8 *)(val))[0]; + u8 retry_limit = *val; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | @@ -579,7 +576,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlefuse->efuse_usedbytes = *((u16 *)val); break; case HW_VAR_EFUSE_USAGE: - rtlefuse->efuse_usedpercentage = *((u8 *)val); + rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl88e_phy_set_io_cmd(hw, (*(enum io_type *)val)); @@ -591,15 +588,13 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) udelay(1); if (rpwm_val & BIT(7)) { - rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, - (*(u8 *)val)); + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val); } else { - rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, - ((*(u8 *)val) | BIT(7))); + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7)); } break; } case HW_VAR_H2C_FW_PWRMODE: - rtl88e_set_fw_pwrmode_cmd(hw, (*(u8 *)val)); + rtl88e_set_fw_pwrmode_cmd(hw, *val); break; case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *)val); @@ -616,7 +611,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) _rtl88ee_fwlps_leave(hw); break; } case HW_VAR_H2C_FW_JOINBSSRPT:{ - u8 mstatus = (*(u8 *)val); + u8 mstatus = *val; u8 tmp, tmp_reg422, uval; u8 count = 0, dlbcn_count = 0; bool recover = false; @@ -667,10 +662,10 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) } rtl_write_byte(rtlpriv, REG_CR + 1, (tmp & ~(BIT(0)))); } - rtl88e_set_fw_joinbss_report_cmd(hw, (*(u8 *)val)); + rtl88e_set_fw_joinbss_report_cmd(hw, *val); break; } case HW_VAR_H2C_FW_P2P_PS_OFFLOAD: - rtl88e_set_p2p_ps_offload_cmd(hw, (*(u8 *)val)); + rtl88e_set_p2p_ps_offload_cmd(hw, *val); break; case HW_VAR_AID:{ u16 u2btmp; @@ -680,7 +675,7 @@ void rtl88ee_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) mac->assoc_id)); break; } case HW_VAR_CORRECT_TSF:{ - u8 btype_ibss = ((u8 *)(val))[0]; + u8 btype_ibss = *val; if (btype_ibss == true) _rtl88ee_stop_tx_beacon(hw); @@ -814,11 +809,11 @@ static bool _rtl88ee_init_mac(struct ieee80211_hw *hw) rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00); /* HW Power on sequence */ - if (!rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, - PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, - Rtl8188E_NIC_ENABLE_FLOW)) { + if (!rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, + PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, + Rtl8188E_NIC_ENABLE_FLOW)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, - "init MAC Fail as rtl88_hal_pwrseqcmdparsing\n"); + "init MAC Fail as rtl_hal_pwrseqcmdparsing\n"); return false; } @@ -1024,9 +1019,20 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw) bool rtstatus = true; int err = 0; u8 tmp_u1b, u1byte; + unsigned long flags; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Rtl8188EE hw init\n"); rtlpriv->rtlhal.being_init_adapter = true; + /* As this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); + rtlpriv->intf_ops->disable_aspm(hw); tmp_u1b = rtl_read_byte(rtlpriv, REG_SYS_CLKR+1); @@ -1042,7 +1048,7 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw) if (rtstatus != true) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); err = 1; - return err; + goto exit; } err = rtl88e_download_fw(hw, false); @@ -1050,8 +1056,7 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; - rtlhal->fw_ready = false; - return err; + goto exit; } else { rtlhal->fw_ready = true; } @@ -1096,7 +1101,7 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw) if (ppsc->rfpwr_state == ERFON) { if ((rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) || ((rtlefuse->antenna_div_type == CG_TRX_HW_ANTDIV) && - (rtlhal->oem_id == RT_CID_819x_HP))) { + (rtlhal->oem_id == RT_CID_819X_HP))) { rtl88e_phy_set_rfpath_switch(hw, true); rtlpriv->dm.fat_table.rx_idle_ant = MAIN_ANT; } else { @@ -1134,10 +1139,12 @@ int rtl88ee_hw_init(struct ieee80211_hw *hw) } rtl_write_byte(rtlpriv, REG_NAV_CTRL+2, ((30000+127)/128)); rtl88e_dm_init(hw); +exit: + local_irq_restore(flags); rtlpriv->rtlhal.being_init_adapter = false; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "end of Rtl8188EE hw init %x\n", err); - return 0; + return err; } static enum version_8188e _rtl88ee_read_chip_version(struct ieee80211_hw *hw) @@ -1234,12 +1241,13 @@ static int _rtl88ee_set_media_status(struct ieee80211_hw *hw, void rtl88ee_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - u32 reg_rcr = rtlpci->receive_config; + u32 reg_rcr; if (rtlpriv->psc.rfpwr_state != ERFON) return; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); + if (check_bssid == true) { reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, @@ -1344,9 +1352,9 @@ static void _rtl88ee_poweroff_adapter(struct ieee80211_hw *hw) } rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+1, 0xFF); - rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, - PWR_INTF_PCI_MSK, - Rtl8188E_NIC_LPS_ENTER_FLOW); + rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, + Rtl8188E_NIC_LPS_ENTER_FLOW); rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00); @@ -1360,8 +1368,8 @@ static void _rtl88ee_poweroff_adapter(struct ieee80211_hw *hw) u1b_tmp = rtl_read_byte(rtlpriv, REG_32K_CTRL); rtl_write_byte(rtlpriv, REG_32K_CTRL, (u1b_tmp & (~BIT(0)))); - rtl88_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, - PWR_INTF_PCI_MSK, Rtl8188E_NIC_DISABLE_FLOW); + rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, Rtl8188E_NIC_DISABLE_FLOW); u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL+1); rtl_write_byte(rtlpriv, REG_RSV_CTRL+1, (u1b_tmp & (~BIT(3)))); @@ -1815,7 +1823,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid); /*customer ID*/ - rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID]; + rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; if (rtlefuse->eeprom_oemid == 0xFF) rtlefuse->eeprom_oemid = 0; @@ -1832,7 +1840,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "dev_addr: %pM\n", rtlefuse->dev_addr); /*channel plan */ - rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; /* set channel paln to world wide 13 */ rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13; /*tx power*/ @@ -1844,7 +1852,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw) rtlefuse->autoload_failflag, hwinfo); /*board type*/ - rtlefuse->board_type = (((*(u8 *)&hwinfo[jj]) & 0xE0) >> 5); + rtlefuse->board_type = (hwinfo[jj] & 0xE0) >> 5; /*Wake on wlan*/ rtlefuse->wowlan_enable = ((hwinfo[kk] & 0x40) >> 6); /*parse xtal*/ @@ -1871,15 +1879,15 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw) case EEPROM_CID_DEFAULT: if (rtlefuse->eeprom_did == 0x8179) { if (rtlefuse->eeprom_svid == 0x1025) { - rtlhal->oem_id = RT_CID_819x_Acer; + rtlhal->oem_id = RT_CID_819X_ACER; } else if ((rtlefuse->eeprom_svid == 0x10EC && rtlefuse->eeprom_smid == 0x0179) || (rtlefuse->eeprom_svid == 0x17AA && rtlefuse->eeprom_smid == 0x0179)) { - rtlhal->oem_id = RT_CID_819x_Lenovo; + rtlhal->oem_id = RT_CID_819X_LENOVO; } else if (rtlefuse->eeprom_svid == 0x103c && rtlefuse->eeprom_smid == 0x197d) { - rtlhal->oem_id = RT_CID_819x_HP; + rtlhal->oem_id = RT_CID_819X_HP; } else { rtlhal->oem_id = RT_CID_DEFAULT; } @@ -1891,7 +1899,7 @@ static void _rtl88ee_read_adapter_info(struct ieee80211_hw *hw) rtlhal->oem_id = RT_CID_TOSHIBA; break; case EEPROM_CID_QMI: - rtlhal->oem_id = RT_CID_819x_QMI; + rtlhal->oem_id = RT_CID_819X_QMI; break; case EEPROM_CID_WHQL: default: @@ -1910,14 +1918,14 @@ static void _rtl88ee_hal_customized_behavior(struct ieee80211_hw *hw) pcipriv->ledctl.led_opendrain = true; switch (rtlhal->oem_id) { - case RT_CID_819x_HP: + case RT_CID_819X_HP: pcipriv->ledctl.led_opendrain = true; break; - case RT_CID_819x_Lenovo: + case RT_CID_819X_LENOVO: case RT_CID_DEFAULT: case RT_CID_TOSHIBA: case RT_CID_CCX: - case RT_CID_819x_Acer: + case RT_CID_819X_ACER: case RT_CID_WHQL: default: break; @@ -2210,8 +2218,7 @@ void rtl88ee_update_channel_access_setting(struct ieee80211_hw *hw) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 sifs_timer; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *)&mac->slot_time); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else @@ -2508,23 +2515,3 @@ void rtl88ee_suspend(struct ieee80211_hw *hw) void rtl88ee_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config = 0x%08X, write_into_reg =%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h index b4460a41bd0..1850fde881b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.h @@ -61,8 +61,6 @@ void rtl8188ee_bt_reg_init(struct ieee80211_hw *hw); void rtl8188ee_bt_hw_init(struct ieee80211_hw *hw); void rtl88ee_suspend(struct ieee80211_hw *hw); void rtl88ee_resume(struct ieee80211_hw *hw); -void rtl88ee_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); void rtl88ee_fw_clk_off_timer_callback(unsigned long data); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c index e655c047322..1cd6c16d597 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c @@ -29,6 +29,7 @@ #include "../wifi.h" #include "../pci.h" +#include "../core.h" #include "../ps.h" #include "reg.h" #include "def.h" @@ -151,18 +152,7 @@ static bool config_bb_with_pgheader(struct ieee80211_hw *hw, v2 = table_pg[i + 1]; if (v1 < 0xcdcdcdcd) { - if (table_pg[i] == 0xfe) - mdelay(50); - else if (table_pg[i] == 0xfd) - mdelay(5); - else if (table_pg[i] == 0xfc) - mdelay(1); - else if (table_pg[i] == 0xfb) - udelay(50); - else if (table_pg[i] == 0xfa) - udelay(5); - else if (table_pg[i] == 0xf9) - udelay(1); + rtl_addr_delay(table_pg[i]); store_pwrindex_offset(hw, table_pg[i], table_pg[i + 1], @@ -672,24 +662,9 @@ static void _rtl8188e_config_rf_reg(struct ieee80211_hw *hw, u32 addr, u32 data, enum radio_path rfpath, u32 regaddr) { - if (addr == 0xffe) { - mdelay(50); - } else if (addr == 0xfd) { - mdelay(5); - } else if (addr == 0xfc) { - mdelay(1); - } else if (addr == 0xfb) { - udelay(50); - } else if (addr == 0xfa) { - udelay(5); - } else if (addr == 0xf9) { - udelay(1); - } else { - rtl_set_rfreg(hw, rfpath, regaddr, - RFREG_OFFSET_MASK, - data); - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, regaddr, + RFREG_OFFSET_MASK, + data); } static void rtl88_config_s(struct ieee80211_hw *hw, @@ -702,28 +677,6 @@ static void rtl88_config_s(struct ieee80211_hw *hw, addr | maskforphyset); } -static void _rtl8188e_config_bb_reg(struct ieee80211_hw *hw, - u32 addr, u32 data) -{ - if (addr == 0xfe) { - mdelay(50); - } else if (addr == 0xfd) { - mdelay(5); - } else if (addr == 0xfc) { - mdelay(1); - } else if (addr == 0xfb) { - udelay(50); - } else if (addr == 0xfa) { - udelay(5); - } else if (addr == 0xf9) { - udelay(1); - } else { - rtl_set_bbreg(hw, addr, MASKDWORD, data); - udelay(1); - } -} - - #define NEXT_PAIR(v1, v2, i) \ do { \ i += 2; v1 = array_table[i]; \ @@ -795,7 +748,7 @@ static void set_baseband_phy_config(struct ieee80211_hw *hw) v1 = array_table[i]; v2 = array_table[i + 1]; if (v1 < 0xcdcdcdcd) { - _rtl8188e_config_bb_reg(hw, v1, v2); + rtl_bb_delay(hw, v1, v2); } else {/*This line is the start line of branch.*/ if (!check_cond(hw, array_table[i])) { /*Discard the following (offset, data) pairs*/ @@ -811,7 +764,7 @@ static void set_baseband_phy_config(struct ieee80211_hw *hw) while (v2 != 0xDEAD && v2 != 0xCDEF && v2 != 0xCDCD && i < arraylen - 2) { - _rtl8188e_config_bb_reg(hw, v1, v2); + rtl_bb_delay(hw, v1, v2); NEXT_PAIR(v1, v2, i); } @@ -1002,7 +955,7 @@ bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, } } - if (rtlhal->oem_id == RT_CID_819x_HP) + if (rtlhal->oem_id == RT_CID_819X_HP) rtl88_config_s(hw, 0x52, 0x7E4BD); break; @@ -1136,34 +1089,6 @@ void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) &bw40_pwr[0], channel); } -void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation.\n"); - break; - } - } -} - void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h index f1acd6d27e4..89f0f1ef146 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h @@ -200,37 +200,35 @@ enum _ANT_DIV_TYPE { CGCS_RX_SW_ANTDIV = 0x05, }; -extern u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -extern void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -extern u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); -extern bool rtl88e_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl88e_phy_bb_config(struct ieee80211_hw *hw); -extern bool rtl88e_phy_rf_config(struct ieee80211_hw *hw); -extern void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -extern void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); -extern void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); -extern void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); -extern void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw); -extern void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); -extern void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw); -extern u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw); -extern void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); +u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +bool rtl88e_phy_mac_config(struct ieee80211_hw *hw); +bool rtl88e_phy_bb_config(struct ieee80211_hw *hw); +bool rtl88e_phy_rf_config(struct ieee80211_hw *hw); +void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); +void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw); +u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw); +void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); void rtl88e_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl88e_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl88e_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); +bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h index 028ec6dd52b..32e135ab9a6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/pwrseq.h @@ -30,7 +30,6 @@ #ifndef __RTL8723E_PWRSEQ_H__ #define __RTL8723E_PWRSEQ_H__ -#include "pwrseqcmd.h" /* Check document WM-20110607-Paul-RTL8188E_Power_Architecture-R02.vsd There are 6 HW Power States: diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h index d849abf7d94..7af85cfa8f8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h @@ -2215,22 +2215,6 @@ #define BWORD1 0xc #define BWORD 0xf -#define MASKBYTE0 0xff -#define MASKBYTE1 0xff00 -#define MASKBYTE2 0xff0000 -#define MASKBYTE3 0xff000000 -#define MASKHWORD 0xffff0000 -#define MASKLWORD 0x0000ffff -#define MASKDWORD 0xffffffff -#define MASK12BITS 0xfff -#define MASKH4BITS 0xf0000000 -#define MASKOFDM_D 0xffc00000 -#define MASKCCK 0x3f3f3f3f - -#define MASK4BITS 0x0f -#define MASK20BITS 0xfffff -#define RFREG_OFFSET_MASK 0xfffff - #define BENABLE 0x1 #define BDISABLE 0x0 diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c index c254693a1e6..842d69349a3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -92,6 +93,7 @@ int rtl88e_init_sw_vars(struct ieee80211_hw *hw) u8 tid; rtl8188ee_bt_reg_init(hw); + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; rtlpriv->dm.dm_initialgain_enable = 1; rtlpriv->dm.dm_flag = 0; @@ -244,7 +246,7 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = { .set_bw_mode = rtl88e_phy_set_bw_mode, .switch_channel = rtl88e_phy_sw_chnl, .dm_watchdog = rtl88e_dm_watchdog, - .scan_operation_backup = rtl88e_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl88e_phy_set_rf_power_state, .led_control = rtl88ee_led_control, .set_desc = rtl88ee_set_desc, @@ -253,7 +255,6 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = { .enable_hw_sec = rtl88ee_enable_hw_security_config, .set_key = rtl88ee_set_key, .init_sw_leds = rtl88ee_init_sw_leds, - .allow_all_destaddr = rtl88ee_allow_all_destaddr, .get_bbreg = rtl88e_phy_query_bb_reg, .set_bbreg = rtl88e_phy_set_bb_reg, .get_rfreg = rtl88e_phy_query_rf_reg, @@ -265,6 +266,7 @@ static struct rtl_mod_params rtl88ee_mod_params = { .inactiveps = true, .swctrl_lps = false, .fwctrl_lps = true, + .msi_support = false, .debug = DBG_EMERG, }; @@ -381,10 +383,12 @@ module_param_named(debug, rtl88ee_mod_params.debug, int, 0444); module_param_named(ips, rtl88ee_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl88ee_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl88ee_mod_params.fwctrl_lps, bool, 0444); +module_param_named(msi, rtl88ee_mod_params.msi_support, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); +MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c index 68685a89825..5b4c225396f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c @@ -293,7 +293,7 @@ static void _rtl88ee_translate_rx_signal_stuff(struct ieee80211_hw *hw, u8 *psaddr; __le16 fc; u16 type, ufc; - bool match_bssid, packet_toself, packet_beacon, addr; + bool match_bssid, packet_toself, packet_beacon = false, addr; tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift; @@ -452,7 +452,7 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, /* During testing, hdr was NULL */ return false; } - if ((ieee80211_is_robust_mgmt_frame(hdr)) && + if ((_ieee80211_is_robust_mgmt_frame(hdr)) && (ieee80211_has_protected(hdr->frame_control))) rx_status->flag &= ~RX_FLAG_DECRYPTED; else @@ -478,7 +478,6 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, /*rx_status->qual = status->signal; */ rx_status->signal = status->recvsignalpower + 10; - /*rx_status->noise = -status->noise; */ if (status->packet_report_type == TX_REPORT2) { status->macid_valid_entry[0] = GET_RX_RPT2_DESC_MACID_VALID_1(pdesc); @@ -490,16 +489,15 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, void rtl88ee_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, - struct sk_buff *skb, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); - u8 *pdesc = (u8 *)pdesc_tx; + u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; unsigned int buf_len = 0; @@ -718,7 +716,7 @@ void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw, SET_TX_DESC_OWN(pdesc, 1); - SET_TX_DESC_PKT_SIZE((u8 *)pdesc, (u16)(skb->len)); + SET_TX_DESC_PKT_SIZE(pdesc, (u16)(skb->len)); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); @@ -735,7 +733,8 @@ void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw, pdesc, TX_DESC_SIZE); } -void rtl88ee_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) +void rtl88ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val) { if (istx == true) { switch (desc_name) { diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h index 21ca33a7c77..8c2609412d2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.h @@ -777,15 +777,15 @@ struct rx_desc_88e { void rtl88ee_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, - struct sk_buff *skb, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *status, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); -void rtl88ee_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); +void rtl88ee_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); u32 rtl88ee_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl88ee_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); void rtl88ee_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index d2d57a27a7c..eb78fd8607f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -158,6 +158,42 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} }; +static u32 power_index_reg[6] = {0xc90, 0xc91, 0xc92, 0xc98, 0xc99, 0xc9a}; + +void dm_restorepowerindex(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 index; + + for (index = 0; index < 6; index++) + rtl_write_byte(rtlpriv, power_index_reg[index], + rtlpriv->dm.powerindex_backup[index]); +} +EXPORT_SYMBOL_GPL(dm_restorepowerindex); + +void dm_writepowerindex(struct ieee80211_hw *hw, u8 value) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 index; + + for (index = 0; index < 6; index++) + rtl_write_byte(rtlpriv, power_index_reg[index], value); +} +EXPORT_SYMBOL_GPL(dm_writepowerindex); + +void dm_savepowerindex(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 index; + u8 tmp; + + for (index = 0; index < 6; index++) { + tmp = rtl_read_byte(rtlpriv, power_index_reg[index]); + rtlpriv->dm.powerindex_backup[index] = tmp; + } +} +EXPORT_SYMBOL_GPL(dm_savepowerindex); + static void rtl92c_dm_diginit(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -180,7 +216,12 @@ static void rtl92c_dm_diginit(struct ieee80211_hw *hw) dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; - dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; + dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LowRssi; + + dm_digtable->forbidden_igi = DM_DIG_MIN; + dm_digtable->large_fa_hit = 0; + dm_digtable->recover_cnt = 0; + dm_digtable->dig_dynamic_min = 0x25; } static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) @@ -206,7 +247,9 @@ static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) rssi_val_min = rtlpriv->dm.entry_min_undec_sm_pwdb; } - return (u8) rssi_val_min; + if (rssi_val_min > 100) + rssi_val_min = 100; + return (u8)rssi_val_min; } static void rtl92c_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) @@ -224,9 +267,17 @@ static void rtl92c_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD); falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff); + + ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, MASKDWORD); + falsealm_cnt->cnt_fast_fsync_fail = (ret_value & 0xffff); + falsealm_cnt->cnt_sb_search_fail = ((ret_value & 0xffff0000) >> 16); + falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + - falsealm_cnt->cnt_rate_illegal + - falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail; + falsealm_cnt->cnt_rate_illegal + + falsealm_cnt->cnt_crc8_fail + + falsealm_cnt->cnt_mcs_fail + + falsealm_cnt->cnt_fast_fsync_fail + + falsealm_cnt->cnt_sb_search_fail; rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(14), 1); ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, MASKBYTE0); @@ -271,12 +322,14 @@ static void rtl92c_dm_ctrl_initgain_by_fa(struct ieee80211_hw *hw) value_igi++; else if (rtlpriv->falsealm_cnt.cnt_all >= DM_DIG_FA_TH2) value_igi += 2; + if (value_igi > DM_DIG_FA_UPPER) value_igi = DM_DIG_FA_UPPER; else if (value_igi < DM_DIG_FA_LOWER) value_igi = DM_DIG_FA_LOWER; + if (rtlpriv->falsealm_cnt.cnt_all > 10000) - value_igi = 0x32; + value_igi = DM_DIG_FA_UPPER; dm_digtable->cur_igvalue = value_igi; rtl92c_dm_write_dig(hw); @@ -286,32 +339,80 @@ static void rtl92c_dm_ctrl_initgain_by_rssi(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *digtable = &rtlpriv->dm_digtable; + u32 isbt; + + /* modify DIG lower bound, deal with abnorally large false alarm */ + if (rtlpriv->falsealm_cnt.cnt_all > 10000) { + digtable->large_fa_hit++; + if (digtable->forbidden_igi < digtable->cur_igvalue) { + digtable->forbidden_igi = digtable->cur_igvalue; + digtable->large_fa_hit = 1; + } - if (rtlpriv->falsealm_cnt.cnt_all > digtable->fa_highthresh) { - if ((digtable->back_val - 2) < digtable->back_range_min) - digtable->back_val = digtable->back_range_min; - else - digtable->back_val -= 2; - } else if (rtlpriv->falsealm_cnt.cnt_all < digtable->fa_lowthresh) { - if ((digtable->back_val + 2) > digtable->back_range_max) - digtable->back_val = digtable->back_range_max; - else - digtable->back_val += 2; + if (digtable->large_fa_hit >= 3) { + if ((digtable->forbidden_igi + 1) > + digtable->rx_gain_max) + digtable->rx_gain_min = digtable->rx_gain_max; + else + digtable->rx_gain_min = (digtable->forbidden_igi + 1); + digtable->recover_cnt = 3600; /* 3600=2hr */ + } + } else { + /* Recovery mechanism for IGI lower bound */ + if (digtable->recover_cnt != 0) { + digtable->recover_cnt--; + } else { + if (digtable->large_fa_hit == 0) { + if ((digtable->forbidden_igi-1) < DM_DIG_MIN) { + digtable->forbidden_igi = DM_DIG_MIN; + digtable->rx_gain_min = DM_DIG_MIN; + } else { + digtable->forbidden_igi--; + digtable->rx_gain_min = digtable->forbidden_igi + 1; + } + } else if (digtable->large_fa_hit == 3) { + digtable->large_fa_hit = 0; + } + } + } + if (rtlpriv->falsealm_cnt.cnt_all < 250) { + isbt = rtl_read_byte(rtlpriv, 0x4fd) & 0x01; + + if (!isbt) { + if (rtlpriv->falsealm_cnt.cnt_all > + digtable->fa_lowthresh) { + if ((digtable->back_val - 2) < + digtable->back_range_min) + digtable->back_val = digtable->back_range_min; + else + digtable->back_val -= 2; + } else if (rtlpriv->falsealm_cnt.cnt_all < + digtable->fa_lowthresh) { + if ((digtable->back_val + 2) > + digtable->back_range_max) + digtable->back_val = digtable->back_range_max; + else + digtable->back_val += 2; + } + } else { + digtable->back_val = DM_DIG_BACKOFF_DEFAULT; + } + } else { + /* Adjust initial gain by false alarm */ + if (rtlpriv->falsealm_cnt.cnt_all > 1000) + digtable->cur_igvalue = digtable->pre_igvalue + 2; + else if (rtlpriv->falsealm_cnt.cnt_all > 750) + digtable->cur_igvalue = digtable->pre_igvalue + 1; + else if (rtlpriv->falsealm_cnt.cnt_all < 500) + digtable->cur_igvalue = digtable->pre_igvalue - 1; } - if ((digtable->rssi_val_min + 10 - digtable->back_val) > - digtable->rx_gain_max) + /* Check initial gain by upper/lower bound */ + if (digtable->cur_igvalue > digtable->rx_gain_max) digtable->cur_igvalue = digtable->rx_gain_max; - else if ((digtable->rssi_val_min + 10 - - digtable->back_val) < digtable->rx_gain_min) - digtable->cur_igvalue = digtable->rx_gain_min; - else - digtable->cur_igvalue = digtable->rssi_val_min + 10 - - digtable->back_val; - RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, - "rssi_val_min = %x back_val %x\n", - digtable->rssi_val_min, digtable->back_val); + if (digtable->cur_igvalue < digtable->rx_gain_min) + digtable->cur_igvalue = digtable->rx_gain_min; rtl92c_dm_write_dig(hw); } @@ -329,7 +430,7 @@ static void rtl92c_dm_initial_gain_multi_sta(struct ieee80211_hw *hw) multi_sta = true; if (!multi_sta || - dm_digtable->cursta_cstate != DIG_STA_DISCONNECT) { + dm_digtable->cursta_cstate == DIG_STA_DISCONNECT) { initialized = false; dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; return; @@ -375,7 +476,6 @@ static void rtl92c_dm_initial_gain_sta(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "presta_cstate = %x, cursta_cstate = %x\n", dm_digtable->presta_cstate, dm_digtable->cursta_cstate); - if (dm_digtable->presta_cstate == dm_digtable->cursta_cstate || dm_digtable->cursta_cstate == DIG_STA_BEFORE_CONNECT || dm_digtable->cursta_cstate == DIG_STA_CONNECT) { @@ -383,6 +483,8 @@ static void rtl92c_dm_initial_gain_sta(struct ieee80211_hw *hw) if (dm_digtable->cursta_cstate != DIG_STA_DISCONNECT) { dm_digtable->rssi_val_min = rtl92c_dm_initial_gain_min_pwdb(hw); + if (dm_digtable->rssi_val_min > 100) + dm_digtable->rssi_val_min = 100; rtl92c_dm_ctrl_initgain_by_rssi(hw); } } else { @@ -398,11 +500,12 @@ static void rtl92c_dm_initial_gain_sta(struct ieee80211_hw *hw) static void rtl92c_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; if (dm_digtable->cursta_cstate == DIG_STA_CONNECT) { dm_digtable->rssi_val_min = rtl92c_dm_initial_gain_min_pwdb(hw); + if (dm_digtable->rssi_val_min > 100) + dm_digtable->rssi_val_min = 100; if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { if (dm_digtable->rssi_val_min <= 25) @@ -424,48 +527,14 @@ static void rtl92c_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) } if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { - if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) { - if (rtlpriv->falsealm_cnt.cnt_cck_fail > 800) - dm_digtable->cur_cck_fa_state = - CCK_FA_STAGE_High; - else - dm_digtable->cur_cck_fa_state = CCK_FA_STAGE_Low; - - if (dm_digtable->pre_cck_fa_state != - dm_digtable->cur_cck_fa_state) { - if (dm_digtable->cur_cck_fa_state == - CCK_FA_STAGE_Low) - rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, - 0x83); - else - rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, - 0xcd); - - dm_digtable->pre_cck_fa_state = - dm_digtable->cur_cck_fa_state; - } - - rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x40); - - if (IS_92C_SERIAL(rtlhal->version)) - rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, - MASKBYTE2, 0xd7); - } else { + if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) || + (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_MAX)) + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83); + else rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); - rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x47); - if (IS_92C_SERIAL(rtlhal->version)) - rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, - MASKBYTE2, 0xd3); - } dm_digtable->pre_cck_pd_state = dm_digtable->cur_cck_pd_state; } - - RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "CCKPDStage=%x\n", - dm_digtable->cur_cck_pd_state); - - RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, "is92C=%x\n", - IS_92C_SERIAL(rtlhal->version)); } static void rtl92c_dm_ctrl_initgain_by_twoport(struct ieee80211_hw *hw) @@ -482,6 +551,8 @@ static void rtl92c_dm_ctrl_initgain_by_twoport(struct ieee80211_hw *hw) else dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; + dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; + rtl92c_dm_initial_gain_sta(hw); rtl92c_dm_initial_gain_multi_sta(hw); rtl92c_dm_cck_packet_detection_thresh(hw); @@ -493,23 +564,26 @@ static void rtl92c_dm_ctrl_initgain_by_twoport(struct ieee80211_hw *hw) static void rtl92c_dm_dig(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; if (rtlpriv->dm.dm_initialgain_enable == false) return; - if (dm_digtable->dig_enable_flag == false) + if (!(rtlpriv->dm.dm_flag & DYNAMIC_FUNC_DIG)) return; rtl92c_dm_ctrl_initgain_by_twoport(hw); - } static void rtl92c_dm_init_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); - rtlpriv->dm.dynamic_txpower_enable = false; - + if (rtlpriv->rtlhal.interface == INTF_USB && + rtlpriv->rtlhal.board_type & 0x1) { + dm_savepowerindex(hw); + rtlpriv->dm.dynamic_txpower_enable = true; + } else { + rtlpriv->dm.dynamic_txpower_enable = false; + } rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; } @@ -524,9 +598,14 @@ void rtl92c_dm_write_dig(struct ieee80211_hw *hw) dm_digtable->cur_igvalue, dm_digtable->pre_igvalue, dm_digtable->back_val); - dm_digtable->cur_igvalue += 2; - if (dm_digtable->cur_igvalue > 0x3f) - dm_digtable->cur_igvalue = 0x3f; + if (rtlpriv->rtlhal.interface == INTF_USB && + !dm_digtable->dig_enable_flag) { + dm_digtable->pre_igvalue = 0x17; + return; + } + dm_digtable->cur_igvalue -= 1; + if (dm_digtable->cur_igvalue < DM_DIG_MIN) + dm_digtable->cur_igvalue = DM_DIG_MIN; if (dm_digtable->pre_igvalue != dm_digtable->cur_igvalue) { rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f, @@ -536,34 +615,47 @@ void rtl92c_dm_write_dig(struct ieee80211_hw *hw) dm_digtable->pre_igvalue = dm_digtable->cur_igvalue; } + RT_TRACE(rtlpriv, COMP_DIG, DBG_WARNING, + "dig values 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + dm_digtable->cur_igvalue, dm_digtable->pre_igvalue, + dm_digtable->rssi_val_min, dm_digtable->back_val, + dm_digtable->rx_gain_max, dm_digtable->rx_gain_min, + dm_digtable->large_fa_hit, dm_digtable->forbidden_igi); } EXPORT_SYMBOL(rtl92c_dm_write_dig); static void rtl92c_dm_pwdb_monitor(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); long tmpentry_max_pwdb = 0, tmpentry_min_pwdb = 0xff; - u8 h2c_parameter[3] = { 0 }; + if (mac->link_state != MAC80211_LINKED) + return; - return; + if (mac->opmode == NL80211_IFTYPE_ADHOC || + mac->opmode == NL80211_IFTYPE_AP) { + /* TODO: Handle ADHOC and AP Mode */ + } - if (tmpentry_max_pwdb != 0) { + if (tmpentry_max_pwdb != 0) rtlpriv->dm.entry_max_undec_sm_pwdb = tmpentry_max_pwdb; - } else { + else rtlpriv->dm.entry_max_undec_sm_pwdb = 0; - } - if (tmpentry_min_pwdb != 0xff) { + if (tmpentry_min_pwdb != 0xff) rtlpriv->dm.entry_min_undec_sm_pwdb = tmpentry_min_pwdb; - } else { + else rtlpriv->dm.entry_min_undec_sm_pwdb = 0; - } - h2c_parameter[2] = (u8) (rtlpriv->dm.undec_sm_pwdb & 0xFF); - h2c_parameter[0] = 0; - - rtl92c_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, h2c_parameter); +/* TODO: + * if (mac->opmode == NL80211_IFTYPE_STATION) { + * if (rtlpriv->rtlhal.fw_ready) { + * u32 param = (u32)(rtlpriv->dm.undec_sm_pwdb << 16); + * rtl8192c_set_rssi_cmd(hw, param); + * } + * } + */ } void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw) @@ -673,7 +765,7 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw s8 cck_index = 0; int i; bool is2t = IS_92C_SERIAL(rtlhal->version); - s8 txpwr_level[2] = {0, 0}; + s8 txpwr_level[3] = {0, 0, 0}; u8 ofdm_min_index = 6, rf; rtlpriv->dm.txpower_trackinginit = true; @@ -773,6 +865,7 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw rtlpriv->dm.ofdm_index[i] = ofdm_index_old[i]; rtlpriv->dm.cck_index = cck_index_old; } + /* Handle USB High PA boards */ delta = (thermalvalue > rtlpriv->dm.thermalvalue) ? (thermalvalue - rtlpriv->dm.thermalvalue) : @@ -1163,22 +1256,22 @@ void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct ps_t *dm_pstable = &rtlpriv->dm_pstable; - static u8 initialize; - static u32 reg_874, reg_c70, reg_85c, reg_a74; - if (initialize == 0) { - reg_874 = (rtl_get_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, - MASKDWORD) & 0x1CC000) >> 14; + if (!rtlpriv->reg_init) { + rtlpriv->reg_874 = (rtl_get_bbreg(hw, + RFPGA0_XCD_RFINTERFACESW, + MASKDWORD) & 0x1CC000) >> 14; - reg_c70 = (rtl_get_bbreg(hw, ROFDM0_AGCPARAMETER1, - MASKDWORD) & BIT(3)) >> 3; + rtlpriv->reg_c70 = (rtl_get_bbreg(hw, ROFDM0_AGCPARAMETER1, + MASKDWORD) & BIT(3)) >> 3; - reg_85c = (rtl_get_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, - MASKDWORD) & 0xFF000000) >> 24; + rtlpriv->reg_85c = (rtl_get_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, + MASKDWORD) & 0xFF000000) >> 24; - reg_a74 = (rtl_get_bbreg(hw, 0xa74, MASKDWORD) & 0xF000) >> 12; + rtlpriv->reg_a74 = (rtl_get_bbreg(hw, 0xa74, MASKDWORD) & + 0xF000) >> 12; - initialize = 1; + rtlpriv->reg_init = true; } if (!bforce_in_normal) { @@ -1215,12 +1308,12 @@ void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal) rtl_set_bbreg(hw, 0x818, BIT(28), 0x1); } else { rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, - 0x1CC000, reg_874); + 0x1CC000, rtlpriv->reg_874); rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, BIT(3), - reg_c70); + rtlpriv->reg_c70); rtl_set_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, 0xFF000000, - reg_85c); - rtl_set_bbreg(hw, 0xa74, 0xF000, reg_a74); + rtlpriv->reg_85c); + rtl_set_bbreg(hw, 0xa74, 0xF000, rtlpriv->reg_a74); rtl_set_bbreg(hw, 0x818, BIT(28), 0x0); } @@ -1236,6 +1329,7 @@ static void rtl92c_dm_dynamic_bb_powersaving(struct ieee80211_hw *hw) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + /* Determine the minimum RSSI */ if (((mac->link_state == MAC80211_NOLINK)) && (rtlpriv->dm.entry_min_undec_sm_pwdb == 0)) { dm_pstable->rssi_val_min = 0; @@ -1264,6 +1358,7 @@ static void rtl92c_dm_dynamic_bb_powersaving(struct ieee80211_hw *hw) dm_pstable->rssi_val_min); } + /* Power Saving for 92C */ if (IS_92C_SERIAL(rtlhal->version)) ;/* rtl92c_dm_1r_cca(hw); */ else @@ -1275,12 +1370,23 @@ void rtl92c_dm_init(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; + rtlpriv->dm.dm_flag = DYNAMIC_FUNC_DISABLE | DYNAMIC_FUNC_DIG; + rtlpriv->dm.undec_sm_pwdb = -1; + rtlpriv->dm.undec_sm_cck = -1; + rtlpriv->dm.dm_initialgain_enable = true; rtl92c_dm_diginit(hw); + + rtlpriv->dm.dm_flag |= HAL_DM_HIPWR_DISABLE; rtl92c_dm_init_dynamic_txpower(hw); + rtl92c_dm_init_edca_turbo(hw); rtl92c_dm_init_rate_adaptive_mask(hw); + rtlpriv->dm.dm_flag |= DYNAMIC_FUNC_SS; rtl92c_dm_initialize_txpower_tracking(hw); rtl92c_dm_init_dynamic_bb_powersaving(hw); + + rtlpriv->dm.ofdm_pkt_cnt = 0; + rtlpriv->dm.dm_rssi_sel = RSSI_DEFAULT; } EXPORT_SYMBOL(rtl92c_dm_init); @@ -1331,7 +1437,7 @@ void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw) } if (undec_sm_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) { - rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL2; RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"); } else if ((undec_sm_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) && @@ -1351,8 +1457,16 @@ void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw) "PHY_SetTxPowerLevel8192S() Channel = %d\n", rtlphy->current_channel); rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); + if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_NORMAL) + dm_restorepowerindex(hw); + else if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_LEVEL1) + dm_writepowerindex(hw, 0x14); + else if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_LEVEL2) + dm_writepowerindex(hw, 0x10); } - rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } @@ -1423,12 +1537,6 @@ u8 rtl92c_bt_rssi_state_change(struct ieee80211_hw *hw) else curr_bt_rssi_state &= (~BT_RSSI_STATE_SPECIAL_LOW); - /* Set Tx Power according to BT status. */ - if (undec_sm_pwdb >= 30) - curr_bt_rssi_state |= BT_RSSI_STATE_TXPOWER_LOW; - else if (undec_sm_pwdb < 25) - curr_bt_rssi_state &= (~BT_RSSI_STATE_TXPOWER_LOW); - /* Check BT state related to BT_Idle in B/G mode. */ if (undec_sm_pwdb < 15) curr_bt_rssi_state |= BT_RSSI_STATE_BG_EDCA_LOW; diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h index 518e208c018..4f232a06363 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h @@ -91,6 +91,17 @@ #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 +#define DYNAMIC_FUNC_DISABLE 0x0 +#define DYNAMIC_FUNC_DIG BIT(0) +#define DYNAMIC_FUNC_HP BIT(1) +#define DYNAMIC_FUNC_SS BIT(2) /*Tx Power Tracking*/ +#define DYNAMIC_FUNC_BT BIT(3) +#define DYNAMIC_FUNC_ANT_DIV BIT(4) + +#define RSSI_CCK 0 +#define RSSI_OFDM 1 +#define RSSI_DEFAULT 2 + struct swat_t { u8 failure_cnt; u8 try_flag; @@ -167,5 +178,8 @@ void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw); void rtl92c_dm_bt_coexist(struct ieee80211_hw *hw); +void dm_savepowerindex(struct ieee80211_hw *hw); +void dm_writepowerindex(struct ieee80211_hw *hw, u8 value); +void dm_restorepowerindex(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 246e5352f2e..9e32ac8a442 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -592,36 +592,6 @@ long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, } EXPORT_SYMBOL(_rtl92c_phy_txpwr_idx_to_dbm); -void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation\n"); - break; - } - } -} -EXPORT_SYMBOL(rtl92c_phy_scan_operation_backup); - void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { @@ -1177,6 +1147,12 @@ static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, 0x522, 0x550, 0x551, 0x040 }; + u32 iqk_bb_reg_92C[9] = { + 0xc04, 0xc08, 0x874, 0xb68, + 0xb6c, 0x870, 0x860, 0x864, + 0x800 + }; + const u32 retrycount = 2; if (t == 0) { @@ -1187,6 +1163,8 @@ static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, rtlphy->adda_backup, 16); _rtl92c_phy_save_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); + _rtl92c_phy_save_adda_registers(hw, iqk_bb_reg_92C, + rtlphy->iqk_bb_backup, 9); } _rtl92c_phy_path_adda_on(hw, adda_reg, true, is2t); if (t == 0) { @@ -1197,14 +1175,18 @@ static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, if (!rtlphy->rfpi_enable) _rtl92c_phy_pi_mode_switch(hw, true); - if (t == 0) { - rtlphy->reg_c04 = rtl_get_bbreg(hw, 0xc04, MASKDWORD); - rtlphy->reg_c08 = rtl_get_bbreg(hw, 0xc08, MASKDWORD); - rtlphy->reg_874 = rtl_get_bbreg(hw, 0x874, MASKDWORD); - } + + rtl_set_bbreg(hw, 0x800, BIT(24), 0x0); + rtl_set_bbreg(hw, 0xc04, MASKDWORD, 0x03a05600); rtl_set_bbreg(hw, 0xc08, MASKDWORD, 0x000800e4); rtl_set_bbreg(hw, 0x874, MASKDWORD, 0x22204000); + + rtl_set_bbreg(hw, 0x870, BIT(10), 0x1); + rtl_set_bbreg(hw, 0x870, BIT(26), 0x1); + rtl_set_bbreg(hw, 0x860, BIT(10), 0x0); + rtl_set_bbreg(hw, 0x864, BIT(10), 0x0); + if (is2t) { rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00010000); @@ -1269,13 +1251,9 @@ static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, 0x3FF0000) >> 16; } } - rtl_set_bbreg(hw, 0xc04, MASKDWORD, rtlphy->reg_c04); - rtl_set_bbreg(hw, 0x874, MASKDWORD, rtlphy->reg_874); - rtl_set_bbreg(hw, 0xc08, MASKDWORD, rtlphy->reg_c08); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0); - rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00032ed3); - if (is2t) - rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3); + if (t != 0) { if (!rtlphy->rfpi_enable) _rtl92c_phy_pi_mode_switch(hw, false); @@ -1283,6 +1261,15 @@ static void _rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, rtlphy->adda_backup, 16); _rtl92c_phy_reload_mac_registers(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); + _rtl92c_phy_reload_adda_registers(hw, iqk_bb_reg_92C, + rtlphy->iqk_bb_backup, 9); + + rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00032ed3); + if (is2t) + rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3); + + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x01008c00); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x01008c00); } } diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h index cec10d69649..e79dabe9ba1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h @@ -39,9 +39,7 @@ #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 -#define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 -#define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 @@ -205,8 +203,6 @@ void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm); -void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h index 3cfa1bb0f47..fa24de43ce7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h @@ -152,8 +152,6 @@ enum version_8192c { #define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) #define IS_CHIP_VER_B(version) ((version & CHIP_VER_B) ? true : false) -#define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ - ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) #define IS_92C_SERIAL(version) ((version & CHIP_92C_BITMASK) ? true : false) #define IS_CHIP_VENDOR_UMC(version) \ ((version & CHIP_VENDOR_UMC) ? true : false) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index a82b30a1996..cdecb0fd4d8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -319,7 +319,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 e_aci = *(val); rtl92c_dm_init_edca_turbo(hw); - if (rtlpci->acm_method != eAcmWay2_SW) + if (rtlpci->acm_method != EACMWAY2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, (&e_aci)); @@ -476,7 +476,7 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) break; } case HW_VAR_H2C_FW_P2P_PS_OFFLOAD: - rtl92c_set_p2p_ps_offload_cmd(hw, (*(u8 *)val)); + rtl92c_set_p2p_ps_offload_cmd(hw, *val); break; case HW_VAR_AID:{ u16 u2btmp; @@ -521,30 +521,32 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) (u8 *)(&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&ppsc->fwctrl_psmode)); + &ppsc->fwctrl_psmode); rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + HW_VAR_SET_RPWM, + &rpwm_val); } else { rpwm_val = 0x0C; /* RF on */ fw_pwrmode = FW_PS_ACTIVE_MODE; fw_current_inps = false; rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + HW_VAR_SET_RPWM, + &rpwm_val); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&fw_pwrmode)); + &fw_pwrmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, (u8 *)(&fw_current_inps)); } break; } + case HW_VAR_KEEP_ALIVE: + break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "switch case not processed\n"); + "switch case %d not processed\n", variable); break; } } @@ -937,14 +939,26 @@ int rtl92ce_hw_init(struct ieee80211_hw *hw) bool is92c; int err; u8 tmp_u1b; + unsigned long flags; rtlpci->being_init_adapter = true; + + /* Since this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); + rtlpriv->intf_ops->disable_aspm(hw); rtstatus = _rtl92ce_init_mac(hw); if (!rtstatus) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); err = 1; - return err; + goto exit; } err = rtl92c_download_fw(hw); @@ -952,7 +966,7 @@ int rtl92ce_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; - return err; + goto exit; } rtlhal->last_hmeboxnum = 0; @@ -1032,6 +1046,8 @@ int rtl92ce_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n"); } rtl92c_dm_init(hw); +exit: + local_irq_restore(flags); rtlpci->being_init_adapter = false; return err; } @@ -1200,11 +1216,13 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, void rtl92ce_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR); + u32 reg_rcr; if (rtlpriv->psc.rfpwr_state != ERFON) return; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); + if (check_bssid) { reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, @@ -1720,7 +1738,7 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw) if (rtlefuse->eeprom_did == 0x8176) { if ((rtlefuse->eeprom_svid == 0x103C && rtlefuse->eeprom_smid == 0x1629)) - rtlhal->oem_id = RT_CID_819x_HP; + rtlhal->oem_id = RT_CID_819X_HP; else rtlhal->oem_id = RT_CID_DEFAULT; } else { @@ -1731,7 +1749,7 @@ static void _rtl92ce_read_adapter_info(struct ieee80211_hw *hw) rtlhal->oem_id = RT_CID_TOSHIBA; break; case EEPROM_CID_QMI: - rtlhal->oem_id = RT_CID_819x_QMI; + rtlhal->oem_id = RT_CID_819X_QMI; break; case EEPROM_CID_WHQL: default: @@ -1750,14 +1768,14 @@ static void _rtl92ce_hal_customized_behavior(struct ieee80211_hw *hw) struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); switch (rtlhal->oem_id) { - case RT_CID_819x_HP: + case RT_CID_819X_HP: pcipriv->ledctl.led_opendrain = true; break; - case RT_CID_819x_Lenovo: + case RT_CID_819X_LENOVO: case RT_CID_DEFAULT: case RT_CID_TOSHIBA: case RT_CID_CCX: - case RT_CID_819x_Acer: + case RT_CID_819X_ACER: case RT_CID_WHQL: default: break; @@ -2405,24 +2423,3 @@ void rtl92ce_suspend(struct ieee80211_hw *hw) void rtl92ce_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) {/* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - } else {/* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - } - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config=0x%08X, write_into_reg=%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h index 2d063b0c776..5533070f266 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h @@ -76,7 +76,5 @@ void rtl8192ce_bt_reg_init(struct ieee80211_hw *hw); void rtl8192ce_bt_hw_init(struct ieee80211_hw *hw); void rtl92ce_suspend(struct ieee80211_hw *hw); void rtl92ce_resume(struct ieee80211_hw *hw); -void rtl92ce_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c index 73262ca3864..98b22303c84 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../pci.h" #include "../ps.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "hw.h" @@ -198,18 +199,7 @@ bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, } if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { - if (phy_regarray_table[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table[i] == 0xfb) - udelay(50); - else if (phy_regarray_table[i] == 0xfa) - udelay(5); - else if (phy_regarray_table[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table[i]); rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, phy_regarray_table[i + 1]); udelay(1); @@ -245,18 +235,7 @@ bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { - if (phy_regarray_table_pg[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table_pg[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table_pg[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table_pg[i] == 0xfb) - udelay(50); - else if (phy_regarray_table_pg[i] == 0xfa) - udelay(5); - else if (phy_regarray_table_pg[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table_pg[i]); _rtl92c_store_pwrIndex_diffrate_offset(hw, phy_regarray_table_pg[i], @@ -305,46 +284,16 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { - if (radioa_array_table[i] == 0xfe) - mdelay(50); - else if (radioa_array_table[i] == 0xfd) - mdelay(5); - else if (radioa_array_table[i] == 0xfc) - mdelay(1); - else if (radioa_array_table[i] == 0xfb) - udelay(50); - else if (radioa_array_table[i] == 0xfa) - udelay(5); - else if (radioa_array_table[i] == 0xf9) - udelay(1); - else { - rtl_set_rfreg(hw, rfpath, radioa_array_table[i], - RFREG_OFFSET_MASK, - radioa_array_table[i + 1]); - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radioa_array_table[i], + RFREG_OFFSET_MASK, + radioa_array_table[i + 1]); } break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { - if (radiob_array_table[i] == 0xfe) { - mdelay(50); - } else if (radiob_array_table[i] == 0xfd) - mdelay(5); - else if (radiob_array_table[i] == 0xfc) - mdelay(1); - else if (radiob_array_table[i] == 0xfb) - udelay(50); - else if (radiob_array_table[i] == 0xfa) - udelay(5); - else if (radiob_array_table[i] == 0xf9) - udelay(1); - else { - rtl_set_rfreg(hw, rfpath, radiob_array_table[i], - RFREG_OFFSET_MASK, - radiob_array_table[i + 1]); - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radiob_array_table[i], + RFREG_OFFSET_MASK, + radiob_array_table[i + 1]); } break; case RF90_PATH_C: @@ -355,6 +304,8 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; + default: + break; } return true; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h index d5e3b704f93..94486cca400 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h @@ -39,9 +39,7 @@ #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 -#define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 -#define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 @@ -188,36 +186,29 @@ struct tx_power_struct { }; bool rtl92c_phy_bb_config(struct ieee80211_hw *hw); -u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); +u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); +void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, + u32 data); +u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 regaddr, u32 bitmask); +void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 regaddr, u32 bitmask, u32 data); bool rtl92c_phy_mac_config(struct ieee80211_hw *hw); bool rtl92ce_phy_bb_config(struct ieee80211_hw *hw); bool rtl92c_phy_rf_config(struct ieee80211_hw *hw); bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, - enum radio_path rfpath); + enum radio_path rfpath); void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); +void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel); void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm); -void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); + enum nl80211_channel_type ch_type); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw); void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); -void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, - u16 beaconinterval); +void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, u16 beaconinterval); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); @@ -225,28 +216,25 @@ void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, - u32 rfpath); -bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); + u32 rfpath); bool rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); + enum rf_pwrstate rfpwr_state); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); void rtl92c_phy_set_io(struct ieee80211_hw *hw); void rtl92c_bb_block_on(struct ieee80211_hw *hw); -u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); +u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 offset); u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); + enum radio_path rfpath, u32 offset); u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, - u32 data); + enum radio_path rfpath, u32 offset, u32 data); void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, - u32 data); + enum radio_path rfpath, u32 offset, + u32 data); void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, - u32 data); + u32 regaddr, u32 bitmask, u32 data); bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h index bd4aef74c05..ed703a1b3b7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h @@ -560,7 +560,6 @@ #define EEPROM_DEFAULT_TXPOWERLEVEL 0x22 #define EEPROM_DEFAULT_HT40_2SDIFF 0x0 #define EEPROM_DEFAULT_HT20_DIFF 2 -#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 #define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0 #define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0 @@ -639,17 +638,8 @@ #define EEPROM_TXPWR_GROUP 0x6F -#define EEPROM_TSSI_A 0x76 -#define EEPROM_TSSI_B 0x77 -#define EEPROM_THERMAL_METER 0x78 - #define EEPROM_CHANNELPLAN 0x75 -#define RF_OPTION1 0x79 -#define RF_OPTION2 0x7A -#define RF_OPTION3 0x7B -#define RF_OPTION4 0x7C - #define STOPBECON BIT(6) #define STOPHIGHT BIT(5) #define STOPMGT BIT(4) @@ -689,13 +679,6 @@ #define RSV_CTRL 0x001C #define RD_CTRL 0x0524 -#define REG_USB_INFO 0xFE17 -#define REG_USB_SPECIAL_OPTION 0xFE55 - -#define REG_USB_DMA_AGG_TO 0xFE5B -#define REG_USB_AGG_TO 0xFE5C -#define REG_USB_AGG_TH 0xFE5D - #define REG_USB_VID 0xFE60 #define REG_USB_PID 0xFE62 #define REG_USB_OPTIONAL 0xFE64 @@ -1196,9 +1179,6 @@ #define POLLING_LLT_THRESHOLD 20 #define POLLING_READY_TIMEOUT_COUNT 1000 -#define MAX_MSS_DENSITY_2T 0x13 -#define MAX_MSS_DENSITY_1T 0x0A - #define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6)) #define EPROM_CMD_CONFIG 0x3 #define EPROM_CMD_LOAD 1 @@ -2064,22 +2044,6 @@ #define BWORD1 0xc #define BWORD 0xf -#define MASKBYTE0 0xff -#define MASKBYTE1 0xff00 -#define MASKBYTE2 0xff0000 -#define MASKBYTE3 0xff000000 -#define MASKHWORD 0xffff0000 -#define MASKLWORD 0x0000ffff -#define MASKDWORD 0xffffffff -#define MASK12BITS 0xfff -#define MASKH4BITS 0xf0000000 -#define MASKOFDM_D 0xffc00000 -#define MASKCCK 0x3f3f3f3f - -#define MASK4BITS 0x0f -#define MASK20BITS 0xfffff -#define RFREG_OFFSET_MASK 0xfffff - #define BENABLE 0x1 #define BDISABLE 0x0 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h index 6c8d56efcea..d8fe68b389d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h @@ -34,11 +34,10 @@ #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 -extern void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); -extern bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw); +void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index 14203561b6e..12f21f4073e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -219,7 +220,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = { .set_bw_mode = rtl92c_phy_set_bw_mode, .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, - .scan_operation_backup = rtl92c_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl92c_phy_set_rf_power_state, .led_control = rtl92ce_led_control, .set_desc = rtl92ce_set_desc, @@ -228,7 +229,6 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = { .enable_hw_sec = rtl92ce_enable_hw_security_config, .set_key = rtl92ce_set_key, .init_sw_leds = rtl92ce_init_sw_leds, - .allow_all_destaddr = rtl92ce_allow_all_destaddr, .get_bbreg = rtl92c_phy_query_bb_reg, .set_bbreg = rtl92c_phy_set_bb_reg, .set_rfreg = rtl92ce_phy_set_rf_reg, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 6ad23b413eb..8f04817cb7e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -393,7 +393,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, /* In testing, hdr was NULL here */ return false; } - if ((ieee80211_is_robust_mgmt_frame(hdr)) && + if ((_ieee80211_is_robust_mgmt_frame(hdr)) && (ieee80211_has_protected(hdr->frame_control))) rx_status->flag &= ~RX_FLAG_DECRYPTED; else @@ -420,14 +420,13 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, /*rx_status->qual = stats->signal; */ rx_status->signal = stats->recvsignalpower + 10; - /*rx_status->noise = -stats->noise; */ return true; } void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *tcb_desc) @@ -667,7 +666,8 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, "H2C Tx Cmd Content", pdesc, TX_DESC_SIZE); } -void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) +void rtl92ce_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val) { if (istx) { switch (desc_name) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h index a7cdd514cb2..9a39ec4204d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h @@ -711,8 +711,8 @@ struct rx_desc_92c { } __packed; void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, - struct ieee80211_hdr *hdr, - u8 *pdesc, struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 *pdesc, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); @@ -720,7 +720,8 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); -void rtl92ce_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); +void rtl92ce_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); u32 rtl92ce_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl92ce_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c index 16a0b9e59ac..c16209a336e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c @@ -101,6 +101,15 @@ void rtl92cu_dm_dynamic_txpower(struct ieee80211_hw *hw) "PHY_SetTxPowerLevel8192S() Channel = %d\n", rtlphy->current_channel); rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); + if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_NORMAL) + dm_restorepowerindex(hw); + else if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_LEVEL1) + dm_writepowerindex(hw, 0x14); + else if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_LEVEL2) + dm_writepowerindex(hw, 0x10); } rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h index d947e7d350b..fafa6bac2a3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h @@ -30,3 +30,6 @@ #include "../rtl8192ce/dm.h" void rtl92cu_dm_dynamic_txpower(struct ieee80211_hw *hw); +void dm_savepowerindex(struct ieee80211_hw *hw); +void dm_writepowerindex(struct ieee80211_hw *hw, u8 value); +void dm_restorepowerindex(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index 189ba124a8c..a903c2671b4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -394,7 +394,7 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw) if (rtlefuse->eeprom_did == 0x8176) { if ((rtlefuse->eeprom_svid == 0x103C && rtlefuse->eeprom_smid == 0x1629)) - rtlhal->oem_id = RT_CID_819x_HP; + rtlhal->oem_id = RT_CID_819X_HP; else rtlhal->oem_id = RT_CID_DEFAULT; } else { @@ -405,7 +405,7 @@ static void _rtl92cu_read_adapter_info(struct ieee80211_hw *hw) rtlhal->oem_id = RT_CID_TOSHIBA; break; case EEPROM_CID_QMI: - rtlhal->oem_id = RT_CID_819x_QMI; + rtlhal->oem_id = RT_CID_819X_QMI; break; case EEPROM_CID_WHQL: default: @@ -423,14 +423,14 @@ static void _rtl92cu_hal_customized_behavior(struct ieee80211_hw *hw) struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); switch (rtlhal->oem_id) { - case RT_CID_819x_HP: + case RT_CID_819X_HP: usb_priv->ledctl.led_opendrain = true; break; - case RT_CID_819x_Lenovo: + case RT_CID_819X_LENOVO: case RT_CID_DEFAULT: case RT_CID_TOSHIBA: case RT_CID_CCX: - case RT_CID_819x_Acer: + case RT_CID_819X_ACER: case RT_CID_WHQL: default: break; @@ -511,7 +511,7 @@ static int _rtl92cu_init_power_on(struct ieee80211_hw *hw) pr_info("MAC auto ON okay!\n"); break; } - if (pollingCount++ > 100) { + if (pollingCount++ > 1000) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "Failed to polling REG_APS_FSMCO[APFM_ONMAC] done!\n"); return -ENODEV; @@ -985,19 +985,30 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); int err = 0; static bool iqk_initialized; + unsigned long flags; + + /* As this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); rtlhal->hw_type = HARDWARE_TYPE_RTL8192CU; err = _rtl92cu_init_mac(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "init mac failed!\n"); - return err; + goto exit; } err = rtl92c_download_fw(hw); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; - return err; + goto exit; } rtlhal->last_hmeboxnum = 0; /* h2c */ _rtl92cu_phy_param_tab_init(hw); @@ -1022,7 +1033,7 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) if (ppsc->rfpwr_state == ERFON) { rtl92c_phy_set_rfpath_switch(hw, 1); if (iqk_initialized) { - rtl92c_phy_iq_calibrate(hw, false); + rtl92c_phy_iq_calibrate(hw, true); } else { rtl92c_phy_iq_calibrate(hw, false); iqk_initialized = true; @@ -1034,6 +1045,8 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) _InitPABias(hw); _update_mac_setting(hw); rtl92c_dm_init(hw); +exit: + local_irq_restore(flags); return err; } @@ -1379,11 +1392,13 @@ void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtlpriv); - u32 reg_rcr = rtl_read_dword(rtlpriv, REG_RCR); + u32 reg_rcr; if (rtlpriv->psc.rfpwr_state != ERFON) return; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); + if (check_bssid) { u8 tmp; if (IS_NORMAL_CHIP(rtlhal->version)) { @@ -1795,7 +1810,7 @@ void rtl92cu_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) e_aci); break; } - if (rtlusb->acm_method != eAcmWay2_SW) + if (rtlusb->acm_method != EACMWAY2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, &e_aci); break; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c index da4f587199e..e26312fb435 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c @@ -32,6 +32,7 @@ #include "../usb.h" #include "../ps.h" #include "../cam.h" +#include "../stats.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -738,16 +739,6 @@ static u8 _rtl92c_evm_db_to_percentage(char value) return ret_val; } -static long _rtl92c_translate_todbm(struct ieee80211_hw *hw, - u8 signal_strength_index) -{ - long signal_power; - - signal_power = (long)((signal_strength_index + 1) >> 1); - signal_power -= 95; - return signal_power; -} - static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { @@ -778,7 +769,7 @@ static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw, static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstats, - struct rx_desc_92c *pdesc, + struct rx_desc_92c *p_desc, struct rx_fwinfo_92c *p_drvinfo, bool packet_match_bssid, bool packet_toself, @@ -793,11 +784,11 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, u32 rssi, total_rssi = 0; bool in_powersavemode = false; bool is_cck_rate; + u8 *pdesc = (u8 *)p_desc; - is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc); + is_cck_rate = RX_HAL_IS_CCK_RATE(p_desc); pstats->packet_matchbssid = packet_match_bssid; pstats->packet_toself = packet_toself; - pstats->is_cck = is_cck_rate; pstats->packet_beacon = packet_beacon; pstats->is_cck = is_cck_rate; pstats->RX_SIGQ[0] = -1; @@ -913,180 +904,6 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, (hw, total_rssi /= rf_rx_num)); } -static void _rtl92c_process_ui_rssi(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &(rtlpriv->phy); - u8 rfpath; - u32 last_rssi, tmpval; - - if (pstats->packet_toself || pstats->packet_beacon) { - rtlpriv->stats.rssi_calculate_cnt++; - if (rtlpriv->stats.ui_rssi.total_num++ >= - PHY_RSSI_SLID_WIN_MAX) { - rtlpriv->stats.ui_rssi.total_num = - PHY_RSSI_SLID_WIN_MAX; - last_rssi = - rtlpriv->stats.ui_rssi.elements[rtlpriv-> - stats.ui_rssi.index]; - rtlpriv->stats.ui_rssi.total_val -= last_rssi; - } - rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength; - rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi. - index++] = pstats->signalstrength; - if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) - rtlpriv->stats.ui_rssi.index = 0; - tmpval = rtlpriv->stats.ui_rssi.total_val / - rtlpriv->stats.ui_rssi.total_num; - rtlpriv->stats.signal_strength = - _rtl92c_translate_todbm(hw, (u8) tmpval); - pstats->rssi = rtlpriv->stats.signal_strength; - } - if (!pstats->is_cck && pstats->packet_toself) { - for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; - rfpath++) { - if (!rtl8192_phy_check_is_legal_rfpath(hw, rfpath)) - continue; - if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { - rtlpriv->stats.rx_rssi_percentage[rfpath] = - pstats->rx_mimo_signalstrength[rfpath]; - } - if (pstats->rx_mimo_signalstrength[rfpath] > - rtlpriv->stats.rx_rssi_percentage[rfpath]) { - rtlpriv->stats.rx_rssi_percentage[rfpath] = - ((rtlpriv->stats. - rx_rssi_percentage[rfpath] * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_mimo_signalstrength[rfpath])) / - (RX_SMOOTH_FACTOR); - - rtlpriv->stats.rx_rssi_percentage[rfpath] = - rtlpriv->stats.rx_rssi_percentage[rfpath] + - 1; - } else { - rtlpriv->stats.rx_rssi_percentage[rfpath] = - ((rtlpriv->stats. - rx_rssi_percentage[rfpath] * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_mimo_signalstrength[rfpath])) / - (RX_SMOOTH_FACTOR); - } - } - } -} - -static void _rtl92c_update_rxsignalstatistics(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - int weighting = 0; - - if (rtlpriv->stats.recv_signal_power == 0) - rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; - if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power) - weighting = 5; - else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power) - weighting = (-5); - rtlpriv->stats.recv_signal_power = - (rtlpriv->stats.recv_signal_power * 5 + - pstats->recvsignalpower + weighting) / 6; -} - -static void _rtl92c_process_pwdb(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - long undec_sm_pwdb = 0; - - if (mac->opmode == NL80211_IFTYPE_ADHOC) { - return; - } else { - undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; - } - if (pstats->packet_toself || pstats->packet_beacon) { - if (undec_sm_pwdb < 0) - undec_sm_pwdb = pstats->rx_pwdb_all; - if (pstats->rx_pwdb_all > (u32) undec_sm_pwdb) { - undec_sm_pwdb = (((undec_sm_pwdb) * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); - undec_sm_pwdb += 1; - } else { - undec_sm_pwdb = (((undec_sm_pwdb) * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); - } - rtlpriv->dm.undec_sm_pwdb = undec_sm_pwdb; - _rtl92c_update_rxsignalstatistics(hw, pstats); - } -} - -static void _rtl92c_process_LINK_Q(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 last_evm = 0, n_stream, tmpval; - - if (pstats->signalquality != 0) { - if (pstats->packet_toself || pstats->packet_beacon) { - if (rtlpriv->stats.LINK_Q.total_num++ >= - PHY_LINKQUALITY_SLID_WIN_MAX) { - rtlpriv->stats.LINK_Q.total_num = - PHY_LINKQUALITY_SLID_WIN_MAX; - last_evm = - rtlpriv->stats.LINK_Q.elements - [rtlpriv->stats.LINK_Q.index]; - rtlpriv->stats.LINK_Q.total_val -= - last_evm; - } - rtlpriv->stats.LINK_Q.total_val += - pstats->signalquality; - rtlpriv->stats.LINK_Q.elements - [rtlpriv->stats.LINK_Q.index++] = - pstats->signalquality; - if (rtlpriv->stats.LINK_Q.index >= - PHY_LINKQUALITY_SLID_WIN_MAX) - rtlpriv->stats.LINK_Q.index = 0; - tmpval = rtlpriv->stats.LINK_Q.total_val / - rtlpriv->stats.LINK_Q.total_num; - rtlpriv->stats.signal_quality = tmpval; - rtlpriv->stats.last_sigstrength_inpercent = tmpval; - for (n_stream = 0; n_stream < 2; - n_stream++) { - if (pstats->RX_SIGQ[n_stream] != -1) { - if (!rtlpriv->stats.RX_EVM[n_stream]) { - rtlpriv->stats.RX_EVM[n_stream] - = pstats->RX_SIGQ[n_stream]; - } - rtlpriv->stats.RX_EVM[n_stream] = - ((rtlpriv->stats.RX_EVM - [n_stream] * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->RX_SIGQ - [n_stream] * 1)) / - (RX_SMOOTH_FACTOR); - } - } - } - } else { - ; - } -} - -static void _rtl92c_process_phyinfo(struct ieee80211_hw *hw, - u8 *buffer, - struct rtl_stats *pcurrent_stats) -{ - if (!pcurrent_stats->packet_matchbssid && - !pcurrent_stats->packet_beacon) - return; - _rtl92c_process_ui_rssi(hw, pcurrent_stats); - _rtl92c_process_pwdb(hw, pcurrent_stats); - _rtl92c_process_LINK_Q(hw, pcurrent_stats); -} - void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, @@ -1123,5 +940,5 @@ void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, _rtl92c_query_rxphystatus(hw, pstats, pdesc, p_drvinfo, packet_matchbssid, packet_toself, packet_beacon); - _rtl92c_process_phyinfo(hw, tmp_buf, pstats); + rtl_process_phyinfo(hw, tmp_buf, pstats); } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c index 34e56308301..9831ff1128c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../pci.h" #include "../ps.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -120,6 +121,7 @@ bool rtl92cu_phy_bb_config(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); u16 regval; + u32 regval32; u8 b_reg_hwparafile = 1; _rtl92c_phy_init_bb_rf_register_definition(hw); @@ -135,8 +137,11 @@ bool rtl92cu_phy_bb_config(struct ieee80211_hw *hw) } else if (IS_HARDWARE_TYPE_8192CU(rtlhal)) { rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, FEN_USBA | FEN_USBD | FEN_BB_GLB_RSTn | FEN_BBRSTB); - rtl_write_byte(rtlpriv, REG_LDOHCI12_CTRL, 0x0f); } + regval32 = rtl_read_dword(rtlpriv, 0x87c); + rtl_write_dword(rtlpriv, 0x87c, regval32 & (~BIT(31))); + if (IS_HARDWARE_TYPE_8192CU(rtlhal)) + rtl_write_byte(rtlpriv, REG_LDOHCI12_CTRL, 0x0f); rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80); if (b_reg_hwparafile == 1) rtstatus = _rtl92c_phy_bb8192c_config_parafile(hw); @@ -184,18 +189,7 @@ bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, } if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { - if (phy_regarray_table[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table[i] == 0xfb) - udelay(50); - else if (phy_regarray_table[i] == 0xfa) - udelay(5); - else if (phy_regarray_table[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table[i]); rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, phy_regarray_table[i + 1]); udelay(1); @@ -232,18 +226,7 @@ bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, phy_regarray_table_pg = rtlphy->hwparam_tables[PHY_REG_PG].pdata; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { - if (phy_regarray_table_pg[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table_pg[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table_pg[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table_pg[i] == 0xfb) - udelay(50); - else if (phy_regarray_table_pg[i] == 0xfa) - udelay(5); - else if (phy_regarray_table_pg[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table_pg[i]); _rtl92c_store_pwrIndex_diffrate_offset(hw, phy_regarray_table_pg[i], phy_regarray_table_pg[i + 1], @@ -290,46 +273,16 @@ bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { - if (radioa_array_table[i] == 0xfe) - mdelay(50); - else if (radioa_array_table[i] == 0xfd) - mdelay(5); - else if (radioa_array_table[i] == 0xfc) - mdelay(1); - else if (radioa_array_table[i] == 0xfb) - udelay(50); - else if (radioa_array_table[i] == 0xfa) - udelay(5); - else if (radioa_array_table[i] == 0xf9) - udelay(1); - else { - rtl_set_rfreg(hw, rfpath, radioa_array_table[i], - RFREG_OFFSET_MASK, - radioa_array_table[i + 1]); - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radioa_array_table[i], + RFREG_OFFSET_MASK, + radioa_array_table[i + 1]); } break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { - if (radiob_array_table[i] == 0xfe) { - mdelay(50); - } else if (radiob_array_table[i] == 0xfd) - mdelay(5); - else if (radiob_array_table[i] == 0xfc) - mdelay(1); - else if (radiob_array_table[i] == 0xfb) - udelay(50); - else if (radiob_array_table[i] == 0xfa) - udelay(5); - else if (radiob_array_table[i] == 0xf9) - udelay(1); - else { - rtl_set_rfreg(hw, rfpath, radiob_array_table[i], - RFREG_OFFSET_MASK, - radiob_array_table[i + 1]); - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radiob_array_table[i], + RFREG_OFFSET_MASK, + radiob_array_table[i + 1]); } break; case RF90_PATH_C: @@ -340,6 +293,8 @@ bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case not processed\n"); break; + default: + break; } return true; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c index 2119313a737..b878d56d2f4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c @@ -85,17 +85,15 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, if (mac->act_scanning) { tx_agc[RF90_PATH_A] = 0x3f3f3f3f; tx_agc[RF90_PATH_B] = 0x3f3f3f3f; - if (turbo_scanoff) { - for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { - tx_agc[idx1] = ppowerlevel[idx1] | - (ppowerlevel[idx1] << 8) | - (ppowerlevel[idx1] << 16) | - (ppowerlevel[idx1] << 24); - if (rtlhal->interface == INTF_USB) { - if (tx_agc[idx1] > 0x20 && - rtlefuse->external_pa) - tx_agc[idx1] = 0x20; - } + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + tx_agc[idx1] = ppowerlevel[idx1] | + (ppowerlevel[idx1] << 8) | + (ppowerlevel[idx1] << 16) | + (ppowerlevel[idx1] << 24); + if (rtlhal->interface == INTF_USB) { + if (tx_agc[idx1] > 0x20 && + rtlefuse->external_pa) + tx_agc[idx1] = 0x20; } } } else { @@ -107,7 +105,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, TXHIGHPWRLEVEL_LEVEL2) { tx_agc[RF90_PATH_A] = 0x00000000; tx_agc[RF90_PATH_B] = 0x00000000; - } else{ + } else { for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { tx_agc[idx1] = ppowerlevel[idx1] | (ppowerlevel[idx1] << 8) | @@ -373,7 +371,12 @@ static void _rtl92c_write_ofdm_power_reg(struct ieee80211_hw *hw, regoffset == RTXAGC_B_MCS07_MCS04) regoffset = 0xc98; for (i = 0; i < 3; i++) { - writeVal = (writeVal > 6) ? (writeVal - 6) : 0; + if (i != 2) + writeVal = (writeVal > 8) ? + (writeVal - 8) : 0; + else + writeVal = (writeVal > 6) ? + (writeVal - 6) : 0; rtl_write_byte(rtlpriv, (u32)(regoffset + i), (u8)writeVal); } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h index 090fd33a158..11b439d6b67 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h @@ -34,15 +34,14 @@ #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 -extern void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); +void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw); bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, - enum radio_path rfpath); + enum radio_path rfpath); void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 2bd59852621..361435f8608 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -31,6 +31,7 @@ #include "../core.h" #include "../usb.h" #include "../efuse.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -49,6 +50,9 @@ MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n USB wireless"); MODULE_FIRMWARE("rtlwifi/rtl8192cufw.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8192cufw_A.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8192cufw_B.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8192cufw_TMSC.bin"); static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw) { @@ -68,14 +72,21 @@ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw) "Can't alloc buffer for fw\n"); return 1; } - + if (IS_VENDOR_UMC_A_CUT(rtlpriv->rtlhal.version) && + !IS_92C_SERIAL(rtlpriv->rtlhal.version)) { + rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cufw_A.bin"; + } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlpriv->rtlhal.version)) { + rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cufw_B.bin"; + } else { + rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cufw_TMSC.bin"; + } + /* provide name of alternative file */ + rtlpriv->cfg->alt_fw_name = "rtlwifi/rtl8192cufw.bin"; pr_info("Loading firmware %s\n", rtlpriv->cfg->fw_name); rtlpriv->max_fw_size = 0x4000; err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); - - return err; } @@ -117,7 +128,7 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = { .set_bw_mode = rtl92c_phy_set_bw_mode, .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, - .scan_operation_backup = rtl92c_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl92cu_phy_set_rf_power_state, .led_control = rtl92cu_led_control, .enable_hw_sec = rtl92cu_enable_hw_security_config, @@ -306,6 +317,7 @@ static struct usb_device_id rtl8192c_usb_ids[] = { {RTL_USB_DEVICE(0x0bda, 0x5088, rtl92cu_hal_cfg)}, /*Thinkware-CC&C*/ {RTL_USB_DEVICE(0x0df6, 0x0052, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ {RTL_USB_DEVICE(0x0df6, 0x005c, rtl92cu_hal_cfg)}, /*Sitecom - Edimax*/ + {RTL_USB_DEVICE(0x0df6, 0x0077, rtl92cu_hal_cfg)}, /*Sitecom-WLA2100V2*/ {RTL_USB_DEVICE(0x0eb0, 0x9071, rtl92cu_hal_cfg)}, /*NO Brand - Etop*/ {RTL_USB_DEVICE(0x4856, 0x0091, rtl92cu_hal_cfg)}, /*NetweeN - Feixun*/ /* HP - Lite-On ,8188CUS Slim Combo */ @@ -383,9 +395,6 @@ static struct usb_driver rtl8192cu_driver = { /* .resume = rtl_usb_resume, */ /* .reset_resume = rtl8192c_resume, */ #endif /* CONFIG_PM */ -#ifdef CONFIG_AUTOSUSPEND - .supports_autosuspend = 1, -#endif .disable_hub_initiated_lpm = 1, }; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/table.c b/drivers/net/wireless/rtlwifi/rtl8192cu/table.c index 966be519edb..7903c154de0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/table.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/table.c @@ -36,7 +36,7 @@ u32 RTL8192CUPHY_REG_2TARRAY[RTL8192CUPHY_REG_2TARRAY_LENGTH] = { 0x804, 0x00000003, 0x808, 0x0000fc00, 0x80c, 0x0000000a, - 0x810, 0x10005388, + 0x810, 0x10000330, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, @@ -110,22 +110,22 @@ u32 RTL8192CUPHY_REG_2TARRAY[RTL8192CUPHY_REG_2TARRAY_LENGTH] = { 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, - 0xc50, 0x6954341e, + 0xc50, 0x69543420, 0xc54, 0x43bc0094, - 0xc58, 0x6954341e, + 0xc58, 0x69543420, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, 0xc68, 0x47c00bff, 0xc6c, 0x00000036, 0xc70, 0x2c7f000d, - 0xc74, 0x0186115b, + 0xc74, 0x2186115b, 0xc78, 0x0000001f, 0xc7c, 0x00b99612, 0xc80, 0x40000100, 0xc84, 0x20f60000, 0xc88, 0x40000100, - 0xc8c, 0x20200000, + 0xc8c, 0xa0e40000, 0xc90, 0x00121820, 0xc94, 0x00000000, 0xc98, 0x00121820, @@ -226,7 +226,7 @@ u32 RTL8192CUPHY_REG_1TARRAY[RTL8192CUPHY_REG_1TARRAY_LENGTH] = { 0x804, 0x00000001, 0x808, 0x0000fc00, 0x80c, 0x0000000a, - 0x810, 0x10005388, + 0x810, 0x10000330, 0x814, 0x020c3d10, 0x818, 0x02200385, 0x81c, 0x00000000, @@ -300,9 +300,9 @@ u32 RTL8192CUPHY_REG_1TARRAY[RTL8192CUPHY_REG_1TARRAY_LENGTH] = { 0xc44, 0x000100b7, 0xc48, 0xec020107, 0xc4c, 0x007f037f, - 0xc50, 0x6954341e, + 0xc50, 0x69543420, 0xc54, 0x43bc0094, - 0xc58, 0x6954341e, + 0xc58, 0x69543420, 0xc5c, 0x433c0094, 0xc60, 0x00000000, 0xc64, 0x5116848b, @@ -340,7 +340,7 @@ u32 RTL8192CUPHY_REG_1TARRAY[RTL8192CUPHY_REG_1TARRAY_LENGTH] = { 0xce4, 0x00000000, 0xce8, 0x37644302, 0xcec, 0x2f97d40c, - 0xd00, 0x00080740, + 0xd00, 0x00000740, 0xd04, 0x00020401, 0xd08, 0x0000907f, 0xd0c, 0x20010201, @@ -633,17 +633,17 @@ u32 RTL8192CURADIOA_2TARRAY[RTL8192CURADIOA_2TARRAYLENGTH] = { 0x012, 0x00071000, 0x012, 0x000b0000, 0x012, 0x000fc000, - 0x013, 0x000287af, + 0x013, 0x000287b3, 0x013, 0x000244b7, 0x013, 0x000204ab, 0x013, 0x0001c49f, 0x013, 0x00018493, - 0x013, 0x00014297, - 0x013, 0x00010295, - 0x013, 0x0000c298, - 0x013, 0x0000819c, - 0x013, 0x000040a8, - 0x013, 0x0000001c, + 0x013, 0x0001429b, + 0x013, 0x00010299, + 0x013, 0x0000c29c, + 0x013, 0x000081a0, + 0x013, 0x000040ac, + 0x013, 0x00000020, 0x014, 0x0001944c, 0x014, 0x00059444, 0x014, 0x0009944c, @@ -932,10 +932,10 @@ u32 RTL8192CUMAC_2T_ARRAY[RTL8192CUMAC_2T_ARRAYLENGTH] = { 0x608, 0x0000000e, 0x609, 0x0000002a, 0x652, 0x00000020, - 0x63c, 0x0000000a, - 0x63d, 0x0000000e, - 0x63e, 0x0000000a, - 0x63f, 0x0000000e, + 0x63c, 0x00000008, + 0x63d, 0x00000008, + 0x63e, 0x0000000c, + 0x63f, 0x0000000c, 0x66e, 0x00000005, 0x700, 0x00000021, 0x701, 0x00000043, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 5a060e537fb..035e0dc3922 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -303,10 +303,10 @@ out: bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, - u8 *p_desc, struct sk_buff *skb) + u8 *pdesc, struct sk_buff *skb) { struct rx_fwinfo_92c *p_drvinfo; - struct rx_desc_92c *pdesc = (struct rx_desc_92c *)p_desc; + struct rx_desc_92c *p_desc = (struct rx_desc_92c *)pdesc; u32 phystatus = GET_RX_DESC_PHY_STATUS(pdesc); stats->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); @@ -345,12 +345,11 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(skb->data + stats->rx_bufshift); - rtl92c_translate_rx_signal_stuff(hw, skb, stats, pdesc, + rtl92c_translate_rx_signal_stuff(hw, skb, stats, p_desc, p_drvinfo); } /*rx_status->qual = stats->signal; */ - rx_status->signal = stats->rssi + 10; - /*rx_status->noise = -stats->noise; */ + rx_status->signal = stats->recvsignalpower + 10; return true; } @@ -365,7 +364,6 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) u8 *rxdesc; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; struct rx_fwinfo_92c *p_drvinfo; @@ -497,7 +495,7 @@ static void _rtl_tx_desc_checksum(u8 *txdesc) void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 queue_index, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h index 725c53accc5..fd8051dcd98 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.h @@ -420,7 +420,7 @@ struct sk_buff *rtl8192c_tx_aggregate_hdl(struct ieee80211_hw *, struct sk_buff_head *); void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 queue_index, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index f700f7a614b..304c443b89b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -194,15 +194,15 @@ static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) rtl_set_bbreg(hw, ROFDM0_LSTF, BIT(31), 1); /* hold page C counter */ rtl_set_bbreg(hw, ROFDM1_LSTF, BIT(31), 1); /*hold page D counter */ - ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, BMASKDWORD); + ret_value = rtl_get_bbreg(hw, ROFDM0_FRAMESYNC, MASKDWORD); falsealm_cnt->cnt_fast_fsync_fail = (ret_value & 0xffff); falsealm_cnt->cnt_sb_search_fail = ((ret_value & 0xffff0000) >> 16); - ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, BMASKDWORD); + ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, MASKDWORD); falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16); - ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, BMASKDWORD); + ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, MASKDWORD); falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff); falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16); - ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, BMASKDWORD); + ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD); falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff); falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + falsealm_cnt->cnt_rate_illegal + @@ -214,9 +214,9 @@ static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) if (rtlpriv->rtlhal.current_bandtype != BAND_ON_5G) { /* hold cck counter */ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); - ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, BMASKBYTE0); + ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, MASKBYTE0); falsealm_cnt->cnt_cck_fail = ret_value; - ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, BMASKBYTE3); + ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, MASKBYTE3); falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8; rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } else { @@ -331,11 +331,11 @@ static void rtl92d_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) if (de_digtable->pre_cck_pd_state != de_digtable->cur_cck_pd_state) { if (de_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); - rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0x83); + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } else { rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); - rtl_set_bbreg(hw, RCCK0_CCA, BMASKBYTE2, 0xcd); + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); rtl92d_release_cckandrw_pagea_ctl(hw, &flag); } de_digtable->pre_cck_pd_state = de_digtable->cur_cck_pd_state; @@ -722,7 +722,7 @@ static void rtl92d_dm_rxgain_tracking_thermalmeter(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "===> Rx Gain %x\n", u4tmp); for (i = RF90_PATH_A; i < rtlpriv->phy.num_total_rfpath; i++) - rtl_set_rfreg(hw, i, 0x3C, BRFREGOFFSETMASK, + rtl_set_rfreg(hw, i, 0x3C, RFREG_OFFSET_MASK, (rtlpriv->phy.reg_rf3c[i] & (~(0xF000))) | u4tmp); } @@ -737,7 +737,7 @@ static void rtl92d_bandtype_2_4G(struct ieee80211_hw *hw, long *temp_cckg, /* Query CCK default setting From 0xa24 */ rtl92d_acquire_cckandrw_pagea_ctl(hw, &flag); temp_cck = rtl_get_bbreg(hw, RCCK0_TXFILTER2, - BMASKDWORD) & BMASKCCK; + MASKDWORD) & MASKCCK; rtl92d_release_cckandrw_pagea_ctl(hw, &flag); for (i = 0; i < CCK_TABLE_LENGTH; i++) { if (rtlpriv->dm.cck_inch14) { @@ -840,9 +840,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( bool internal_pa = false; long ele_a = 0, ele_d, temp_cck, val_x, value32; long val_y, ele_c = 0; - u8 ofdm_index[2]; + u8 ofdm_index[3]; s8 cck_index = 0; - u8 ofdm_index_old[2] = {0, 0}; + u8 ofdm_index_old[3] = {0, 0, 0}; s8 cck_index_old = 0; u8 index; int i; @@ -896,9 +896,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( rf = 1; if (thermalvalue) { ele_d = rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE, - BMASKDWORD) & BMASKOFDM_D; + MASKDWORD) & MASKOFDM_D; for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) { - if (ele_d == (ofdmswing_table[i] & BMASKOFDM_D)) { + if (ele_d == (ofdmswing_table[i] & MASKOFDM_D)) { ofdm_index_old[0] = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, @@ -910,10 +910,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( } if (is2t) { ele_d = rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, - BMASKDWORD) & BMASKOFDM_D; + MASKDWORD) & MASKOFDM_D; for (i = 0; i < OFDM_TABLE_SIZE_92D; i++) { if (ele_d == - (ofdmswing_table[i] & BMASKOFDM_D)) { + (ofdmswing_table[i] & MASKOFDM_D)) { ofdm_index_old[1] = (u8) i; RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, @@ -1091,10 +1091,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( value32 = (ele_d << 22) | ((ele_c & 0x3F) << 16) | ele_a; rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, - BMASKDWORD, value32); + MASKDWORD, value32); value32 = (ele_c & 0x000003C0) >> 6; - rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS, + rtl_set_bbreg(hw, ROFDM0_XCTxAFE, MASKH4BITS, value32); value32 = ((val_x * ele_d) >> 7) & 0x01; @@ -1103,10 +1103,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( } else { rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, - BMASKDWORD, + MASKDWORD, ofdmswing_table [(u8)ofdm_index[0]]); - rtl_set_bbreg(hw, ROFDM0_XCTxAFE, BMASKH4BITS, + rtl_set_bbreg(hw, ROFDM0_XCTxAFE, MASKH4BITS, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), 0x00); @@ -1118,6 +1118,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( val_x, val_y, ele_a, ele_c, ele_d, val_x, val_y); + if (cck_index >= CCK_TABLE_SIZE) + cck_index = CCK_TABLE_SIZE - 1; + if (cck_index < 0) + cck_index = 0; if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* Adjust CCK according to IQK result */ if (!rtlpriv->dm.cck_inch14) { @@ -1200,21 +1204,21 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( ele_a; rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, - BMASKDWORD, value32); + MASKDWORD, value32); value32 = (ele_c & 0x000003C0) >> 6; rtl_set_bbreg(hw, ROFDM0_XDTxAFE, - BMASKH4BITS, value32); + MASKH4BITS, value32); value32 = ((val_x * ele_d) >> 7) & 0x01; rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(28), value32); } else { rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, - BMASKDWORD, + MASKDWORD, ofdmswing_table [(u8) ofdm_index[1]]); rtl_set_bbreg(hw, ROFDM0_XDTxAFE, - BMASKH4BITS, 0x00); + MASKH4BITS, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(28), 0x00); } @@ -1225,10 +1229,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( } RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "TxPwrTracking 0xc80 = 0x%x, 0xc94 = 0x%x RF 0x24 = 0x%x\n", - rtl_get_bbreg(hw, 0xc80, BMASKDWORD), - rtl_get_bbreg(hw, 0xc94, BMASKDWORD), + rtl_get_bbreg(hw, 0xc80, MASKDWORD), + rtl_get_bbreg(hw, 0xc94, MASKDWORD), rtl_get_rfreg(hw, RF90_PATH_A, 0x24, - BRFREGOFFSETMASK)); + RFREG_OFFSET_MASK)); } if ((delta_iqk > rtlefuse->delta_iqk) && (rtlefuse->delta_iqk != 0)) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index 7dd8f6de055..2b08671004a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -318,7 +318,7 @@ void rtl92de_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_AC_PARAM: { u8 e_aci = *val; rtl92d_dm_init_edca_turbo(hw); - if (rtlpci->acm_method != eAcmWay2_SW) + if (rtlpci->acm_method != EACMWAY2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, &e_aci); break; @@ -985,9 +985,9 @@ int rtl92de_hw_init(struct ieee80211_hw *hw) /* set default value after initialize RF, */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0); rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, - RF_CHNLBW, BRFREGOFFSETMASK); + RF_CHNLBW, RFREG_OFFSET_MASK); rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, - RF_CHNLBW, BRFREGOFFSETMASK); + RF_CHNLBW, RFREG_OFFSET_MASK); /*---- Set CCK and OFDM Block "ON"----*/ if (rtlhal->current_bandtype == BAND_ON_2_4G) @@ -1035,7 +1035,7 @@ int rtl92de_hw_init(struct ieee80211_hw *hw) tmp_rega = rtl_get_rfreg(hw, (enum radio_path)RF90_PATH_A, - 0x2a, BMASKDWORD); + 0x2a, MASKDWORD); if (((tmp_rega & BIT(11)) == BIT(11))) break; @@ -1138,11 +1138,13 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw, void rtl92de_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - u32 reg_rcr = rtlpci->receive_config; + u32 reg_rcr; if (rtlpriv->psc.rfpwr_state != ERFON) return; + + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); + if (check_bssid) { reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); @@ -1194,25 +1196,7 @@ void rtl92d_linked_set_reg(struct ieee80211_hw *hw) * mac80211 will send pkt when scan */ void rtl92de_set_qos(struct ieee80211_hw *hw, int aci) { - struct rtl_priv *rtlpriv = rtl_priv(hw); rtl92d_dm_init_edca_turbo(hw); - return; - switch (aci) { - case AC1_BK: - rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); - break; - case AC0_BE: - break; - case AC2_VI: - rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322); - break; - case AC3_VO: - rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222); - break; - default: - RT_ASSERT(false, "invalid aci: %d !\n", aci); - break; - } } void rtl92de_enable_interrupt(struct ieee80211_hw *hw) @@ -1350,13 +1334,13 @@ void rtl92de_card_disable(struct ieee80211_hw *hw) /* c. ========RF OFF sequence========== */ /* 0x88c[23:20] = 0xf. */ rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0x00f00000, 0xf); - rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); /* APSD_CTRL 0x600[7:0] = 0x40 */ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); /* Close antenna 0,0xc04,0xd04 */ - rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0); /* SYS_FUNC_EN 0x02[7:0] = 0xE2 reset BB state machine */ diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.h b/drivers/net/wireless/rtlwifi/rtl8192de/hw.h index 7c9f7a2f1e4..1bc7b1a96d4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.h @@ -55,10 +55,9 @@ void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); -extern void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, - u32 value, u8 direct); -extern u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, - u8 direct); +void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, u32 value, + u8 direct); +u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, u8 direct); void rtl92de_suspend(struct ieee80211_hw *hw); void rtl92de_resume(struct ieee80211_hw *hw); void rtl92d_linked_set_reg(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c index 840bac5fa2f..3d1f0dd4e52 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../pci.h" #include "../ps.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -242,7 +243,7 @@ void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, else if (rtlhal->during_mac0init_radiob) /* mac0 use phy1 write radio_b. */ dbi_direct = BIT(3) | BIT(2); - if (bitmask != BMASKDWORD) { + if (bitmask != MASKDWORD) { if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) originalvalue = rtl92de_read_dword_dbi(hw, @@ -275,20 +276,20 @@ static u32 _rtl92d_phy_rf_serial_read(struct ieee80211_hw *hw, u32 retvalue; newoffset = offset; - tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD); + tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD); if (rfpath == RF90_PATH_A) tmplong2 = tmplong; else - tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD); + tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD); tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) | (newoffset << 23) | BLSSIREADEDGE; - rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, tmplong & (~BLSSIREADEDGE)); udelay(10); - rtl_set_bbreg(hw, pphyreg->rfhssi_para2, BMASKDWORD, tmplong2); + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2); udelay(50); udelay(50); - rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, tmplong | BLSSIREADEDGE); udelay(10); if (rfpath == RF90_PATH_A) @@ -321,7 +322,7 @@ static void _rtl92d_phy_rf_serial_write(struct ieee80211_hw *hw, newoffset = offset; /* T65 RF */ data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; - rtl_set_bbreg(hw, pphyreg->rf3wire_offset, BMASKDWORD, data_and_addr); + rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n", rfpath, pphyreg->rf3wire_offset, data_and_addr); } @@ -362,7 +363,7 @@ void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, return; spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); if (rtlphy->rf_mode != RF_OP_BY_FW) { - if (bitmask != BRFREGOFFSETMASK) { + if (bitmask != RFREG_OFFSET_MASK) { original_value = _rtl92d_phy_rf_serial_read(hw, rfpath, regaddr); bitshift = _rtl92d_phy_calculate_bit_shift(bitmask); @@ -567,19 +568,8 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, " ===> phy:Rtl819XPHY_REG_Array_PG\n"); if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { - if (phy_regarray_table[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table[i] == 0xfb) - udelay(50); - else if (phy_regarray_table[i] == 0xfa) - udelay(5); - else if (phy_regarray_table[i] == 0xf9) - udelay(1); - rtl_set_bbreg(hw, phy_regarray_table[i], BMASKDWORD, + rtl_addr_delay(phy_regarray_table[i]); + rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, phy_regarray_table[i + 1]); udelay(1); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, @@ -591,7 +581,7 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, if (rtlhal->interfaceindex == 0) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], - BMASKDWORD, + MASKDWORD, agctab_array_table[i + 1]); /* Add 1us delay between BB/RF register * setting. */ @@ -607,7 +597,7 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, if (rtlhal->current_bandtype == BAND_ON_2_4G) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], - BMASKDWORD, + MASKDWORD, agctab_array_table[i + 1]); /* Add 1us delay between BB/RF register * setting. */ @@ -623,7 +613,7 @@ static bool _rtl92d_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, for (i = 0; i < agctab_5garraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_5garray_table[i], - BMASKDWORD, + MASKDWORD, agctab_5garray_table[i + 1]); /* Add 1us delay between BB/RF registeri * setting. */ @@ -705,18 +695,7 @@ static bool _rtl92d_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, phy_regarray_table_pg = rtl8192de_phy_reg_array_pg; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { - if (phy_regarray_table_pg[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table_pg[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table_pg[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table_pg[i] == 0xfb) - udelay(50); - else if (phy_regarray_table_pg[i] == 0xfa) - udelay(5); - else if (phy_regarray_table_pg[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table_pg[i]); _rtl92d_store_pwrindex_diffrate_offset(hw, phy_regarray_table_pg[i], phy_regarray_table_pg[i + 1], @@ -843,54 +822,16 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { - if (radioa_array_table[i] == 0xfe) { - mdelay(50); - } else if (radioa_array_table[i] == 0xfd) { - /* delay_ms(5); */ - mdelay(5); - } else if (radioa_array_table[i] == 0xfc) { - /* delay_ms(1); */ - mdelay(1); - } else if (radioa_array_table[i] == 0xfb) { - udelay(50); - } else if (radioa_array_table[i] == 0xfa) { - udelay(5); - } else if (radioa_array_table[i] == 0xf9) { - udelay(1); - } else { - rtl_set_rfreg(hw, rfpath, radioa_array_table[i], - BRFREGOFFSETMASK, - radioa_array_table[i + 1]); - /* Add 1us delay between BB/RF register set. */ - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radioa_array_table[i], + RFREG_OFFSET_MASK, + radioa_array_table[i + 1]); } break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { - if (radiob_array_table[i] == 0xfe) { - /* Delay specific ms. Only RF configuration - * requires delay. */ - mdelay(50); - } else if (radiob_array_table[i] == 0xfd) { - /* delay_ms(5); */ - mdelay(5); - } else if (radiob_array_table[i] == 0xfc) { - /* delay_ms(1); */ - mdelay(1); - } else if (radiob_array_table[i] == 0xfb) { - udelay(50); - } else if (radiob_array_table[i] == 0xfa) { - udelay(5); - } else if (radiob_array_table[i] == 0xf9) { - udelay(1); - } else { - rtl_set_rfreg(hw, rfpath, radiob_array_table[i], - BRFREGOFFSETMASK, - radiob_array_table[i + 1]); - /* Add 1us delay between BB/RF register set. */ - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radiob_array_table[i], + RFREG_OFFSET_MASK, + radiob_array_table[i + 1]); } break; case RF90_PATH_C: @@ -911,13 +852,13 @@ void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) struct rtl_phy *rtlphy = &(rtlpriv->phy); rtlphy->default_initialgain[0] = - (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, BMASKBYTE0); + (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[1] = - (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, BMASKBYTE0); + (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[2] = - (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, BMASKBYTE0); + (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0); rtlphy->default_initialgain[3] = - (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, BMASKBYTE0); + (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x\n", rtlphy->default_initialgain[0], @@ -925,9 +866,9 @@ void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) rtlphy->default_initialgain[2], rtlphy->default_initialgain[3]); rtlphy->framesync = (u8)rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3, - BMASKBYTE0); + MASKBYTE0); rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2, - BMASKDWORD); + MASKDWORD); RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Default framesync (0x%x) = 0x%x\n", ROFDM0_RXDETECTOR3, rtlphy->framesync); @@ -1022,34 +963,6 @@ void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) rtl92d_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel); } -void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - rtlhal->current_bandtypebackup = - rtlhal->current_bandtype; - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation\n"); - break; - } - } -} - void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { @@ -1134,7 +1047,7 @@ static void _rtl92d_phy_stop_trx_before_changeband(struct ieee80211_hw *hw) { rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0); rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0); - rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x00); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x00); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x0); } @@ -1196,7 +1109,7 @@ static void _rtl92d_phy_reload_imr_setting(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 imr_num = MAX_RF_IMR_INDEX; - u32 rfmask = BRFREGOFFSETMASK; + u32 rfmask = RFREG_OFFSET_MASK; u8 group, i; unsigned long flag = 0; @@ -1239,7 +1152,7 @@ static void _rtl92d_phy_reload_imr_setting(struct ieee80211_hw *hw, for (i = 0; i < imr_num; i++) { rtl_set_rfreg(hw, (enum radio_path)rfpath, rf_reg_for_5g_swchnl_normal[i], - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, rf_imr_param_normal[0][0][i]); } rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, @@ -1357,7 +1270,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) if (i == 0 && (rtlhal->macphymode == DUALMAC_DUALPHY)) { rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], - BRFREGOFFSETMASK, 0xE439D); + RFREG_OFFSET_MASK, 0xE439D); } else if (rf_reg_for_c_cut_5g[i] == RF_SYN_G4) { u4tmp2 = (rf_reg_pram_c_5g[index][i] & 0x7FF) | (u4tmp << 11); @@ -1365,11 +1278,11 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) u4tmp2 &= ~(BIT(7) | BIT(6)); rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], - BRFREGOFFSETMASK, u4tmp2); + RFREG_OFFSET_MASK, u4tmp2); } else { rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, rf_reg_pram_c_5g[index][i]); } RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, @@ -1379,7 +1292,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) path, index, rtl_get_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_5g[i], - BRFREGOFFSETMASK)); + RFREG_OFFSET_MASK)); } if (need_pwr_down) _rtl92d_phy_restore_rf_env(hw, path, &u4regvalue); @@ -1409,7 +1322,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) i++) { rtl_set_rfreg(hw, rfpath, rf_for_c_cut_5g_internal_pa[i], - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, rf_pram_c_5g_int_pa[index][i]); RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "offset 0x%x value 0x%x path %d index %d\n", @@ -1450,13 +1363,13 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) if (rf_reg_for_c_cut_2g[i] == RF_SYN_G7) rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_2g[i], - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, (rf_reg_param_for_c_cut_2g[index][i] | BIT(17))); else rtl_set_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_2g[i], - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, rf_reg_param_for_c_cut_2g [index][i]); RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, @@ -1466,14 +1379,14 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) rf_reg_mask_for_c_cut_2g[i], path, index, rtl_get_rfreg(hw, (enum radio_path)path, rf_reg_for_c_cut_2g[i], - BRFREGOFFSETMASK)); + RFREG_OFFSET_MASK)); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "cosa ver 3 set RF-B, 2G, 0x28 = 0x%x !!\n", rf_syn_g4_for_c_cut_2g | (u4tmp << 11)); rtl_set_rfreg(hw, (enum radio_path)path, RF_SYN_G4, - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, rf_syn_g4_for_c_cut_2g | (u4tmp << 11)); if (need_pwr_down) _rtl92d_phy_restore_rf_env(hw, path, &u4regvalue); @@ -1521,41 +1434,41 @@ static u8 _rtl92d_phy_patha_iqk(struct ieee80211_hw *hw, bool configpathb) /* path-A IQK setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n"); if (rtlhal->interfaceindex == 0) { - rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c1f); - rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c1f); + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1f); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x10008c1f); } else { - rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x10008c22); - rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x10008c22); + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c22); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x10008c22); } - rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140102); - rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x28160206); + rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82140102); + rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x28160206); /* path-B IQK setting */ if (configpathb) { - rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x10008c22); - rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x10008c22); - rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140102); - rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x28160206); + rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x10008c22); + rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x10008c22); + rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82140102); + rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x28160206); } /* LO calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n"); - rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911); + rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911); /* One shot, path A LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); - rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000); - rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path A LOK & IQK\n", IQK_DELAY_TIME); mdelay(IQK_DELAY_TIME); /* Check failed */ - regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); + regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); - rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD); + rege94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe94 = 0x%x\n", rege94); - rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD); + rege9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe9c = 0x%x\n", rege9c); - regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD); + regea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xea4 = 0x%x\n", regea4); if (!(regeac & BIT(28)) && (((rege94 & 0x03FF0000) >> 16) != 0x142) && (((rege9c & 0x03FF0000) >> 16) != 0x42)) @@ -1591,42 +1504,42 @@ static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw, RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK!\n"); /* path-A IQK setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n"); - rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f); - rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f); - rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82140307); - rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68160960); + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x18008c1f); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x18008c1f); + rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82140307); + rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x68160960); /* path-B IQK setting */ if (configpathb) { - rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f); - rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f); - rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82110000); - rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68110000); + rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x18008c2f); + rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x18008c2f); + rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82110000); + rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x68110000); } /* LO calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n"); - rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911); + rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911); /* path-A PA on */ - rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x07000f60); - rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD, 0x66e60e30); + rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD, 0x07000f60); + rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, MASKDWORD, 0x66e60e30); for (i = 0; i < retrycount; i++) { /* One shot, path A LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); - rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf9000000); - rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path A LOK & IQK.\n", IQK_DELAY_TIME); mdelay(IQK_DELAY_TIME * 10); /* Check failed */ - regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); + regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); - rege94 = rtl_get_bbreg(hw, 0xe94, BMASKDWORD); + rege94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe94 = 0x%x\n", rege94); - rege9c = rtl_get_bbreg(hw, 0xe9c, BMASKDWORD); + rege9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xe9c = 0x%x\n", rege9c); - regea4 = rtl_get_bbreg(hw, 0xea4, BMASKDWORD); + regea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xea4 = 0x%x\n", regea4); if (!(regeac & TxOKBit) && (((rege94 & 0x03FF0000) >> 16) != 0x142)) { @@ -1648,9 +1561,9 @@ static u8 _rtl92d_phy_patha_iqk_5g_normal(struct ieee80211_hw *hw, } } /* path A PA off */ - rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD, rtlphy->iqk_bb_backup[0]); - rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, MASKDWORD, rtlphy->iqk_bb_backup[1]); return result; } @@ -1665,22 +1578,22 @@ static u8 _rtl92d_phy_pathb_iqk(struct ieee80211_hw *hw) RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK!\n"); /* One shot, path B LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); - rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000002); - rtl_set_bbreg(hw, 0xe60, BMASKDWORD, 0x00000000); + rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000002); + rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Delay %d ms for One shot, path B LOK & IQK\n", IQK_DELAY_TIME); mdelay(IQK_DELAY_TIME); /* Check failed */ - regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); + regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); - regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD); + regeb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeb4 = 0x%x\n", regeb4); - regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD); + regebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xebc = 0x%x\n", regebc); - regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD); + regec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xec4 = 0x%x\n", regec4); - regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD); + regecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xecc = 0x%x\n", regecc); if (!(regeac & BIT(31)) && (((regeb4 & 0x03FF0000) >> 16) != 0x142) && (((regebc & 0x03FF0000) >> 16) != 0x42)) @@ -1708,31 +1621,31 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw) RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK!\n"); /* path-A IQK setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A IQK setting!\n"); - rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x18008c1f); - rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x18008c1f); - rtl_set_bbreg(hw, 0xe38, BMASKDWORD, 0x82110000); - rtl_set_bbreg(hw, 0xe3c, BMASKDWORD, 0x68110000); + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x18008c1f); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x18008c1f); + rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82110000); + rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x68110000); /* path-B IQK setting */ - rtl_set_bbreg(hw, 0xe50, BMASKDWORD, 0x18008c2f); - rtl_set_bbreg(hw, 0xe54, BMASKDWORD, 0x18008c2f); - rtl_set_bbreg(hw, 0xe58, BMASKDWORD, 0x82140307); - rtl_set_bbreg(hw, 0xe5c, BMASKDWORD, 0x68160960); + rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x18008c2f); + rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x18008c2f); + rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82140307); + rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x68160960); /* LO calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "LO calibration setting!\n"); - rtl_set_bbreg(hw, 0xe4c, BMASKDWORD, 0x00462911); + rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911); /* path-B PA on */ - rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, 0x0f600700); - rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD, 0x061f0d30); + rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD, 0x0f600700); + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, MASKDWORD, 0x061f0d30); for (i = 0; i < retrycount; i++) { /* One shot, path B LOK & IQK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "One shot, path A LOK & IQK!\n"); - rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xfa000000); - rtl_set_bbreg(hw, 0xe48, BMASKDWORD, 0xf8000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xfa000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000); /* delay x ms */ RTPRINT(rtlpriv, FINIT, INIT_IQK, @@ -1740,15 +1653,15 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw) mdelay(IQK_DELAY_TIME * 10); /* Check failed */ - regeac = rtl_get_bbreg(hw, 0xeac, BMASKDWORD); + regeac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeac = 0x%x\n", regeac); - regeb4 = rtl_get_bbreg(hw, 0xeb4, BMASKDWORD); + regeb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xeb4 = 0x%x\n", regeb4); - regebc = rtl_get_bbreg(hw, 0xebc, BMASKDWORD); + regebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xebc = 0x%x\n", regebc); - regec4 = rtl_get_bbreg(hw, 0xec4, BMASKDWORD); + regec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xec4 = 0x%x\n", regec4); - regecc = rtl_get_bbreg(hw, 0xecc, BMASKDWORD); + regecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xecc = 0x%x\n", regecc); if (!(regeac & BIT(31)) && (((regeb4 & 0x03FF0000) >> 16) != 0x142)) @@ -1766,9 +1679,9 @@ static u8 _rtl92d_phy_pathb_iqk_5g_normal(struct ieee80211_hw *hw) } /* path B PA off */ - rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, MASKDWORD, rtlphy->iqk_bb_backup[0]); - rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, MASKDWORD, rtlphy->iqk_bb_backup[2]); return result; } @@ -1782,7 +1695,7 @@ static void _rtl92d_phy_save_adda_registers(struct ieee80211_hw *hw, RTPRINT(rtlpriv, FINIT, INIT_IQK, "Save ADDA parameters.\n"); for (i = 0; i < regnum; i++) - adda_backup[i] = rtl_get_bbreg(hw, adda_reg[i], BMASKDWORD); + adda_backup[i] = rtl_get_bbreg(hw, adda_reg[i], MASKDWORD); } static void _rtl92d_phy_save_mac_registers(struct ieee80211_hw *hw, @@ -1807,7 +1720,7 @@ static void _rtl92d_phy_reload_adda_registers(struct ieee80211_hw *hw, RTPRINT(rtlpriv, FINIT, INIT_IQK, "Reload ADDA power saving parameters !\n"); for (i = 0; i < regnum; i++) - rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, adda_backup[i]); + rtl_set_bbreg(hw, adda_reg[i], MASKDWORD, adda_backup[i]); } static void _rtl92d_phy_reload_mac_registers(struct ieee80211_hw *hw, @@ -1835,7 +1748,7 @@ static void _rtl92d_phy_path_adda_on(struct ieee80211_hw *hw, pathon = rtlpriv->rtlhal.interfaceindex == 0 ? 0x04db25a4 : 0x0b1b25a4; for (i = 0; i < IQK_ADDA_REG_NUM; i++) - rtl_set_bbreg(hw, adda_reg[i], BMASKDWORD, pathon); + rtl_set_bbreg(hw, adda_reg[i], MASKDWORD, pathon); } static void _rtl92d_phy_mac_setting_calibration(struct ieee80211_hw *hw, @@ -1858,9 +1771,9 @@ static void _rtl92d_phy_patha_standby(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path-A standby mode!\n"); - rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x0); - rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD, 0x00010000); - rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0); + rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, MASKDWORD, 0x00010000); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); } static void _rtl92d_phy_pimode_switch(struct ieee80211_hw *hw, bool pi_mode) @@ -1871,8 +1784,8 @@ static void _rtl92d_phy_pimode_switch(struct ieee80211_hw *hw, bool pi_mode) RTPRINT(rtlpriv, FINIT, INIT_IQK, "BB Switch to %s mode!\n", pi_mode ? "PI" : "SI"); mode = pi_mode ? 0x01000100 : 0x01000000; - rtl_set_bbreg(hw, 0x820, BMASKDWORD, mode); - rtl_set_bbreg(hw, 0x828, BMASKDWORD, mode); + rtl_set_bbreg(hw, 0x820, MASKDWORD, mode); + rtl_set_bbreg(hw, 0x828, MASKDWORD, mode); } static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], @@ -1903,7 +1816,7 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK for 2.4G :Start!!!\n"); if (t == 0) { - bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD); + bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "==>0x%08x\n", bbvalue); RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQ Calibration for %s\n", is2t ? "2T2R" : "1T1R"); @@ -1926,40 +1839,40 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], _rtl92d_phy_pimode_switch(hw, true); rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00); - rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600); - rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4); - rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22204000); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKDWORD, 0x03a05600); + rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, MASKDWORD, 0x000800e4); + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, MASKDWORD, 0x22204000); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f); if (is2t) { - rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XA_LSSIPARAMETER, MASKDWORD, 0x00010000); - rtl_set_bbreg(hw, RFPGA0_XB_LSSIPARAMETER, BMASKDWORD, + rtl_set_bbreg(hw, RFPGA0_XB_LSSIPARAMETER, MASKDWORD, 0x00010000); } /* MAC settings */ _rtl92d_phy_mac_setting_calibration(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); /* Page B init */ - rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000); + rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000); if (is2t) - rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000); + rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x0f600000); /* IQ calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK setting!\n"); - rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000); - rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x01007c00); - rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); + rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00); + rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x01004800); for (i = 0; i < retrycount; i++) { patha_ok = _rtl92d_phy_patha_iqk(hw, is2t); if (patha_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Success!!\n"); - result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & + result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & 0x3FF0000) >> 16; - result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & + result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; - result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) & + result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) & 0x3FF0000) >> 16; - result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) & + result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) & 0x3FF0000) >> 16; break; } else if (i == (retrycount - 1) && patha_ok == 0x01) { @@ -1967,9 +1880,9 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Only Tx Success!!\n"); - result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & + result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & 0x3FF0000) >> 16; - result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & + result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; } } @@ -1985,22 +1898,22 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK Success!!\n"); result[t][4] = (rtl_get_bbreg(hw, 0xeb4, - BMASKDWORD) & 0x3FF0000) >> 16; + MASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, - BMASKDWORD) & 0x3FF0000) >> 16; + MASKDWORD) & 0x3FF0000) >> 16; result[t][6] = (rtl_get_bbreg(hw, 0xec4, - BMASKDWORD) & 0x3FF0000) >> 16; + MASKDWORD) & 0x3FF0000) >> 16; result[t][7] = (rtl_get_bbreg(hw, 0xecc, - BMASKDWORD) & 0x3FF0000) >> 16; + MASKDWORD) & 0x3FF0000) >> 16; break; } else if (i == (retrycount - 1) && pathb_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B Only Tx IQK Success!!\n"); result[t][4] = (rtl_get_bbreg(hw, 0xeb4, - BMASKDWORD) & 0x3FF0000) >> 16; + MASKDWORD) & 0x3FF0000) >> 16; result[t][5] = (rtl_get_bbreg(hw, 0xebc, - BMASKDWORD) & 0x3FF0000) >> 16; + MASKDWORD) & 0x3FF0000) >> 16; } } if (0x00 == pathb_ok) @@ -2012,7 +1925,7 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK:Back to BB mode, load original value!\n"); - rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0); if (t != 0) { /* Switch back BB to SI mode after finish IQ Calibration. */ if (!rtlphy->rfpi_enable) @@ -2032,8 +1945,8 @@ static void _rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw, long result[][8], rtlphy->iqk_bb_backup, IQK_BB_REG_NUM - 1); /* load 0xe30 IQC default value */ - rtl_set_bbreg(hw, 0xe30, BMASKDWORD, 0x01008c00); - rtl_set_bbreg(hw, 0xe34, BMASKDWORD, 0x01008c00); + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x01008c00); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x01008c00); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "<==\n"); } @@ -2070,7 +1983,7 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw, RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK for 5G NORMAL:Start!!!\n"); mdelay(IQK_DELAY_TIME * 20); if (t == 0) { - bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, BMASKDWORD); + bbvalue = rtl_get_bbreg(hw, RFPGA0_RFMOD, MASKDWORD); RTPRINT(rtlpriv, FINIT, INIT_IQK, "==>0x%08x\n", bbvalue); RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQ Calibration for %s\n", is2t ? "2T2R" : "1T1R"); @@ -2100,38 +2013,38 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw, if (!rtlphy->rfpi_enable) _rtl92d_phy_pimode_switch(hw, true); rtl_set_bbreg(hw, RFPGA0_RFMOD, BIT(24), 0x00); - rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKDWORD, 0x03a05600); - rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, BMASKDWORD, 0x000800e4); - rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, BMASKDWORD, 0x22208000); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKDWORD, 0x03a05600); + rtl_set_bbreg(hw, ROFDM0_TRMUXPAR, MASKDWORD, 0x000800e4); + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, MASKDWORD, 0x22208000); rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER4, 0xf00000, 0x0f); /* Page B init */ - rtl_set_bbreg(hw, 0xb68, BMASKDWORD, 0x0f600000); + rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000); if (is2t) - rtl_set_bbreg(hw, 0xb6c, BMASKDWORD, 0x0f600000); + rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x0f600000); /* IQ calibration setting */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK setting!\n"); - rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0x80800000); - rtl_set_bbreg(hw, 0xe40, BMASKDWORD, 0x10007c00); - rtl_set_bbreg(hw, 0xe44, BMASKDWORD, 0x01004800); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); + rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x10007c00); + rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x01004800); patha_ok = _rtl92d_phy_patha_iqk_5g_normal(hw, is2t); if (patha_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Success!!\n"); - result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & + result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & 0x3FF0000) >> 16; - result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & + result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; - result[t][2] = (rtl_get_bbreg(hw, 0xea4, BMASKDWORD) & + result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) & 0x3FF0000) >> 16; - result[t][3] = (rtl_get_bbreg(hw, 0xeac, BMASKDWORD) & + result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) & 0x3FF0000) >> 16; } else if (patha_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Only Tx Success!!\n"); - result[t][0] = (rtl_get_bbreg(hw, 0xe94, BMASKDWORD) & + result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & 0x3FF0000) >> 16; - result[t][1] = (rtl_get_bbreg(hw, 0xe9c, BMASKDWORD) & + result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path A IQK Fail!!\n"); @@ -2144,20 +2057,20 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw, if (pathb_ok == 0x03) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B IQK Success!!\n"); - result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) & + result[t][4] = (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) & 0x3FF0000) >> 16; - result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) & + result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) & 0x3FF0000) >> 16; - result[t][6] = (rtl_get_bbreg(hw, 0xec4, BMASKDWORD) & + result[t][6] = (rtl_get_bbreg(hw, 0xec4, MASKDWORD) & 0x3FF0000) >> 16; - result[t][7] = (rtl_get_bbreg(hw, 0xecc, BMASKDWORD) & + result[t][7] = (rtl_get_bbreg(hw, 0xecc, MASKDWORD) & 0x3FF0000) >> 16; } else if (pathb_ok == 0x01) { /* Tx IQK OK */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "Path B Only Tx IQK Success!!\n"); - result[t][4] = (rtl_get_bbreg(hw, 0xeb4, BMASKDWORD) & + result[t][4] = (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) & 0x3FF0000) >> 16; - result[t][5] = (rtl_get_bbreg(hw, 0xebc, BMASKDWORD) & + result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) & 0x3FF0000) >> 16; } else { RTPRINT(rtlpriv, FINIT, INIT_IQK, @@ -2168,7 +2081,7 @@ static void _rtl92d_phy_iq_calibrate_5g_normal(struct ieee80211_hw *hw, /* Back to BB mode, load original value */ RTPRINT(rtlpriv, FINIT, INIT_IQK, "IQK:Back to BB mode, load original value!\n"); - rtl_set_bbreg(hw, 0xe28, BMASKDWORD, 0); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0); if (t != 0) { if (is2t) _rtl92d_phy_reload_adda_registers(hw, iqk_bb_reg, @@ -2268,7 +2181,7 @@ static void _rtl92d_phy_patha_fill_iqk_matrix(struct ieee80211_hw *hw, return; } else if (iqk_ok) { oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE, - BMASKDWORD) >> 22) & 0x3FF; /* OFDM0_D */ + MASKDWORD) >> 22) & 0x3FF; /* OFDM0_D */ val_x = result[final_candidate][0]; if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; @@ -2299,7 +2212,7 @@ static void _rtl92d_phy_patha_fill_iqk_matrix(struct ieee80211_hw *hw, ((val_y * oldval_0 >> 7) & 0x1)); RTPRINT(rtlpriv, FINIT, INIT_IQK, "0xC80 = 0x%x\n", rtl_get_bbreg(hw, ROFDM0_XATxIQIMBALANCE, - BMASKDWORD)); + MASKDWORD)); if (txonly) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "only Tx OK\n"); return; @@ -2327,7 +2240,7 @@ static void _rtl92d_phy_pathb_fill_iqk_matrix(struct ieee80211_hw *hw, return; } else if (iqk_ok) { oldval_1 = (rtl_get_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, - BMASKDWORD) >> 22) & 0x3FF; + MASKDWORD) >> 22) & 0x3FF; val_x = result[final_candidate][4]; if ((val_x & 0x00000200) != 0) val_x = val_x | 0xFFFFFC00; @@ -2685,7 +2598,7 @@ static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t) rf_mode[index] = rtl_read_byte(rtlpriv, offset); /* 2. Set RF mode = standby mode */ rtl_set_rfreg(hw, (enum radio_path)index, RF_AC, - BRFREGOFFSETMASK, 0x010000); + RFREG_OFFSET_MASK, 0x010000); if (rtlpci->init_ready) { /* switch CV-curve control by LC-calibration */ rtl_set_rfreg(hw, (enum radio_path)index, RF_SYN_G7, @@ -2695,16 +2608,16 @@ static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t) 0x08000, 0x01); } u4tmp = rtl_get_rfreg(hw, (enum radio_path)index, RF_SYN_G6, - BRFREGOFFSETMASK); + RFREG_OFFSET_MASK); while ((!(u4tmp & BIT(11))) && timecount <= timeout) { mdelay(50); timecount += 50; u4tmp = rtl_get_rfreg(hw, (enum radio_path)index, - RF_SYN_G6, BRFREGOFFSETMASK); + RF_SYN_G6, RFREG_OFFSET_MASK); } RTPRINT(rtlpriv, FINIT, INIT_IQK, "PHY_LCK finish delay for %d ms=2\n", timecount); - u4tmp = rtl_get_rfreg(hw, index, RF_SYN_G4, BRFREGOFFSETMASK); + u4tmp = rtl_get_rfreg(hw, index, RF_SYN_G4, RFREG_OFFSET_MASK); if (index == 0 && rtlhal->interfaceindex == 0) { RTPRINT(rtlpriv, FINIT, INIT_IQK, "path-A / 5G LCK\n"); @@ -2724,9 +2637,9 @@ static void _rtl92d_phy_lc_calibrate_sw(struct ieee80211_hw *hw, bool is2t) 0x7f, i); rtl_set_rfreg(hw, (enum radio_path)index, 0x4D, - BRFREGOFFSETMASK, 0x0); + RFREG_OFFSET_MASK, 0x0); readval = rtl_get_rfreg(hw, (enum radio_path)index, - 0x4F, BRFREGOFFSETMASK); + 0x4F, RFREG_OFFSET_MASK); curvecount_val[2 * i + 1] = (readval & 0xfffe0) >> 5; /* reg 0x4f [4:0] */ /* reg 0x50 [19:10] */ @@ -2940,7 +2853,7 @@ static bool _rtl92d_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, } rtl_set_rfreg(hw, (enum radio_path)rfpath, currentcmd->para1, - BRFREGOFFSETMASK, + RFREG_OFFSET_MASK, rtlphy->rfreg_chnlval[rfpath]); _rtl92d_phy_reload_imr_setting(hw, channel, rfpath); @@ -2988,7 +2901,7 @@ u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw) if (rtlhal->macphymode == SINGLEMAC_SINGLEPHY && rtlhal->bandset == BAND_ON_BOTH) { ret_value = rtl_get_bbreg(hw, RFPGA0_XAB_RFPARAMETER, - BMASKDWORD); + MASKDWORD); if (rtlphy->current_channel > 14 && !(ret_value & BIT(0))) rtl92d_phy_switch_wirelessband(hw, BAND_ON_5G); else if (rtlphy->current_channel <= 14 && (ret_value & BIT(0))) @@ -3140,7 +3053,7 @@ static void _rtl92d_phy_set_rfsleep(struct ieee80211_hw *hw) /* a. TXPAUSE 0x522[7:0] = 0xFF Pause MAC TX queue */ rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); /* b. RF path 0 offset 0x00 = 0x00 disable RF */ - rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); /* c. APSD_CTRL 0x600[7:0] = 0x40 */ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); /* d. APSD_CTRL 0x600[7:0] = 0x00 @@ -3148,12 +3061,12 @@ static void _rtl92d_phy_set_rfsleep(struct ieee80211_hw *hw) * RF path 0 offset 0x00 = 0x00 * APSD_CTRL 0x600[7:0] = 0x40 * */ - u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK); + u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); while (u4btmp != 0 && delay > 0) { rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0); - rtl_set_rfreg(hw, RF90_PATH_A, 0x00, BRFREGOFFSETMASK, 0x00); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); - u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, BRFREGOFFSETMASK); + u4btmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); delay--; } if (delay == 0) { @@ -3496,9 +3409,9 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) /* 5G LAN ON */ rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0xa); /* TX BB gain shift*1,Just for testchip,0xc80,0xc88 */ - rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, + rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, MASKDWORD, 0x40000100); - rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, + rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, MASKDWORD, 0x40000100); if (rtlhal->macphymode == DUALMAC_DUALPHY) { rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, @@ -3552,16 +3465,16 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) rtl_set_bbreg(hw, 0xB30, 0x00F00000, 0x0); /* TX BB gain shift,Just for testchip,0xc80,0xc88 */ if (rtlefuse->internal_pa_5g[0]) - rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, + rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, MASKDWORD, 0x2d4000b5); else - rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, BMASKDWORD, + rtl_set_bbreg(hw, ROFDM0_XATxIQIMBALANCE, MASKDWORD, 0x20000080); if (rtlefuse->internal_pa_5g[1]) - rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, + rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, MASKDWORD, 0x2d4000b5); else - rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, BMASKDWORD, + rtl_set_bbreg(hw, ROFDM0_XBTxIQIMBALANCE, MASKDWORD, 0x20000080); if (rtlhal->macphymode == DUALMAC_DUALPHY) { rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, @@ -3588,8 +3501,8 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) } } /* update IQK related settings */ - rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, BMASKDWORD, 0x40000100); - rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, BMASKDWORD, 0x40000100); + rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, MASKDWORD, 0x40000100); + rtl_set_bbreg(hw, ROFDM0_XBRXIQIMBALANCE, MASKDWORD, 0x40000100); rtl_set_bbreg(hw, ROFDM0_XCTxAFE, 0xF0000000, 0x00); rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(30) | BIT(28) | BIT(26) | BIT(24), 0x00); @@ -3618,7 +3531,7 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) /* DMDP */ if (rtlphy->rf_type == RF_1T1R) { /* Use antenna 0,0xc04,0xd04 */ - rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x11); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x11); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x1); /* enable ad/da clock1 for dual-phy reg0x888 */ @@ -3640,7 +3553,7 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) } else { /* Single PHY */ /* Use antenna 0 & 1,0xc04,0xd04 */ - rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, BMASKBYTE0, 0x33); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x33); rtl_set_bbreg(hw, ROFDM1_TRXPATHENABLE, BDWORD, 0x3); /* disable ad/da clock1,0x888 */ rtl_set_bbreg(hw, RFPGA0_ADDALLOCKEN, BIT(12) | BIT(13), 0); @@ -3648,9 +3561,9 @@ void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw) for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; rfpath++) { rtlphy->rfreg_chnlval[rfpath] = rtl_get_rfreg(hw, rfpath, - RF_CHNLBW, BRFREGOFFSETMASK); + RF_CHNLBW, RFREG_OFFSET_MASK); rtlphy->reg_rf3c[rfpath] = rtl_get_rfreg(hw, rfpath, 0x3C, - BRFREGOFFSETMASK); + RFREG_OFFSET_MASK); } for (i = 0; i < 2; i++) RT_TRACE(rtlpriv, COMP_RF, DBG_LOUD, "RF 0x18 = 0x%x\n", diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.h b/drivers/net/wireless/rtlwifi/rtl8192de/phy.h index f074952bf25..48d5c6835b6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.h @@ -39,9 +39,7 @@ #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 -#define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 -#define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 @@ -127,34 +125,32 @@ static inline void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw, *flag); } -extern u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -extern void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -extern u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); -extern bool rtl92d_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl92d_phy_bb_config(struct ieee80211_hw *hw); -extern bool rtl92d_phy_rf_config(struct ieee80211_hw *hw); -extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, - enum radio_path rfpath); -extern void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -extern void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); -extern void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); -extern void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); -extern u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw); +u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +bool rtl92d_phy_mac_config(struct ieee80211_hw *hw); +bool rtl92d_phy_bb_config(struct ieee80211_hw *hw); +bool rtl92d_phy_rf_config(struct ieee80211_hw *hw); +bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); +void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw); bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum rf_content content, enum radio_path rfpath); bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); +bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw); void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw); @@ -173,6 +169,5 @@ void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw, unsigned long *flag); u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl); void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel); -void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h index b7498c5bafc..7f29b8d765b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h @@ -1295,18 +1295,4 @@ #define BWORD1 0xc #define BDWORD 0xf -#define BMASKBYTE0 0xff -#define BMASKBYTE1 0xff00 -#define BMASKBYTE2 0xff0000 -#define BMASKBYTE3 0xff000000 -#define BMASKHWORD 0xffff0000 -#define BMASKLWORD 0x0000ffff -#define BMASKDWORD 0xffffffff -#define BMASK12BITS 0xfff -#define BMASKH4BITS 0xf0000000 -#define BMASKOFDM_D 0xffc00000 -#define BMASKCCK 0x3f3f3f3f - -#define BRFREGOFFSETMASK 0xfffff - #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/rf.c b/drivers/net/wireless/rtlwifi/rtl8192de/rf.c index 20144e0b414..6a6ac540d5b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/rf.c @@ -125,7 +125,7 @@ void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, } tmpval = tx_agc[RF90_PATH_A] & 0xff; - rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, BMASKBYTE1, tmpval); + rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_A_CCK1_MCS32); @@ -135,7 +135,7 @@ void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); tmpval = tx_agc[RF90_PATH_B] >> 24; - rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, BMASKBYTE0, tmpval); + rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, RTXAGC_B_CCK11_A_CCK2_11); @@ -360,7 +360,7 @@ static void _rtl92d_write_ofdm_power_reg(struct ieee80211_hw *hw, regoffset = regoffset_a[index]; else regoffset = regoffset_b[index]; - rtl_set_bbreg(hw, regoffset, BMASKDWORD, writeval); + rtl_set_bbreg(hw, regoffset, MASKDWORD, writeval); RTPRINT(rtlpriv, FPHY, PHY_TXPWR, "Set 0x%x = %08x\n", regoffset, writeval); if (((get_rf_type(rtlphy) == RF_2T2R) && diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/rf.h b/drivers/net/wireless/rtlwifi/rtl8192de/rf.h index 0fe1a48593e..7303d12c266 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/rf.h @@ -30,15 +30,13 @@ #ifndef __RTL92D_RF_H__ #define __RTL92D_RF_H__ -extern void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); -extern bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw); -extern bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0); -extern void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, - bool bmac0); +void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw); +bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0); +void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c index c18c04bf0c1..edab5a5351b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -236,7 +237,7 @@ static struct rtl_hal_ops rtl8192de_hal_ops = { .set_bw_mode = rtl92d_phy_set_bw_mode, .switch_channel = rtl92d_phy_sw_chnl, .dm_watchdog = rtl92d_dm_watchdog, - .scan_operation_backup = rtl92d_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl92d_phy_set_rf_power_state, .led_control = rtl92de_led_control, .set_desc = rtl92de_set_desc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index b8ec718a0fa..99c2ab5dfce 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -525,8 +525,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, p_drvinfo); } /*rx_status->qual = stats->signal; */ - rx_status->signal = stats->rssi + 10; - /*rx_status->noise = -stats->noise; */ + rx_status->signal = stats->recvsignalpower + 10; return true; } @@ -546,7 +545,7 @@ static void _rtl92de_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) @@ -787,7 +786,8 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, SET_TX_DESC_OWN(pdesc, 1); } -void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) +void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val) { if (istx) { switch (desc_name) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h index c1b5dfb79d5..fb5cf0634e8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h @@ -728,8 +728,8 @@ struct rx_desc_92d { } __packed; void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, - struct ieee80211_hdr *hdr, - u8 *pdesc, struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 *pdesc, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); @@ -737,7 +737,8 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); -void rtl92de_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); +void rtl92de_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); u32 rtl92de_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl92de_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c index 4f461786a7e..1c7101bcd79 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c @@ -251,7 +251,7 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 e_aci = *val; rtl92s_dm_init_edca_turbo(hw); - if (rtlpci->acm_method != eAcmWay2_SW) + if (rtlpci->acm_method != EACMWAY2_SW) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, &e_aci); @@ -413,20 +413,18 @@ void rtl92se_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) (u8 *)(&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&ppsc->fwctrl_psmode)); + &ppsc->fwctrl_psmode); - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, + &rpwm_val); } else { rpwm_val = 0x0C; /* RF on */ fw_pwrmode = FW_PS_ACTIVE_MODE; fw_current_inps = false; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&fw_pwrmode)); + &rpwm_val); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, + &fw_pwrmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, @@ -955,7 +953,7 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); u8 tmp_byte = 0; - + unsigned long flags; bool rtstatus = true; u8 tmp_u1b; int err = false; @@ -967,6 +965,16 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) rtlpci->being_init_adapter = true; + /* As this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); + rtlpriv->intf_ops->disable_aspm(hw); /* 1. MAC Initialize */ @@ -984,7 +992,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now... " "Please copy FW into /lib/firmware/rtlwifi\n"); - return 1; + err = 1; + goto exit; } /* After FW download, we have to reset MAC register */ @@ -997,7 +1006,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) /* 3. Initialize MAC/PHY Config by MACPHY_reg.txt */ if (!rtl92s_phy_mac_config(hw)) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "MAC Config failed\n"); - return rtstatus; + err = rtstatus; + goto exit; } /* because last function modify RCR, so we update @@ -1016,7 +1026,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) /* 4. Initialize BB After MAC Config PHY_reg.txt, AGC_Tab.txt */ if (!rtl92s_phy_bb_config(hw)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, "BB Config failed\n"); - return rtstatus; + err = rtstatus; + goto exit; } /* 5. Initiailze RF RAIO_A.txt RF RAIO_B.txt */ @@ -1033,7 +1044,8 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) if (!rtl92s_phy_rf_config(hw)) { RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "RF Config failed\n"); - return rtstatus; + err = rtstatus; + goto exit; } /* After read predefined TXT, we must set BB/MAC/RF @@ -1122,8 +1134,9 @@ int rtl92se_hw_init(struct ieee80211_hw *hw) rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_ON); rtl92s_dm_init(hw); +exit: + local_irq_restore(flags); rtlpci->being_init_adapter = false; - return err; } @@ -1135,12 +1148,13 @@ void rtl92se_set_mac_addr(struct rtl_io *io, const u8 *addr) void rtl92se_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - u32 reg_rcr = rtlpci->receive_config; + u32 reg_rcr; if (rtlpriv->psc.rfpwr_state != ERFON) return; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); + if (check_bssid) { reg_rcr |= (RCR_CBSSID); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); @@ -2530,23 +2544,3 @@ void rtl92se_resume(struct ieee80211_hw *hw) pci_write_config_dword(rtlpci->pdev, 0x40, val & 0xffff00ff); } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, RCR, rtlpci->receive_config); - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config=0x%08X, write_into_reg=%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/hw.h b/drivers/net/wireless/rtlwifi/rtl8192se/hw.h index da48aa8cbe6..4cacee10f31 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.h @@ -74,7 +74,5 @@ void rtl92se_set_key(struct ieee80211_hw *hw, u8 enc_algo, bool is_wepkey, bool clear_all); void rtl92se_suspend(struct ieee80211_hw *hw); void rtl92se_resume(struct ieee80211_hw *hw); -void rtl92se_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c index 9c092e6eb3f..77c5b5f3524 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/phy.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../pci.h" #include "../ps.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -833,18 +834,7 @@ static bool _rtl92s_phy_config_bb(struct ieee80211_hw *hw, u8 configtype) if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_len; i = i + 2) { - if (phy_reg_table[i] == 0xfe) - mdelay(50); - else if (phy_reg_table[i] == 0xfd) - mdelay(5); - else if (phy_reg_table[i] == 0xfc) - mdelay(1); - else if (phy_reg_table[i] == 0xfb) - udelay(50); - else if (phy_reg_table[i] == 0xfa) - udelay(5); - else if (phy_reg_table[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_reg_table[i]); /* Add delay for ECS T20 & LG malow platform, */ udelay(1); @@ -886,18 +876,7 @@ static bool _rtl92s_phy_set_bb_to_diff_rf(struct ieee80211_hw *hw, if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray2xtxr_len; i = i + 3) { - if (phy_regarray2xtxr_table[i] == 0xfe) - mdelay(50); - else if (phy_regarray2xtxr_table[i] == 0xfd) - mdelay(5); - else if (phy_regarray2xtxr_table[i] == 0xfc) - mdelay(1); - else if (phy_regarray2xtxr_table[i] == 0xfb) - udelay(50); - else if (phy_regarray2xtxr_table[i] == 0xfa) - udelay(5); - else if (phy_regarray2xtxr_table[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray2xtxr_table[i]); rtl92s_phy_set_bb_reg(hw, phy_regarray2xtxr_table[i], phy_regarray2xtxr_table[i + 1], @@ -920,18 +899,7 @@ static bool _rtl92s_phy_config_bb_with_pg(struct ieee80211_hw *hw, if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_pg_len; i = i + 3) { - if (phy_table_pg[i] == 0xfe) - mdelay(50); - else if (phy_table_pg[i] == 0xfd) - mdelay(5); - else if (phy_table_pg[i] == 0xfc) - mdelay(1); - else if (phy_table_pg[i] == 0xfb) - udelay(50); - else if (phy_table_pg[i] == 0xfa) - udelay(5); - else if (phy_table_pg[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_table_pg[i]); _rtl92s_store_pwrindex_diffrate_offset(hw, phy_table_pg[i], @@ -1034,28 +1002,9 @@ u8 rtl92s_phy_config_rf(struct ieee80211_hw *hw, enum radio_path rfpath) switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radio_a_tblen; i = i + 2) { - if (radio_a_table[i] == 0xfe) - /* Delay specific ms. Only RF configuration - * requires delay. */ - mdelay(50); - else if (radio_a_table[i] == 0xfd) - mdelay(5); - else if (radio_a_table[i] == 0xfc) - mdelay(1); - else if (radio_a_table[i] == 0xfb) - udelay(50); - else if (radio_a_table[i] == 0xfa) - udelay(5); - else if (radio_a_table[i] == 0xf9) - udelay(1); - else - rtl92s_phy_set_rf_reg(hw, rfpath, - radio_a_table[i], - MASK20BITS, - radio_a_table[i + 1]); + rtl_rfreg_delay(hw, rfpath, radio_a_table[i], + MASK20BITS, radio_a_table[i + 1]); - /* Add delay for ECS T20 & LG malow platform */ - udelay(1); } /* PA Bias current for inferiority IC */ @@ -1063,28 +1012,8 @@ u8 rtl92s_phy_config_rf(struct ieee80211_hw *hw, enum radio_path rfpath) break; case RF90_PATH_B: for (i = 0; i < radio_b_tblen; i = i + 2) { - if (radio_b_table[i] == 0xfe) - /* Delay specific ms. Only RF configuration - * requires delay.*/ - mdelay(50); - else if (radio_b_table[i] == 0xfd) - mdelay(5); - else if (radio_b_table[i] == 0xfc) - mdelay(1); - else if (radio_b_table[i] == 0xfb) - udelay(50); - else if (radio_b_table[i] == 0xfa) - udelay(5); - else if (radio_b_table[i] == 0xf9) - udelay(1); - else - rtl92s_phy_set_rf_reg(hw, rfpath, - radio_b_table[i], - MASK20BITS, - radio_b_table[i + 1]); - - /* Add delay for ECS T20 & LG malow platform */ - udelay(1); + rtl_rfreg_delay(hw, rfpath, radio_b_table[i], + MASK20BITS, radio_b_table[i + 1]); } break; case RF90_PATH_C: diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h index 84d1181795b..e13043479b7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h @@ -425,14 +425,9 @@ #define EXT_IMEM_CODE_DONE BIT(2) #define IMEM_CHK_RPT BIT(1) #define IMEM_CODE_DONE BIT(0) -#define IMEM_CODE_DONE BIT(0) -#define IMEM_CHK_RPT BIT(1) #define EMEM_CODE_DONE BIT(2) #define EMEM_CHK_RPT BIT(3) -#define DMEM_CODE_DONE BIT(4) #define IMEM_RDY BIT(5) -#define BASECHG BIT(6) -#define FWRDY BIT(7) #define LOAD_FW_READY (IMEM_CODE_DONE | \ IMEM_CHK_RPT | \ EMEM_CODE_DONE | \ @@ -1170,16 +1165,4 @@ #define BTX_AGCRATECCK 0x7f00 -#define MASKBYTE0 0xff -#define MASKBYTE1 0xff00 -#define MASKBYTE2 0xff0000 -#define MASKBYTE3 0xff000000 -#define MASKHWORD 0xffff0000 -#define MASKLWORD 0x0000ffff -#define MASKDWORD 0xffffffff - -#define MAKS12BITS 0xfffff -#define MASK20BITS 0xfffff -#define RFREG_OFFSET_MASK 0xfffff - #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/rf.c b/drivers/net/wireless/rtlwifi/rtl8192se/rf.c index 5061f1db3f0..78a81c1e390 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/rf.c @@ -52,7 +52,7 @@ static void _rtl92s_get_powerbase(struct ieee80211_hw *hw, u8 *p_pwrlevel, /* We only care about the path A for legacy. */ if (rtlefuse->eeprom_version < 2) { pwrbase0 = pwrlevel[0] + (rtlefuse->legacy_httxpowerdiff & 0xf); - } else if (rtlefuse->eeprom_version >= 2) { + } else { legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff [RF90_PATH_A][chnl - 1]; @@ -265,7 +265,7 @@ static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw, rtlefuse->pwrgroup_ht40 [RF90_PATH_A][chnl - 1]) { pwrdiff_limit[i] = - rtlefuse->pwrgroup_ht20 + rtlefuse->pwrgroup_ht40 [RF90_PATH_A][chnl - 1]; } } else { diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index 2e8e6f8d2d5..1bff2a0f760 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -290,7 +290,6 @@ static struct rtl_hal_ops rtl8192se_hal_ops = { .enable_hw_sec = rtl92se_enable_hw_security_config, .set_key = rtl92se_set_key, .init_sw_leds = rtl92se_init_sw_leds, - .allow_all_destaddr = rtl92se_allow_all_destaddr, .get_bbreg = rtl92s_phy_query_bb_reg, .set_bbreg = rtl92s_phy_set_bb_reg, .get_rfreg = rtl92s_phy_query_rf_reg, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index c7095118de6..2b3c78baa9f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -49,6 +49,12 @@ static u8 _rtl92se_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 skb_queue) if (ieee80211_is_nullfunc(fc)) return QSLT_HIGH; + /* Kernel commit 1bf4bbb4024dcdab changed EAPOL packets to use + * queue V0 at priority 7; however, the RTL8192SE appears to have + * that queue at priority 6 + */ + if (skb->priority == 7) + return QSLT_VO; return skb->priority; } @@ -310,7 +316,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, /* during testing, hdr was NULL here */ return false; } - if ((ieee80211_is_robust_mgmt_frame(hdr)) && + if ((_ieee80211_is_robust_mgmt_frame(hdr)) && (ieee80211_has_protected(hdr->frame_control))) rx_status->flag &= ~RX_FLAG_DECRYPTED; else @@ -329,15 +335,14 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, } /*rx_status->qual = stats->signal; */ - rx_status->signal = stats->rssi + 10; - /*rx_status->noise = -stats->noise; */ + rx_status->signal = stats->recvsignalpower + 10; return true; } void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) @@ -574,7 +579,8 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, } } -void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) +void rtl92se_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val) { if (istx) { switch (desc_name) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.h b/drivers/net/wireless/rtlwifi/rtl8192se/trx.h index 64dd66f287c..5a13f17e3b4 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.h @@ -29,8 +29,9 @@ #ifndef __REALTEK_PCI92SE_TRX_H__ #define __REALTEK_PCI92SE_TRX_H__ -void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, - u8 *pdesc, struct ieee80211_tx_info *info, +void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, + struct ieee80211_hdr *hdr, u8 *pdesc, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); @@ -39,7 +40,8 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, bool firstseg, bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); -void rtl92se_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); +void rtl92se_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); u32 rtl92se_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl92se_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile index 4ed731f09b1..9c34a85fdb8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile @@ -10,7 +10,6 @@ rtl8723ae-objs := \ led.o \ phy.o \ pwrseq.o \ - pwrseqcmd.o \ rf.o \ sw.o \ table.o \ diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h index 8c110356dff..debe261a7ee 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h @@ -46,11 +46,6 @@ #define E_CUT_VERSION BIT(14) #define RF_RL_ID (BIT(31)|BIT(30)|BIT(29)|BIT(28)) -enum version_8723e { - VERSION_TEST_UMC_CHIP_8723 = 0x0081, - VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089, - VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089, -}; /* MASK */ #define IC_TYPE_MASK (BIT(0)|BIT(1)|BIT(2)) diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c index a36eee28f9e..25cc83058b0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c @@ -35,6 +35,7 @@ #include "def.h" #include "phy.h" #include "dm.h" +#include "../rtl8723com/dm_common.h" #include "fw.h" #include "hal_btc.h" @@ -483,16 +484,6 @@ static void rtl8723ae_dm_dig(struct ieee80211_hw *hw) rtl8723ae_dm_ctrl_initgain_by_twoport(hw); } -static void rtl8723ae_dm_init_dynamic_txpower(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - - rtlpriv->dm.dynamic_txpower_enable = false; - - rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; - rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; -} - static void rtl8723ae_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -585,19 +576,6 @@ void rtl8723ae_dm_write_dig(struct ieee80211_hw *hw) } } -static void rtl8723ae_dm_pwdmonitor(struct ieee80211_hw *hw) -{ -} - -void rtl8723ae_dm_init_edca_turbo(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - - rtlpriv->dm.current_turbo_edca = false; - rtlpriv->dm.is_any_nonbepkts = false; - rtlpriv->dm.is_cur_rdlstate = false; -} - static void rtl8723ae_dm_check_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -669,9 +647,8 @@ static void rtl8723ae_dm_check_edca_turbo(struct ieee80211_hw *hw) } else { if (rtlpriv->dm.current_turbo_edca) { u8 tmp = AC0_BE; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_AC_PARAM, - (u8 *) (&tmp)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, + &tmp); rtlpriv->dm.current_turbo_edca = false; } } @@ -778,17 +755,6 @@ static void rtl8723ae_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw) } } -static void rtl8723ae_dm_init_dynamic_bpowersaving(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - - rtlpriv->dm_pstable.pre_ccastate = CCA_MAX; - rtlpriv->dm_pstable.cur_ccasate = CCA_MAX; - rtlpriv->dm_pstable.pre_rfstate = RF_MAX; - rtlpriv->dm_pstable.cur_rfstate = RF_MAX; - rtlpriv->dm_pstable.rssi_val_min = 0; -} - void rtl8723ae_dm_rf_saving(struct ieee80211_hw *hw, u8 force_in_normal) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -905,11 +871,11 @@ void rtl8723ae_dm_init(struct ieee80211_hw *hw) rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; rtl8723ae_dm_diginit(hw); - rtl8723ae_dm_init_dynamic_txpower(hw); - rtl8723ae_dm_init_edca_turbo(hw); + rtl8723_dm_init_dynamic_txpower(hw); + rtl8723_dm_init_edca_turbo(hw); rtl8723ae_dm_init_rate_adaptive_mask(hw); rtl8723ae_dm_initialize_txpower_tracking(hw); - rtl8723ae_dm_init_dynamic_bpowersaving(hw); + rtl8723_dm_init_dynamic_bb_powersaving(hw); } void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw) @@ -930,7 +896,6 @@ void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw) if ((ppsc->rfpwr_state == ERFON) && ((!fw_current_inpsmode) && fw_ps_awake) && (!ppsc->rfchange_inprogress)) { - rtl8723ae_dm_pwdmonitor(hw); rtl8723ae_dm_dig(hw); rtl8723ae_dm_false_alarm_counter_statistics(hw); rtl8723ae_dm_dynamic_bpowersaving(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h index a372b020445..d253bb53d03 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h @@ -147,7 +147,6 @@ enum dm_dig_connect_e { void rtl8723ae_dm_init(struct ieee80211_hw *hw); void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw); void rtl8723ae_dm_write_dig(struct ieee80211_hw *hw); -void rtl8723ae_dm_init_edca_turbo(struct ieee80211_hw *hw); void rtl8723ae_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); void rtl8723ae_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal); void rtl8723ae_dm_bt_coexist(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c index ba1502b172a..728b7563ad3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c @@ -34,199 +34,7 @@ #include "reg.h" #include "def.h" #include "fw.h" - -static void _rtl8723ae_enable_fw_download(struct ieee80211_hw *hw, bool enable) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 tmp; - if (enable) { - tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); - rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04); - - tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); - rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01); - - tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2); - rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7); - } else { - tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); - rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe); - - rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00); - } -} - -static void _rtl8723ae_fw_block_write(struct ieee80211_hw *hw, - const u8 *buffer, u32 size) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 blockSize = sizeof(u32); - u8 *bufferPtr = (u8 *) buffer; - u32 *pu4BytePtr = (u32 *) buffer; - u32 i, offset, blockCount, remainSize; - - blockCount = size / blockSize; - remainSize = size % blockSize; - - for (i = 0; i < blockCount; i++) { - offset = i * blockSize; - rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset), - *(pu4BytePtr + i)); - } - - if (remainSize) { - offset = blockCount * blockSize; - bufferPtr += offset; - for (i = 0; i < remainSize; i++) { - rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS + - offset + i), *(bufferPtr + i)); - } - } -} - -static void _rtl8723ae_fw_page_write(struct ieee80211_hw *hw, - u32 page, const u8 *buffer, u32 size) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 value8; - u8 u8page = (u8) (page & 0x07); - - value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page; - - rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8); - _rtl8723ae_fw_block_write(hw, buffer, size); -} - -static void _rtl8723ae_write_fw(struct ieee80211_hw *hw, - enum version_8723e version, u8 *buffer, - u32 size) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 *bufferPtr = (u8 *) buffer; - u32 page_nums, remain_size; - u32 page, offset; - - RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size); - - page_nums = size / FW_8192C_PAGE_SIZE; - remain_size = size % FW_8192C_PAGE_SIZE; - - if (page_nums > 6) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Page numbers should not be greater then 6\n"); - } - - for (page = 0; page < page_nums; page++) { - offset = page * FW_8192C_PAGE_SIZE; - _rtl8723ae_fw_page_write(hw, page, (bufferPtr + offset), - FW_8192C_PAGE_SIZE); - } - - if (remain_size) { - offset = page_nums * FW_8192C_PAGE_SIZE; - page = page_nums; - _rtl8723ae_fw_page_write(hw, page, (bufferPtr + offset), - remain_size); - } - - RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW write done.\n"); -} - -static int _rtl8723ae_fw_free_to_go(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - int err = -EIO; - u32 counter = 0; - u32 value32; - - do { - value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); - } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) && - (!(value32 & FWDL_ChkSum_rpt))); - - if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "chksum report faill ! REG_MCUFWDL:0x%08x .\n", - value32); - goto exit; - } - - RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, - "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32); - - value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); - value32 |= MCUFWDL_RDY; - value32 &= ~WINTINI_RDY; - rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); - - counter = 0; - - do { - value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); - if (value32 & WINTINI_RDY) { - RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, - "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n", - value32); - err = 0; - goto exit; - } - - mdelay(FW_8192C_POLLING_DELAY); - - } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT); - - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", value32); - -exit: - return err; -} - -int rtl8723ae_download_fw(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - struct rtl8723ae_firmware_header *pfwheader; - u8 *pfwdata; - u32 fwsize; - int err; - enum version_8723e version = rtlhal->version; - - if (!rtlhal->pfirmware) - return 1; - - pfwheader = (struct rtl8723ae_firmware_header *)rtlhal->pfirmware; - pfwdata = (u8 *) rtlhal->pfirmware; - fwsize = rtlhal->fwsize; - - if (IS_FW_HEADER_EXIST(pfwheader)) { - RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, - "Firmware Version(%d), Signature(%#x),Size(%d)\n", - pfwheader->version, pfwheader->signature, - (int)sizeof(struct rtl8723ae_firmware_header)); - - pfwdata = pfwdata + sizeof(struct rtl8723ae_firmware_header); - fwsize = fwsize - sizeof(struct rtl8723ae_firmware_header); - } - - if (rtl_read_byte(rtlpriv, REG_MCUFWDL)&BIT(7)) { - rtl8723ae_firmware_selfreset(hw); - rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); - } - _rtl8723ae_enable_fw_download(hw, true); - _rtl8723ae_write_fw(hw, version, pfwdata, fwsize); - _rtl8723ae_enable_fw_download(hw, false); - - err = _rtl8723ae_fw_free_to_go(hw); - if (err) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Firmware is not ready to run!\n"); - } else { - RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, - "Firmware is ready to run!\n"); - } - return 0; -} +#include "../rtl8723com/fw_common.h" static bool rtl8723ae_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum) { @@ -463,50 +271,6 @@ void rtl8723ae_fill_h2c_cmd(struct ieee80211_hw *hw, return; } -void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw) -{ - u8 u1tmp; - u8 delay = 100; - struct rtl_priv *rtlpriv = rtl_priv(hw); - - rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20); - u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); - - while (u1tmp & BIT(2)) { - delay--; - if (delay == 0) - break; - udelay(50); - u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); - } - if (delay == 0) { - u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); - rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, u1tmp&(~BIT(2))); - } -} - -void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 u1_h2c_set_pwrmode[3] = { 0 }; - struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - - RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); - - SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); - SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, - (rtlpriv->mac80211.p2p) ? - ppsc->smart_ps : 1); - SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, - ppsc->reg_max_lps_awakeintvl); - - RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, - "rtl8723ae_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n", - u1_h2c_set_pwrmode, 3); - rtl8723ae_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); - -} - static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { @@ -812,7 +576,6 @@ void rtl8723ae_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4)); p2p_ps_offload->offload_en = 1; - if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) { p2p_ps_offload->role = 1; p2p_ps_offload->allstasleep = 0; @@ -836,3 +599,24 @@ void rtl8723ae_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, u8 p2p_ps_state) } rtl8723ae_fill_h2c_cmd(hw, H2C_P2P_PS_OFFLOAD, 1, (u8 *)p2p_ps_offload); } + +void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 u1_h2c_set_pwrmode[3] = { 0 }; + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); + + SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); + SET_H2CCMD_PWRMODE_PARM_SMART_PS_23A(u1_h2c_set_pwrmode, + (rtlpriv->mac80211.p2p) ? + ppsc->smart_ps : 1); + SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, + ppsc->reg_max_lps_awakeintvl); + + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, + "rtl8723ae_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n", + u1_h2c_set_pwrmode, 3); + rtl8723ae_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h index ed3b795e698..d355b85dd9f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h @@ -34,7 +34,7 @@ #define FW_8192C_END_ADDRESS 0x3FFF #define FW_8192C_PAGE_SIZE 4096 #define FW_8192C_POLLING_DELAY 5 -#define FW_8192C_POLLING_TIMEOUT_COUNT 1000 +#define FW_8192C_POLLING_TIMEOUT_COUNT 6000 #define BEACON_PG 0 #define PSPOLL_PG 2 @@ -65,21 +65,9 @@ struct rtl8723ae_firmware_header { u32 rsvd5; }; -enum rtl8192c_h2c_cmd { - H2C_AP_OFFLOAD = 0, - H2C_SETPWRMODE = 1, - H2C_JOINBSSRPT = 2, - H2C_RSVDPAGE = 3, - H2C_RSSI_REPORT = 4, - H2C_P2P_PS_CTW_CMD = 5, - H2C_P2P_PS_OFFLOAD = 6, - H2C_RA_MASK = 7, - MAX_H2CCMD -}; - #define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) -#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ +#define SET_H2CCMD_PWRMODE_PARM_SMART_PS_23A(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) #define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) @@ -92,10 +80,8 @@ enum rtl8192c_h2c_cmd { #define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) -int rtl8723ae_download_fw(struct ieee80211_hw *hw); void rtl8723ae_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); -void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw); void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl8723ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); void rtl8723ae_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c index 3d092e4b0b7..5b4a714f3c8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c @@ -31,6 +31,7 @@ #include "../pci.h" #include "dm.h" #include "fw.h" +#include "../rtl8723com/fw_common.h" #include "phy.h" #include "reg.h" #include "hal_btc.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c index 68c28340f79..5d534df8d90 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c @@ -30,7 +30,9 @@ #include "hal_btc.h" #include "../pci.h" #include "phy.h" +#include "../rtl8723com/phy_common.h" #include "fw.h" +#include "../rtl8723com/fw_common.h" #include "reg.h" #include "def.h" @@ -391,13 +393,13 @@ static void rtl8723ae_dm_bt_set_sw_full_time_dac_swing(struct ieee80211_hw *hw, if (sw_dac_swing_on) { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, "[BTCoex], SwDacSwing = 0x%x\n", sw_dac_swing_lvl); - rtl8723ae_phy_set_bb_reg(hw, 0x880, 0xff000000, - sw_dac_swing_lvl); + rtl8723_phy_set_bb_reg(hw, 0x880, 0xff000000, + sw_dac_swing_lvl); rtlpcipriv->bt_coexist.sw_coexist_all_off = false; } else { RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, "[BTCoex], SwDacSwing Off!\n"); - rtl8723ae_phy_set_bb_reg(hw, 0x880, 0xff000000, 0xc0); + rtl8723_phy_set_bb_reg(hw, 0x880, 0xff000000, 0xc0); } } diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c index c333dfd116b..87f69166a7e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c @@ -38,10 +38,11 @@ #include "def.h" #include "phy.h" #include "dm.h" +#include "../rtl8723com/dm_common.h" #include "fw.h" +#include "../rtl8723com/fw_common.h" #include "led.h" #include "hw.h" -#include "pwrseqcmd.h" #include "pwrseq.h" #include "btc.h" @@ -206,14 +207,13 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtl_write_byte(rtlpriv, REG_SLOT, val[0]); for (e_aci = 0; e_aci < AC_MAX; e_aci++) { - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_AC_PARAM, - (u8 *) (&e_aci)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, + &e_aci); } break; } case HW_VAR_ACK_PREAMBLE:{ u8 reg_tmp; - u8 short_preamble = (bool) (*(u8 *) val); + u8 short_preamble = (bool)*val; reg_tmp = (mac->cur_40_prime_sc) << 5; if (short_preamble) reg_tmp |= 0x80; @@ -224,7 +224,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) u8 min_spacing_to_set; u8 sec_min_space; - min_spacing_to_set = *((u8 *) val); + min_spacing_to_set = *val; if (min_spacing_to_set <= 7) { sec_min_space = 0; @@ -248,7 +248,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) case HW_VAR_SHORTGI_DENSITY:{ u8 density_to_set; - density_to_set = *((u8 *) val); + density_to_set = *val; mac->min_space_cfg |= (density_to_set << 3); RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, @@ -272,7 +272,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) else p_regtoset = regtoset_normal; - factor_toset = *((u8 *) val); + factor_toset = *val; if (factor_toset <= 3) { factor_toset = (1 << (factor_toset + 2)); if (factor_toset > 0xf) @@ -303,16 +303,15 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) } break; } case HW_VAR_AC_PARAM:{ - u8 e_aci = *((u8 *) val); - rtl8723ae_dm_init_edca_turbo(hw); + u8 e_aci = *val; + rtl8723_dm_init_edca_turbo(hw); - if (rtlpci->acm_method != eAcmWay2_SW) - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_ACM_CTRL, - (u8 *) (&e_aci)); + if (rtlpci->acm_method != EACMWAY2_SW) + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, + &e_aci); break; } case HW_VAR_ACM_CTRL:{ - u8 e_aci = *((u8 *) val); + u8 e_aci = *val; union aci_aifsn *p_aci_aifsn = (union aci_aifsn *)(&(mac->ac[0].aifs)); u8 acm = p_aci_aifsn->f.acm; @@ -365,7 +364,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlpci->receive_config = ((u32 *) (val))[0]; break; case HW_VAR_RETRY_LIMIT:{ - u8 retry_limit = ((u8 *) (val))[0]; + u8 retry_limit = *val; rtl_write_word(rtlpriv, REG_RL, retry_limit << RETRY_LIMIT_SHORT_SHIFT | @@ -378,13 +377,13 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtlefuse->efuse_usedbytes = *((u16 *) val); break; case HW_VAR_EFUSE_USAGE: - rtlefuse->efuse_usedpercentage = *((u8 *) val); + rtlefuse->efuse_usedpercentage = *val; break; case HW_VAR_IO_CMD: rtl8723ae_phy_set_io_cmd(hw, (*(enum io_type *)val)); break; case HW_VAR_WPA_CONFIG: - rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val)); + rtl_write_byte(rtlpriv, REG_SECCFG, *val); break; case HW_VAR_SET_RPWM:{ u8 rpwm_val; @@ -393,27 +392,25 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) udelay(1); if (rpwm_val & BIT(7)) { - rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, - (*(u8 *) val)); + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val); } else { - rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, - ((*(u8 *) val) | BIT(7))); + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7)); } break; } case HW_VAR_H2C_FW_PWRMODE:{ - u8 psmode = (*(u8 *) val); + u8 psmode = *val; if (psmode != FW_PS_ACTIVE_MODE) rtl8723ae_dm_rf_saving(hw, true); - rtl8723ae_set_fw_pwrmode_cmd(hw, (*(u8 *) val)); + rtl8723ae_set_fw_pwrmode_cmd(hw, *val); break; } case HW_VAR_FW_PSMODE_STATUS: ppsc->fw_current_inpsmode = *((bool *) val); break; case HW_VAR_H2C_FW_JOINBSSRPT:{ - u8 mstatus = (*(u8 *) val); + u8 mstatus = *val; u8 tmp_regcr, tmp_reg422; bool recover = false; @@ -446,11 +443,11 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) rtl_write_byte(rtlpriv, REG_CR + 1, (tmp_regcr & ~(BIT(0)))); } - rtl8723ae_set_fw_joinbss_report_cmd(hw, (*(u8 *) val)); + rtl8723ae_set_fw_joinbss_report_cmd(hw, *val); break; } case HW_VAR_H2C_FW_P2P_PS_OFFLOAD: - rtl8723ae_set_p2p_ps_offload_cmd(hw, (*(u8 *)val)); + rtl8723ae_set_p2p_ps_offload_cmd(hw, *val); break; case HW_VAR_AID:{ u16 u2btmp; @@ -460,7 +457,7 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) mac->assoc_id)); break; } case HW_VAR_CORRECT_TSF:{ - u8 btype_ibss = ((u8 *) (val))[0]; + u8 btype_ibss = *val; if (btype_ibss == true) _rtl8723ae_stop_tx_beacon(hw); @@ -490,20 +487,18 @@ void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) (u8 *)(&fw_current_inps)); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&ppsc->fwctrl_psmode)); + &ppsc->fwctrl_psmode); - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, + &rpwm_val); } else { rpwm_val = 0x0C; /* RF on */ fw_pwrmode = FW_PS_ACTIVE_MODE; fw_current_inps = false; rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, - (u8 *)(&rpwm_val)); - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_H2C_FW_PWRMODE, - (u8 *)(&fw_pwrmode)); + &rpwm_val); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, + &fw_pwrmode); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, @@ -880,23 +875,33 @@ int rtl8723ae_hw_init(struct ieee80211_hw *hw) bool rtstatus = true; int err; u8 tmp_u1b; + unsigned long flags; rtlpriv->rtlhal.being_init_adapter = true; + /* As this function can take a very long time (up to 350 ms) + * and can be called with irqs disabled, reenable the irqs + * to let the other devices continue being serviced. + * + * It is safe doing so since our own interrupts will only be enabled + * in a subsequent step. + */ + local_save_flags(flags); + local_irq_enable(); + rtlpriv->intf_ops->disable_aspm(hw); rtstatus = _rtl8712e_init_mac(hw); if (rtstatus != true) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); err = 1; - return err; + goto exit; } - err = rtl8723ae_download_fw(hw); + err = rtl8723_download_fw(hw, false); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "Failed to download FW. Init HW without FW now..\n"); err = 1; - rtlhal->fw_ready = false; - return err; + goto exit; } else { rtlhal->fw_ready = true; } @@ -971,6 +976,8 @@ int rtl8723ae_hw_init(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n"); } rtl8723ae_dm_init(hw); +exit: + local_irq_restore(flags); rtlpriv->rtlhal.being_init_adapter = false; return err; } @@ -1112,12 +1119,13 @@ static int _rtl8723ae_set_media_status(struct ieee80211_hw *hw, void rtl8723ae_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) { struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - u32 reg_rcr = rtlpci->receive_config; + u32 reg_rcr; if (rtlpriv->psc.rfpwr_state != ERFON) return; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RCR, (u8 *)(®_rcr)); + if (check_bssid == true) { reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, @@ -1153,7 +1161,7 @@ void rtl8723ae_set_qos(struct ieee80211_hw *hw, int aci) { struct rtl_priv *rtlpriv = rtl_priv(hw); - rtl8723ae_dm_init_edca_turbo(hw); + rtl8723_dm_init_edca_turbo(hw); switch (aci) { case AC1_BK: rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); @@ -1614,10 +1622,10 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, rtl8723ae_read_bt_coexist_info_from_hwpg(hw, rtlefuse->autoload_failflag, hwinfo); - rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; rtlefuse->txpwr_fromeprom = true; - rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID]; + rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); @@ -1655,7 +1663,7 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, CHK_SVID_SMID(0x10EC, 0x9185)) rtlhal->oem_id = RT_CID_TOSHIBA; else if (rtlefuse->eeprom_svid == 0x1025) - rtlhal->oem_id = RT_CID_819x_Acer; + rtlhal->oem_id = RT_CID_819X_ACER; else if (CHK_SVID_SMID(0x10EC, 0x6191) || CHK_SVID_SMID(0x10EC, 0x6192) || CHK_SVID_SMID(0x10EC, 0x6193) || @@ -1665,7 +1673,7 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, CHK_SVID_SMID(0x10EC, 0x8191) || CHK_SVID_SMID(0x10EC, 0x8192) || CHK_SVID_SMID(0x10EC, 0x8193)) - rtlhal->oem_id = RT_CID_819x_SAMSUNG; + rtlhal->oem_id = RT_CID_819X_SAMSUNG; else if (CHK_SVID_SMID(0x10EC, 0x8195) || CHK_SVID_SMID(0x10EC, 0x9195) || CHK_SVID_SMID(0x10EC, 0x7194) || @@ -1673,24 +1681,24 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, CHK_SVID_SMID(0x10EC, 0x8201) || CHK_SVID_SMID(0x10EC, 0x8202) || CHK_SVID_SMID(0x10EC, 0x9200)) - rtlhal->oem_id = RT_CID_819x_Lenovo; + rtlhal->oem_id = RT_CID_819X_LENOVO; else if (CHK_SVID_SMID(0x10EC, 0x8197) || CHK_SVID_SMID(0x10EC, 0x9196)) - rtlhal->oem_id = RT_CID_819x_CLEVO; + rtlhal->oem_id = RT_CID_819X_CLEVO; else if (CHK_SVID_SMID(0x1028, 0x8194) || CHK_SVID_SMID(0x1028, 0x8198) || CHK_SVID_SMID(0x1028, 0x9197) || CHK_SVID_SMID(0x1028, 0x9198)) - rtlhal->oem_id = RT_CID_819x_DELL; + rtlhal->oem_id = RT_CID_819X_DELL; else if (CHK_SVID_SMID(0x103C, 0x1629)) - rtlhal->oem_id = RT_CID_819x_HP; + rtlhal->oem_id = RT_CID_819X_HP; else if (CHK_SVID_SMID(0x1A32, 0x2315)) - rtlhal->oem_id = RT_CID_819x_QMI; + rtlhal->oem_id = RT_CID_819X_QMI; else if (CHK_SVID_SMID(0x10EC, 0x8203)) - rtlhal->oem_id = RT_CID_819x_PRONETS; + rtlhal->oem_id = RT_CID_819X_PRONETS; else if (CHK_SVID_SMID(0x1043, 0x84B5)) rtlhal->oem_id = - RT_CID_819x_Edimax_ASUS; + RT_CID_819X_EDIMAX_ASUS; else rtlhal->oem_id = RT_CID_DEFAULT; } else if (rtlefuse->eeprom_did == 0x8178) { @@ -1712,12 +1720,12 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, CHK_SVID_SMID(0x10EC, 0x9185)) rtlhal->oem_id = RT_CID_TOSHIBA; else if (rtlefuse->eeprom_svid == 0x1025) - rtlhal->oem_id = RT_CID_819x_Acer; + rtlhal->oem_id = RT_CID_819X_ACER; else if (CHK_SVID_SMID(0x10EC, 0x8186)) - rtlhal->oem_id = RT_CID_819x_PRONETS; + rtlhal->oem_id = RT_CID_819X_PRONETS; else if (CHK_SVID_SMID(0x1043, 0x8486)) rtlhal->oem_id = - RT_CID_819x_Edimax_ASUS; + RT_CID_819X_EDIMAX_ASUS; else rtlhal->oem_id = RT_CID_DEFAULT; } else { @@ -1731,7 +1739,7 @@ static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, rtlhal->oem_id = RT_CID_CCX; break; case EEPROM_CID_QMI: - rtlhal->oem_id = RT_CID_819x_QMI; + rtlhal->oem_id = RT_CID_819X_QMI; break; case EEPROM_CID_WHQL: break; @@ -2037,8 +2045,7 @@ void rtl8723ae_update_channel_access_setting(struct ieee80211_hw *hw) struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); u16 sifs_timer; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, - (u8 *)&mac->slot_time); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); if (!mac->ht_enable) sifs_timer = 0x0a0a; else @@ -2376,24 +2383,3 @@ void rtl8723ae_suspend(struct ieee80211_hw *hw) void rtl8723ae_resume(struct ieee80211_hw *hw) { } - -/* Turn on AAP (RCR:bit 0) for promicuous mode. */ -void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - - if (allow_all_da) /* Set BIT0 */ - rtlpci->receive_config |= RCR_AAP; - else /* Clear BIT0 */ - rtlpci->receive_config &= ~RCR_AAP; - - if (write_into_reg) - rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); - - - RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, - "receive_config=0x%08X, write_into_reg=%d\n", - rtlpci->receive_config, write_into_reg); -} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h index 6fa24f79b1d..d3bc39fb27a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h @@ -67,7 +67,5 @@ void rtl8723ae_bt_reg_init(struct ieee80211_hw *hw); void rtl8723ae_bt_hw_init(struct ieee80211_hw *hw); void rtl8723ae_suspend(struct ieee80211_hw *hw); void rtl8723ae_resume(struct ieee80211_hw *hw); -void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index eafbb18dd48..3ea78afdec7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -30,12 +30,14 @@ #include "../wifi.h" #include "../pci.h" #include "../ps.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "table.h" +#include "../rtl8723com/phy_common.h" /* static forward definitions */ static u32 _phy_fw_rf_serial_read(struct ieee80211_hw *hw, @@ -43,72 +45,17 @@ static u32 _phy_fw_rf_serial_read(struct ieee80211_hw *hw, static void _phy_fw_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data); -static u32 _phy_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); -static void _phy_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, u32 data); -static u32 _phy_calculate_bit_shift(u32 bitmask); static bool _phy_bb8192c_config_parafile(struct ieee80211_hw *hw); static bool _phy_cfg_mac_w_header(struct ieee80211_hw *hw); static bool _phy_cfg_bb_w_header(struct ieee80211_hw *hw, u8 configtype); static bool _phy_cfg_bb_w_pgheader(struct ieee80211_hw *hw, u8 configtype); -static void _phy_init_bb_rf_reg_def(struct ieee80211_hw *hw); -static bool _phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, - u32 cmdtableidx, u32 cmdtablesz, - enum swchnlcmd_id cmdid, - u32 para1, u32 para2, - u32 msdelay); static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 *stage, u8 *step, u32 *delay); static u8 _phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, enum wireless_mode wirelessmode, long power_indbm); -static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, - enum wireless_mode wirelessmode, u8 txpwridx); static void rtl8723ae_phy_set_io(struct ieee80211_hw *hw); -u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, - u32 bitmask) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 returnvalue, originalvalue, bitshift; - - RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, - "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); - originalvalue = rtl_read_dword(rtlpriv, regaddr); - bitshift = _phy_calculate_bit_shift(bitmask); - returnvalue = (originalvalue & bitmask) >> bitshift; - - RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, - "BBR MASK=0x%x Addr[0x%x]=0x%x\n", bitmask, regaddr, - originalvalue); - - return returnvalue; -} - -void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 originalvalue, bitshift; - - RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, - "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, - bitmask, data); - - if (bitmask != MASKDWORD) { - originalvalue = rtl_read_dword(rtlpriv, regaddr); - bitshift = _phy_calculate_bit_shift(bitmask); - data = ((originalvalue & (~bitmask)) | (data << bitshift)); - } - - rtl_write_dword(rtlpriv, regaddr, data); - - RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, - "regaddr(%#x), bitmask(%#x), data(%#x)\n", - regaddr, bitmask, data); -} - u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { @@ -124,11 +71,11 @@ u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); if (rtlphy->rf_mode != RF_OP_BY_FW) - original_value = _phy_rf_serial_read(hw, rfpath, regaddr); + original_value = rtl8723_phy_rf_serial_read(hw, rfpath, regaddr); else original_value = _phy_fw_rf_serial_read(hw, rfpath, regaddr); - bitshift = _phy_calculate_bit_shift(bitmask); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); readback_value = (original_value & bitmask) >> bitshift; spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); @@ -157,19 +104,19 @@ void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, if (rtlphy->rf_mode != RF_OP_BY_FW) { if (bitmask != RFREG_OFFSET_MASK) { - original_value = _phy_rf_serial_read(hw, rfpath, - regaddr); - bitshift = _phy_calculate_bit_shift(bitmask); + original_value = rtl8723_phy_rf_serial_read(hw, rfpath, + regaddr); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } - _phy_rf_serial_write(hw, rfpath, regaddr, data); + rtl8723_phy_rf_serial_write(hw, rfpath, regaddr, data); } else { if (bitmask != RFREG_OFFSET_MASK) { original_value = _phy_fw_rf_serial_read(hw, rfpath, regaddr); - bitshift = _phy_calculate_bit_shift(bitmask); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); data = ((original_value & (~bitmask)) | (data << bitshift)); } @@ -197,87 +144,6 @@ static void _phy_fw_rf_serial_write(struct ieee80211_hw *hw, RT_ASSERT(false, "deprecated!\n"); } -static u32 _phy_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &(rtlpriv->phy); - struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; - u32 newoffset; - u32 tmplong, tmplong2; - u8 rfpi_enable = 0; - u32 retvalue; - - offset &= 0x3f; - newoffset = offset; - if (RT_CANNOT_IO(hw)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n"); - return 0xFFFFFFFF; - } - tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD); - if (rfpath == RF90_PATH_A) - tmplong2 = tmplong; - else - tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD); - tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) | - (newoffset << 23) | BLSSIREADEDGE; - rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, - tmplong & (~BLSSIREADEDGE)); - mdelay(1); - rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2); - mdelay(1); - rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, - tmplong | BLSSIREADEDGE); - mdelay(1); - if (rfpath == RF90_PATH_A) - rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, - BIT(8)); - else if (rfpath == RF90_PATH_B) - rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1, - BIT(8)); - if (rfpi_enable) - retvalue = rtl_get_bbreg(hw, pphyreg->rf_rbpi, - BLSSIREADBACKDATA); - else - retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb, - BLSSIREADBACKDATA); - RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]=0x%x\n", - rfpath, pphyreg->rf_rb, retvalue); - return retvalue; -} - -static void _phy_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, u32 data) -{ - u32 data_and_addr; - u32 newoffset; - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &(rtlpriv->phy); - struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; - - if (RT_CANNOT_IO(hw)) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n"); - return; - } - offset &= 0x3f; - newoffset = offset; - data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; - rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr); - RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n", - rfpath, pphyreg->rf3wire_offset, data_and_addr); -} - -static u32 _phy_calculate_bit_shift(u32 bitmask) -{ - u32 i; - - for (i = 0; i <= 31; i++) { - if (((bitmask >> i) & 0x1) == 1) - break; - } - return i; -} - static void _rtl8723ae_phy_bb_config_1t(struct ieee80211_hw *hw) { rtl_set_bbreg(hw, RFPGA0_TXINFO, 0x3, 0x2); @@ -307,7 +173,7 @@ bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw) u8 tmpu1b; u8 reg_hwparafile = 1; - _phy_init_bb_rf_reg_def(hw); + rtl8723_phy_init_bb_rf_reg_def(hw); /* 1. 0x28[1] = 1 */ tmpu1b = rtl_read_byte(rtlpriv, REG_AFE_PLL_CTRL); @@ -412,18 +278,7 @@ static bool _phy_cfg_bb_w_header(struct ieee80211_hw *hw, u8 configtype) phy_regarray_table = RTL8723EPHY_REG_1TARRAY; if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_reg_arraylen; i = i + 2) { - if (phy_regarray_table[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table[i] == 0xfb) - udelay(50); - else if (phy_regarray_table[i] == 0xfa) - udelay(5); - else if (phy_regarray_table[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table[i]); rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, phy_regarray_table[i + 1]); udelay(1); @@ -585,18 +440,7 @@ static bool _phy_cfg_bb_w_pgheader(struct ieee80211_hw *hw, u8 configtype) if (configtype == BASEBAND_CONFIG_PHY_REG) { for (i = 0; i < phy_regarray_pg_len; i = i + 3) { - if (phy_regarray_table_pg[i] == 0xfe) - mdelay(50); - else if (phy_regarray_table_pg[i] == 0xfd) - mdelay(5); - else if (phy_regarray_table_pg[i] == 0xfc) - mdelay(1); - else if (phy_regarray_table_pg[i] == 0xfb) - udelay(50); - else if (phy_regarray_table_pg[i] == 0xfa) - udelay(5); - else if (phy_regarray_table_pg[i] == 0xf9) - udelay(1); + rtl_addr_delay(phy_regarray_table_pg[i]); _st_pwrIdx_dfrate_off(hw, phy_regarray_table_pg[i], phy_regarray_table_pg[i + 1], @@ -623,24 +467,9 @@ bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, switch (rfpath) { case RF90_PATH_A: for (i = 0; i < radioa_arraylen; i = i + 2) { - if (radioa_array_table[i] == 0xfe) - mdelay(50); - else if (radioa_array_table[i] == 0xfd) - mdelay(5); - else if (radioa_array_table[i] == 0xfc) - mdelay(1); - else if (radioa_array_table[i] == 0xfb) - udelay(50); - else if (radioa_array_table[i] == 0xfa) - udelay(5); - else if (radioa_array_table[i] == 0xf9) - udelay(1); - else { - rtl_set_rfreg(hw, rfpath, radioa_array_table[i], - RFREG_OFFSET_MASK, - radioa_array_table[i + 1]); - udelay(1); - } + rtl_rfreg_delay(hw, rfpath, radioa_array_table[i], + RFREG_OFFSET_MASK, + radioa_array_table[i + 1]); } break; case RF90_PATH_B: @@ -690,92 +519,6 @@ void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) ROFDM0_RXDETECTOR3, rtlphy->framesync); } -static void _phy_init_bb_rf_reg_def(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &(rtlpriv->phy); - - rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW; - rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW; - rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW; - rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW; - - rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB; - rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB; - rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB; - rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB; - - rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE; - rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE; - - rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE; - rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE; - - rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = - RFPGA0_XA_LSSIPARAMETER; - rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = - RFPGA0_XB_LSSIPARAMETER; - - rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = rFPGA0_XAB_RFPARAMETER; - rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = rFPGA0_XAB_RFPARAMETER; - rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER; - rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER; - - rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE; - rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE; - rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE; - rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE; - - rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1; - rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1; - - rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2; - rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2; - - rtlphy->phyreg_def[RF90_PATH_A].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL; - rtlphy->phyreg_def[RF90_PATH_B].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL; - rtlphy->phyreg_def[RF90_PATH_C].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL; - rtlphy->phyreg_def[RF90_PATH_D].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL; - - rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1; - rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1; - rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1; - rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1; - - rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2; - rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2; - rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2; - rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2; - - rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbal = ROFDM0_XARXIQIMBALANCE; - rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbal = ROFDM0_XBRXIQIMBALANCE; - rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbal = ROFDM0_XCRXIQIMBANLANCE; - rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbal = ROFDM0_XDRXIQIMBALANCE; - - rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE; - rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE; - rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE; - rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE; - - rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbal = ROFDM0_XATXIQIMBALANCE; - rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbal = ROFDM0_XBTXIQIMBALANCE; - rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbal = ROFDM0_XCTXIQIMBALANCE; - rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbal = ROFDM0_XDTXIQIMBALANCE; - - rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE; - rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE; - rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE; - rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE; - - rtlphy->phyreg_def[RF90_PATH_A].rf_rb = RFPGA0_XA_LSSIREADBACK; - rtlphy->phyreg_def[RF90_PATH_B].rf_rb = RFPGA0_XB_LSSIREADBACK; - rtlphy->phyreg_def[RF90_PATH_C].rf_rb = RFPGA0_XC_LSSIREADBACK; - rtlphy->phyreg_def[RF90_PATH_D].rf_rb = RFPGA0_XD_LSSIREADBACK; - - rtlphy->phyreg_def[RF90_PATH_A].rf_rbpi = TRANSCEIVEA_HSPI_READBACK; - rtlphy->phyreg_def[RF90_PATH_B].rf_rbpi = TRANSCEIVEB_HSPI_READBACK; -} - void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -785,17 +528,17 @@ void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) long txpwr_dbm; txpwr_level = rtlphy->cur_cck_txpwridx; - txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, txpwr_level); + txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, txpwr_level); txpwr_level = rtlphy->cur_ofdm24g_txpwridx + rtlefuse->legacy_ht_txpowerdiff; - if (_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > txpwr_dbm) - txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, + if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > txpwr_dbm) + txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level); txpwr_level = rtlphy->cur_ofdm24g_txpwridx; - if (_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level) > + if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level) > txpwr_dbm) - txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, - txpwr_level); + txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, + txpwr_level); *powerlevel = txpwr_dbm; } @@ -912,57 +655,6 @@ static u8 _phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, return txpwridx; } -static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, - enum wireless_mode wirelessmode, u8 txpwridx) -{ - long offset; - long pwrout_dbm; - - switch (wirelessmode) { - case WIRELESS_MODE_B: - offset = -7; - break; - case WIRELESS_MODE_G: - case WIRELESS_MODE_N_24G: - offset = -8; - break; - default: - offset = -8; - break; - } - pwrout_dbm = txpwridx / 2 + offset; - return pwrout_dbm; -} - -void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation.\n"); - break; - } - } -} - void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -1146,26 +838,26 @@ static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 num_total_rfpath = rtlphy->num_total_rfpath; precommoncmdcnt = 0; - _phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, - MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL, - 0, 0, 0); - _phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, - MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); + rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, + MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL, + 0, 0, 0); + rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, + MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); postcommoncmdcnt = 0; - _phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, - MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0); + rtl8723_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, + MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0); rfdependcmdcnt = 0; RT_ASSERT((channel >= 1 && channel <= 14), "illegal channel for Zebra: %d\n", channel); - _phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, - MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG, + rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, + MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG, RF_CHNLBW, channel, 10); - _phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, - MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0); + rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, + MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0); do { switch (*stage) { @@ -1233,29 +925,6 @@ static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, return false; } -static bool _phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, - u32 cmdtableidx, u32 cmdtablesz, - enum swchnlcmd_id cmdid, u32 para1, - u32 para2, u32 msdelay) -{ - struct swchnlcmd *pcmd; - - if (cmdtable == NULL) { - RT_ASSERT(false, "cmdtable cannot be NULL.\n"); - return false; - } - - if (cmdtableidx >= cmdtablesz) - return false; - - pcmd = cmdtable + cmdtableidx; - pcmd->cmdid = cmdid; - pcmd->para1 = para1; - pcmd->para2 = para2; - pcmd->msdelay = msdelay; - return true; -} - static u8 _rtl8723ae_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb) { u32 reg_eac, reg_e94, reg_e9c, reg_ea4; @@ -1326,136 +995,6 @@ static u8 _rtl8723ae_phy_path_b_iqk(struct ieee80211_hw *hw) return result; } -static void phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok, - long result[][8], u8 final_candidate, - bool btxonly) -{ - u32 oldval_0, x, tx0_a, reg; - long y, tx0_c; - - if (final_candidate == 0xFF) { - return; - } else if (iqk_ok) { - oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE, - MASKDWORD) >> 22) & 0x3FF; - x = result[final_candidate][0]; - if ((x & 0x00000200) != 0) - x = x | 0xFFFFFC00; - tx0_a = (x * oldval_0) >> 8; - rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x3FF, tx0_a); - rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31), - ((x * oldval_0 >> 7) & 0x1)); - y = result[final_candidate][1]; - if ((y & 0x00000200) != 0) - y = y | 0xFFFFFC00; - tx0_c = (y * oldval_0) >> 8; - rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000, - ((tx0_c & 0x3C0) >> 6)); - rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x003F0000, - (tx0_c & 0x3F)); - rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29), - ((y * oldval_0 >> 7) & 0x1)); - if (btxonly) - return; - reg = result[final_candidate][2]; - rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg); - reg = result[final_candidate][3] & 0x3F; - rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg); - reg = (result[final_candidate][3] >> 6) & 0xF; - rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg); - } -} - -static void phy_save_adda_regs(struct ieee80211_hw *hw, - u32 *addareg, u32 *addabackup, - u32 registernum) -{ - u32 i; - - for (i = 0; i < registernum; i++) - addabackup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD); -} - -static void phy_save_mac_regs(struct ieee80211_hw *hw, u32 *macreg, - u32 *macbackup) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 i; - - for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) - macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]); - macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]); -} - -static void phy_reload_adda_regs(struct ieee80211_hw *hw, u32 *addareg, - u32 *addabackup, u32 regiesternum) -{ - u32 i; - - for (i = 0; i < regiesternum; i++) - rtl_set_bbreg(hw, addareg[i], MASKDWORD, addabackup[i]); -} - -static void phy_reload_mac_regs(struct ieee80211_hw *hw, u32 *macreg, - u32 *macbackup) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 i; - - for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) - rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]); - rtl_write_dword(rtlpriv, macreg[i], macbackup[i]); -} - -static void _rtl8723ae_phy_path_adda_on(struct ieee80211_hw *hw, - u32 *addareg, bool is_patha_on, - bool is2t) -{ - u32 pathOn; - u32 i; - - pathOn = is_patha_on ? 0x04db25a4 : 0x0b1b25a4; - if (false == is2t) { - pathOn = 0x0bdb25a0; - rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0); - } else { - rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathOn); - } - - for (i = 1; i < IQK_ADDA_REG_NUM; i++) - rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathOn); -} - -static void _rtl8723ae_phy_mac_setting_calibration(struct ieee80211_hw *hw, - u32 *macreg, u32 *macbackup) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 i = 0; - - rtl_write_byte(rtlpriv, macreg[i], 0x3F); - - for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++) - rtl_write_byte(rtlpriv, macreg[i], - (u8) (macbackup[i] & (~BIT(3)))); - rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5)))); -} - -static void _rtl8723ae_phy_path_a_standby(struct ieee80211_hw *hw) -{ - rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0); - rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); - rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); -} - -static void _rtl8723ae_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode) -{ - u32 mode; - - mode = pi_mode ? 0x01000100 : 0x01000000; - rtl_set_bbreg(hw, 0x820, MASKDWORD, mode); - rtl_set_bbreg(hw, 0x828, MASKDWORD, mode); -} - static bool phy_simularity_comp(struct ieee80211_hw *hw, long result[][8], u8 c1, u8 c2) { @@ -1527,10 +1066,12 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, const u32 retrycount = 2; if (t == 0) { - phy_save_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16); - phy_save_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); + rtl8723_save_adda_registers(hw, adda_reg, rtlphy->adda_backup, + 16); + rtl8723_phy_save_mac_registers(hw, iqk_mac_reg, + rtlphy->iqk_mac_backup); } - _rtl8723ae_phy_path_adda_on(hw, adda_reg, true, is2t); + rtl8723_phy_path_adda_on(hw, adda_reg, true, is2t); if (t == 0) { rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, @@ -1538,7 +1079,7 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, } if (!rtlphy->rfpi_enable) - _rtl8723ae_phy_pi_mode_switch(hw, true); + rtl8723_phy_pi_mode_switch(hw, true); if (t == 0) { rtlphy->reg_c04 = rtl_get_bbreg(hw, 0xc04, MASKDWORD); rtlphy->reg_c08 = rtl_get_bbreg(hw, 0xc08, MASKDWORD); @@ -1551,7 +1092,7 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00010000); } - _rtl8723ae_phy_mac_setting_calibration(hw, iqk_mac_reg, + rtl8723_phy_mac_setting_calibration(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x00080000); if (is2t) @@ -1581,8 +1122,8 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, } if (is2t) { - _rtl8723ae_phy_path_a_standby(hw); - _rtl8723ae_phy_path_adda_on(hw, adda_reg, false, is2t); + rtl8723_phy_path_a_standby(hw); + rtl8723_phy_path_adda_on(hw, adda_reg, false, is2t); for (i = 0; i < retrycount; i++) { pathb_ok = _rtl8723ae_phy_path_b_iqk(hw); if (pathb_ok == 0x03) { @@ -1617,9 +1158,11 @@ static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3); if (t != 0) { if (!rtlphy->rfpi_enable) - _rtl8723ae_phy_pi_mode_switch(hw, false); - phy_reload_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16); - phy_reload_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); + rtl8723_phy_pi_mode_switch(hw, false); + rtl8723_phy_reload_adda_registers(hw, adda_reg, + rtlphy->adda_backup, 16); + rtl8723_phy_reload_mac_registers(hw, iqk_mac_reg, + rtlphy->iqk_mac_backup); } } @@ -1720,7 +1263,8 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery) }; if (recovery) { - phy_reload_adda_regs(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); + rtl8723_phy_reload_adda_registers(hw, iqk_bb_reg, + rtlphy->iqk_bb_backup, 10); return; } if (start_conttx || singletone) @@ -1785,9 +1329,10 @@ void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery) rtlphy->reg_e9c = rtlphy->reg_ebc = 0x0; } if (reg_e94 != 0) /*&&(reg_ea4 != 0) */ - phy_path_a_fill_iqk_matrix(hw, patha_ok, result, - final_candidate, (reg_ea4 == 0)); - phy_save_adda_regs(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); + rtl8723_phy_path_a_fill_iqk_matrix(hw, patha_ok, result, + final_candidate, + (reg_ea4 == 0)); + rtl8723_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); } void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h index e7a59eba351..cd43139ed33 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h @@ -76,23 +76,6 @@ #define RTL92C_MAX_PATH_NUM 2 -enum swchnlcmd_id { - CMDID_END, - CMDID_SET_TXPOWEROWER_LEVEL, - CMDID_BBREGWRITE10, - CMDID_WRITEPORT_ULONG, - CMDID_WRITEPORT_USHORT, - CMDID_WRITEPORT_UCHAR, - CMDID_RF_WRITEREG, -}; - -struct swchnlcmd { - enum swchnlcmd_id cmdid; - u32 para1; - u32 para2; - u32 msdelay; -}; - enum hw90_block_e { HW90_BLOCK_MAC = 0, HW90_BLOCK_PHY0 = 1, @@ -183,42 +166,36 @@ struct tx_power_struct { u32 mcs_original_offset[4][16]; }; -extern u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -extern void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -extern u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); -extern bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw); -extern bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw); -extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, - enum radio_path rfpath); -extern void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -extern void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); -extern void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw, - u8 channel); -extern bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw, - long power_indbm); -extern void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); -extern void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw); -extern void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); -extern void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw); -extern u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw); -extern void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); +u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw); +bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw); +bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw); +bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw, + u8 channel); +bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw, + long power_indbm); +void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw); +u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw); +void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl8723ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl8723ae_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); +bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h index 7a46f9fdf55..a418acb4d0c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h @@ -30,7 +30,6 @@ #ifndef __RTL8723E_PWRSEQ_H__ #define __RTL8723E_PWRSEQ_H__ -#include "pwrseqcmd.h" /* Check document WM-20110607-Paul-RTL8723A_Power_Architecture-R02.vsd There are 6 HW Power States: diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h index 199da366c6d..64376b38708 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h @@ -2059,22 +2059,6 @@ #define BWORD1 0xc #define BWORD 0xf -#define MASKBYTE0 0xff -#define MASKBYTE1 0xff00 -#define MASKBYTE2 0xff0000 -#define MASKBYTE3 0xff000000 -#define MASKHWORD 0xffff0000 -#define MASKLWORD 0x0000ffff -#define MASKDWORD 0xffffffff -#define MASK12BITS 0xfff -#define MASKH4BITS 0xf0000000 -#define MASKOFDM_D 0xffc00000 -#define MASKCCK 0x3f3f3f3f - -#define MASK4BITS 0x0f -#define MASK20BITS 0xfffff -#define RFREG_OFFSET_MASK 0xfffff - #define BENABLE 0x1 #define BDISABLE 0x0 diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h index d0f9dd79abe..57f1933ee66 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h @@ -32,12 +32,11 @@ #define RF6052_MAX_TX_PWR 0x3F -extern void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); -extern bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw); +void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c index d9ee2efffe5..73cba1eec8c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c @@ -33,11 +33,15 @@ #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" +#include "../rtl8723com/phy_common.h" #include "dm.h" #include "hw.h" +#include "fw.h" +#include "../rtl8723com/fw_common.h" #include "sw.h" #include "trx.h" #include "led.h" @@ -192,6 +196,11 @@ void rtl8723ae_deinit_sw_vars(struct ieee80211_hw *hw) } } +static bool is_fw_header(struct rtl92c_firmware_header *hdr) +{ + return (hdr->signature & 0xfff0) == 0x2300; +} + static struct rtl_hal_ops rtl8723ae_hal_ops = { .init_sw_vars = rtl8723ae_init_sw_vars, .deinit_sw_vars = rtl8723ae_deinit_sw_vars, @@ -220,7 +229,7 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = { .set_bw_mode = rtl8723ae_phy_set_bw_mode, .switch_channel = rtl8723ae_phy_sw_chnl, .dm_watchdog = rtl8723ae_dm_watchdog, - .scan_operation_backup = rtl8723ae_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl8723ae_phy_set_rf_power_state, .led_control = rtl8723ae_led_control, .set_desc = rtl8723ae_set_desc, @@ -229,14 +238,14 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = { .enable_hw_sec = rtl8723ae_enable_hw_security_config, .set_key = rtl8723ae_set_key, .init_sw_leds = rtl8723ae_init_sw_leds, - .allow_all_destaddr = rtl8723ae_allow_all_destaddr, - .get_bbreg = rtl8723ae_phy_query_bb_reg, - .set_bbreg = rtl8723ae_phy_set_bb_reg, + .get_bbreg = rtl8723_phy_query_bb_reg, + .set_bbreg = rtl8723_phy_set_bb_reg, .get_rfreg = rtl8723ae_phy_query_rf_reg, .set_rfreg = rtl8723ae_phy_set_rf_reg, .c2h_command_handle = rtl_8723e_c2h_command_handle, .bt_wifi_media_status_notify = rtl_8723e_bt_wifi_media_status_notify, .bt_coex_off_before_lps = rtl8723ae_bt_coex_off_before_lps, + .is_fw_header = is_fw_header, }; static struct rtl_mod_params rtl8723ae_mod_params = { diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index bcd82a1020a..10b7577b6ae 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -334,7 +334,7 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw, /* during testing, hdr could be NULL here */ return false; } - if ((ieee80211_is_robust_mgmt_frame(hdr)) && + if ((_ieee80211_is_robust_mgmt_frame(hdr)) && (ieee80211_has_protected(hdr->frame_control))) rx_status->flag &= ~RX_FLAG_DECRYPTED; else @@ -359,14 +359,13 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw, /*rx_status->qual = status->signal; */ rx_status->signal = status->recvsignalpower + 10; - /*rx_status->noise = -status->noise; */ return true; } void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcdesc) @@ -376,7 +375,7 @@ void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; - u8 *pdesc = (u8 *) pdesc_tx; + u8 *pdesc = pdesc_tx; u16 seq_number; __le16 fc = hdr->frame_control; u8 fw_qsel = _rtl8723ae_map_hwqueue_to_fwqueue(skb, hw_queue); @@ -578,7 +577,7 @@ void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, SET_TX_DESC_OWN(pdesc, 1); - SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len)); + SET_TX_DESC_PKT_SIZE(pdesc, (u16) (skb->len)); SET_TX_DESC_FIRST_SEG(pdesc, 1); SET_TX_DESC_LAST_SEG(pdesc, 1); @@ -598,7 +597,8 @@ void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, pdesc, TX_DESC_SIZE); } -void rtl8723ae_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) +void rtl8723ae_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val) { if (istx == true) { switch (desc_name) { diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h index ad05b54bc0f..4380b7d3a91 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h @@ -521,12 +521,6 @@ do { \ memset(__pdesc, 0, _size); \ } while (0) -#define RTL8723E_RX_HAL_IS_CCK_RATE(rxmcs) \ - ((rxmcs) == DESC92_RATE1M || \ - (rxmcs) == DESC92_RATE2M || \ - (rxmcs) == DESC92_RATE5_5M || \ - (rxmcs) == DESC92_RATE11M) - struct rx_fwinfo_8723e { u8 gain_trsw[4]; u8 pwdb_all; @@ -706,8 +700,8 @@ struct rx_desc_8723e { } __packed; void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, - struct ieee80211_hdr *hdr, u8 *pdesc_tx, - struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, u8 *pdesc, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); @@ -715,7 +709,8 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *status, struct ieee80211_rx_status *rx_status, u8 *pdesc, struct sk_buff *skb); -void rtl8723ae_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); +void rtl8723ae_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); u32 rtl8723ae_get_desc(u8 *pdesc, bool istx, u8 desc_name); void rtl8723ae_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/Makefile b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile new file mode 100644 index 00000000000..59e416abd93 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/Makefile @@ -0,0 +1,19 @@ +obj-m := rtl8723be.o + + +rtl8723be-objs := \ + dm.o \ + fw.o \ + hw.o \ + led.o \ + phy.o \ + pwrseq.o \ + rf.o \ + sw.o \ + table.o \ + trx.o \ + + +obj-$(CONFIG_RTL8723BE) += rtl8723be.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/def.h b/drivers/net/wireless/rtlwifi/rtl8723be/def.h new file mode 100644 index 00000000000..3c30b74e983 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/def.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_DEF_H__ +#define __RTL8723BE_DEF_H__ + +#define HAL_RETRY_LIMIT_INFRA 48 +#define HAL_RETRY_LIMIT_AP_ADHOC 7 + +#define RESET_DELAY_8185 20 + +#define RT_IBSS_INT_MASKS (IMR_BCNINT | IMR_TBDOK | IMR_TBDER) +#define RT_AC_INT_MASKS (IMR_VIDOK | IMR_VODOK | IMR_BEDOK|IMR_BKDOK) + +#define NUM_OF_FIRMWARE_QUEUE 10 +#define NUM_OF_PAGES_IN_FW 0x100 +#define NUM_OF_PAGE_IN_FW_QUEUE_BK 0x07 +#define NUM_OF_PAGE_IN_FW_QUEUE_BE 0x07 +#define NUM_OF_PAGE_IN_FW_QUEUE_VI 0x07 +#define NUM_OF_PAGE_IN_FW_QUEUE_VO 0x07 +#define NUM_OF_PAGE_IN_FW_QUEUE_HCCA 0x0 +#define NUM_OF_PAGE_IN_FW_QUEUE_CMD 0x0 +#define NUM_OF_PAGE_IN_FW_QUEUE_MGNT 0x02 +#define NUM_OF_PAGE_IN_FW_QUEUE_HIGH 0x02 +#define NUM_OF_PAGE_IN_FW_QUEUE_BCN 0x2 +#define NUM_OF_PAGE_IN_FW_QUEUE_PUB 0xA1 + +#define NUM_OF_PAGE_IN_FW_QUEUE_BK_DTM 0x026 +#define NUM_OF_PAGE_IN_FW_QUEUE_BE_DTM 0x048 +#define NUM_OF_PAGE_IN_FW_QUEUE_VI_DTM 0x048 +#define NUM_OF_PAGE_IN_FW_QUEUE_VO_DTM 0x026 +#define NUM_OF_PAGE_IN_FW_QUEUE_PUB_DTM 0x00 + +#define MAX_LINES_HWCONFIG_TXT 1000 +#define MAX_BYTES_LINE_HWCONFIG_TXT 256 + +#define SW_THREE_WIRE 0 +#define HW_THREE_WIRE 2 + +#define BT_DEMO_BOARD 0 +#define BT_QA_BOARD 1 +#define BT_FPGA 2 + +#define HAL_PRIME_CHNL_OFFSET_DONT_CARE 0 +#define HAL_PRIME_CHNL_OFFSET_LOWER 1 +#define HAL_PRIME_CHNL_OFFSET_UPPER 2 + +#define MAX_H2C_QUEUE_NUM 10 + +#define RX_MPDU_QUEUE 0 +#define RX_CMD_QUEUE 1 +#define RX_MAX_QUEUE 2 +#define AC2QUEUEID(_AC) (_AC) + +#define C2H_RX_CMD_HDR_LEN 8 +#define GET_C2H_CMD_CMD_LEN(__prxhdr) \ + LE_BITS_TO_4BYTE((__prxhdr), 0, 16) +#define GET_C2H_CMD_ELEMENT_ID(__prxhdr) \ + LE_BITS_TO_4BYTE((__prxhdr), 16, 8) +#define GET_C2H_CMD_CMD_SEQ(__prxhdr) \ + LE_BITS_TO_4BYTE((__prxhdr), 24, 7) +#define GET_C2H_CMD_CONTINUE(__prxhdr) \ + LE_BITS_TO_4BYTE((__prxhdr), 31, 1) +#define GET_C2H_CMD_CONTENT(__prxhdr) \ + ((u8 *)(__prxhdr) + C2H_RX_CMD_HDR_LEN) + +#define GET_C2H_CMD_FEEDBACK_ELEMENT_ID(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE((__pcmdfbhdr), 0, 8) +#define GET_C2H_CMD_FEEDBACK_CCX_LEN(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE((__pcmdfbhdr), 8, 8) +#define GET_C2H_CMD_FEEDBACK_CCX_CMD_CNT(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE((__pcmdfbhdr), 16, 16) +#define GET_C2H_CMD_FEEDBACK_CCX_MAC_ID(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 0, 5) +#define GET_C2H_CMD_FEEDBACK_CCX_VALID(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 7, 1) +#define GET_C2H_CMD_FEEDBACK_CCX_RETRY_CNT(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 8, 5) +#define GET_C2H_CMD_FEEDBACK_CCX_TOK(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 15, 1) +#define GET_C2H_CMD_FEEDBACK_CCX_QSEL(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 16, 4) +#define GET_C2H_CMD_FEEDBACK_CCX_SEQ(__pcmdfbhdr) \ + LE_BITS_TO_4BYTE(((__pcmdfbhdr) + 4), 20, 12) + +#define CHIP_BONDING_IDENTIFIER(_value) (((_value)>>22)&0x3) +#define CHIP_BONDING_92C_1T2R 0x1 + +#define CHIP_8723 BIT(0) +#define CHIP_8723B (BIT(1) | BIT(2)) +#define NORMAL_CHIP BIT(3) +#define RF_TYPE_1T1R (~(BIT(4) | BIT(5) | BIT(6))) +#define RF_TYPE_1T2R BIT(4) +#define RF_TYPE_2T2R BIT(5) +#define CHIP_VENDOR_UMC BIT(7) +#define B_CUT_VERSION BIT(12) +#define C_CUT_VERSION BIT(13) +#define D_CUT_VERSION ((BIT(12) | BIT(13))) +#define E_CUT_VERSION BIT(14) +#define RF_RL_ID (BIT(31) | BIT(30) | BIT(29) | BIT(28)) + +/* MASK */ +#define IC_TYPE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define CHIP_TYPE_MASK BIT(3) +#define RF_TYPE_MASK (BIT(4) | BIT(5) | BIT(6)) +#define MANUFACTUER_MASK BIT(7) +#define ROM_VERSION_MASK (BIT(11) | BIT(10) | BIT(9) | BIT(8)) +#define CUT_VERSION_MASK (BIT(15) | BIT(14) | BIT(13) | BIT(12)) + +/* Get element */ +#define GET_CVID_IC_TYPE(version) ((version) & IC_TYPE_MASK) +#define GET_CVID_CHIP_TYPE(version) ((version) & CHIP_TYPE_MASK) +#define GET_CVID_RF_TYPE(version) ((version) & RF_TYPE_MASK) +#define GET_CVID_MANUFACTUER(version) ((version) & MANUFACTUER_MASK) +#define GET_CVID_ROM_VERSION(version) ((version) & ROM_VERSION_MASK) +#define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK) + +#define IS_92C_SERIAL(version) ((IS_81XXC(version) && IS_2T2R(version)) ?\ + true : false) +#define IS_81XXC(version) ((GET_CVID_IC_TYPE(version) == 0) ?\ + true : false) +#define IS_8723_SERIES(version) ((GET_CVID_IC_TYPE(version) == CHIP_8723) ?\ + true : false) +#define IS_1T1R(version) ((GET_CVID_RF_TYPE(version)) ? false : true) +#define IS_1T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_1T2R)\ + ? true : false) +#define IS_2T2R(version) ((GET_CVID_RF_TYPE(version) == RF_TYPE_2T2R)\ + ? true : false) +enum rf_optype { + RF_OP_BY_SW_3WIRE = 0, + RF_OP_BY_FW, + RF_OP_MAX +}; + +enum rf_power_state { + RF_ON, + RF_OFF, + RF_SLEEP, + RF_SHUT_DOWN, +}; + +enum power_save_mode { + POWER_SAVE_MODE_ACTIVE, + POWER_SAVE_MODE_SAVE, +}; + +enum power_polocy_config { + POWERCFG_MAX_POWER_SAVINGS, + POWERCFG_GLOBAL_POWER_SAVINGS, + POWERCFG_LOCAL_POWER_SAVINGS, + POWERCFG_LENOVO, +}; + +enum interface_select_pci { + INTF_SEL1_MINICARD = 0, + INTF_SEL0_PCIE = 1, + INTF_SEL2_RSV = 2, + INTF_SEL3_RSV = 3, +}; + +enum rtl_desc_qsel { + QSLT_BK = 0x2, + QSLT_BE = 0x0, + QSLT_VI = 0x5, + QSLT_VO = 0x7, + QSLT_BEACON = 0x10, + QSLT_HIGH = 0x11, + QSLT_MGNT = 0x12, + QSLT_CMD = 0x13, +}; + +enum rtl_desc8723e_rate { + DESC92C_RATE1M = 0x00, + DESC92C_RATE2M = 0x01, + DESC92C_RATE5_5M = 0x02, + DESC92C_RATE11M = 0x03, + + DESC92C_RATE6M = 0x04, + DESC92C_RATE9M = 0x05, + DESC92C_RATE12M = 0x06, + DESC92C_RATE18M = 0x07, + DESC92C_RATE24M = 0x08, + DESC92C_RATE36M = 0x09, + DESC92C_RATE48M = 0x0a, + DESC92C_RATE54M = 0x0b, + + DESC92C_RATEMCS0 = 0x0c, + DESC92C_RATEMCS1 = 0x0d, + DESC92C_RATEMCS2 = 0x0e, + DESC92C_RATEMCS3 = 0x0f, + DESC92C_RATEMCS4 = 0x10, + DESC92C_RATEMCS5 = 0x11, + DESC92C_RATEMCS6 = 0x12, + DESC92C_RATEMCS7 = 0x13, + DESC92C_RATEMCS8 = 0x14, + DESC92C_RATEMCS9 = 0x15, + DESC92C_RATEMCS10 = 0x16, + DESC92C_RATEMCS11 = 0x17, + DESC92C_RATEMCS12 = 0x18, + DESC92C_RATEMCS13 = 0x19, + DESC92C_RATEMCS14 = 0x1a, + DESC92C_RATEMCS15 = 0x1b, + DESC92C_RATEMCS15_SG = 0x1c, + DESC92C_RATEMCS32 = 0x20, +}; + +enum rx_packet_type { + NORMAL_RX, + TX_REPORT1, + TX_REPORT2, + HIS_REPORT, +}; + +struct phy_sts_cck_8723e_t { + u8 adc_pwdb_X[4]; + u8 sq_rpt; + u8 cck_agc_rpt; +}; + +struct h2c_cmd_8723e { + u8 element_id; + u32 cmd_len; + u8 *p_cmdbuffer; +}; + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c new file mode 100644 index 00000000000..13d53a1df78 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c @@ -0,0 +1,1325 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../base.h" +#include "../pci.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "dm.h" +#include "../rtl8723com/dm_common.h" +#include "fw.h" +#include "../rtl8723com/fw_common.h" +#include "trx.h" +#include "../btcoexist/rtl_btc.h" + +static const u32 ofdmswing_table[] = { + 0x0b40002d, /* 0, -15.0dB */ + 0x0c000030, /* 1, -14.5dB */ + 0x0cc00033, /* 2, -14.0dB */ + 0x0d800036, /* 3, -13.5dB */ + 0x0e400039, /* 4, -13.0dB */ + 0x0f00003c, /* 5, -12.5dB */ + 0x10000040, /* 6, -12.0dB */ + 0x11000044, /* 7, -11.5dB */ + 0x12000048, /* 8, -11.0dB */ + 0x1300004c, /* 9, -10.5dB */ + 0x14400051, /* 10, -10.0dB */ + 0x15800056, /* 11, -9.5dB */ + 0x16c0005b, /* 12, -9.0dB */ + 0x18000060, /* 13, -8.5dB */ + 0x19800066, /* 14, -8.0dB */ + 0x1b00006c, /* 15, -7.5dB */ + 0x1c800072, /* 16, -7.0dB */ + 0x1e400079, /* 17, -6.5dB */ + 0x20000080, /* 18, -6.0dB */ + 0x22000088, /* 19, -5.5dB */ + 0x24000090, /* 20, -5.0dB */ + 0x26000098, /* 21, -4.5dB */ + 0x288000a2, /* 22, -4.0dB */ + 0x2ac000ab, /* 23, -3.5dB */ + 0x2d4000b5, /* 24, -3.0dB */ + 0x300000c0, /* 25, -2.5dB */ + 0x32c000cb, /* 26, -2.0dB */ + 0x35c000d7, /* 27, -1.5dB */ + 0x390000e4, /* 28, -1.0dB */ + 0x3c8000f2, /* 29, -0.5dB */ + 0x40000100, /* 30, +0dB */ + 0x43c0010f, /* 31, +0.5dB */ + 0x47c0011f, /* 32, +1.0dB */ + 0x4c000130, /* 33, +1.5dB */ + 0x50800142, /* 34, +2.0dB */ + 0x55400155, /* 35, +2.5dB */ + 0x5a400169, /* 36, +3.0dB */ + 0x5fc0017f, /* 37, +3.5dB */ + 0x65400195, /* 38, +4.0dB */ + 0x6b8001ae, /* 39, +4.5dB */ + 0x71c001c7, /* 40, +5.0dB */ + 0x788001e2, /* 41, +5.5dB */ + 0x7f8001fe /* 42, +6.0dB */ +}; + +static const u8 cckswing_table_ch1ch13[CCK_TABLE_SIZE][8] = { + {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01}, /* 0, -16.0dB */ + {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, /* 1, -15.5dB */ + {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 2, -15.0dB */ + {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, /* 3, -14.5dB */ + {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 4, -14.0dB */ + {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, /* 5, -13.5dB */ + {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, /* 6, -13.0dB */ + {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, /* 7, -12.5dB */ + {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, /* 8, -12.0dB */ + {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, /* 9, -11.5dB */ + {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 10, -11.0dB */ + {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, /* 11, -10.5dB */ + {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 12, -10.0dB */ + {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, /* 13, -9.5dB */ + {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, /* 14, -9.0dB */ + {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, /* 15, -8.5dB */ + {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, /* 16, -8.0dB */ + {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, /* 17, -7.5dB */ + {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, /* 18, -7.0dB */ + {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, /* 19, -6.5dB */ + {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, /* 20, -6.0dB */ + {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, /* 21, -5.5dB */ + {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, /* 22, -5.0dB */ + {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, /* 23, -4.5dB */ + {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, /* 24, -4.0dB */ + {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, /* 25, -3.5dB */ + {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, /* 26, -3.0dB */ + {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, /* 27, -2.5dB */ + {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, /* 28, -2.0dB */ + {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, /* 29, -1.5dB */ + {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, /* 30, -1.0dB */ + {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, /* 31, -0.5dB */ + {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04} /* 32, +0dB */ +}; + +static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { + {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00}, /* 0, -16.0dB */ + {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 1, -15.5dB */ + {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 2, -15.0dB */ + {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 3, -14.5dB */ + {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, /* 4, -14.0dB */ + {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 5, -13.5dB */ + {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 6, -13.0dB */ + {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, /* 7, -12.5dB */ + {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 8, -12.0dB */ + {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, /* 9, -11.5dB */ + {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 10, -11.0dB */ + {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, /* 11, -10.5dB */ + {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 12, -10.0dB */ + {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, /* 13, -9.5dB */ + {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 14, -9.0dB */ + {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, /* 15, -8.5dB */ + {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 16, -8.0dB */ + {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, /* 17, -7.5dB */ + {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, /* 18, -7.0dB */ + {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, /* 19, -6.5dB */ + {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 20, -6.0dB */ + {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, /* 21, -5.5dB */ + {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, /* 22, -5.0dB */ + {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, /* 23, -4.5dB */ + {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, /* 24, -4.0dB */ + {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, /* 25, -3.5dB */ + {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, /* 26, -3.0dB */ + {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, /* 27, -2.5dB */ + {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, /* 28, -2.0dB */ + {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, /* 29, -1.5dB */ + {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, /* 30, -1.0dB */ + {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, /* 31, -0.5dB */ + {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00} /* 32, +0dB */ +}; + +static const u32 edca_setting_dl[PEER_MAX] = { + 0xa44f, /* 0 UNKNOWN */ + 0x5ea44f, /* 1 REALTEK_90 */ + 0x5e4322, /* 2 REALTEK_92SE */ + 0x5ea42b, /* 3 BROAD */ + 0xa44f, /* 4 RAL */ + 0xa630, /* 5 ATH */ + 0x5ea630, /* 6 CISCO */ + 0x5ea42b, /* 7 MARVELL */ +}; + +static const u32 edca_setting_ul[PEER_MAX] = { + 0x5e4322, /* 0 UNKNOWN */ + 0xa44f, /* 1 REALTEK_90 */ + 0x5ea44f, /* 2 REALTEK_92SE */ + 0x5ea32b, /* 3 BROAD */ + 0x5ea422, /* 4 RAL */ + 0x5ea322, /* 5 ATH */ + 0x3ea430, /* 6 CISCO */ + 0x5ea44f, /* 7 MARV */ +}; + +void rtl8723be_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 type, + u8 *pdirection, u32 *poutwrite_val) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw)); + u8 pwr_val = 0; + u8 ofdm_base = rtlpriv->dm.swing_idx_ofdm_base[RF90_PATH_A]; + u8 ofdm_val = rtlpriv->dm.swing_idx_ofdm[RF90_PATH_A]; + u8 cck_base = rtldm->swing_idx_cck_base; + u8 cck_val = rtldm->swing_idx_cck; + + if (type == 0) { + if (ofdm_val <= ofdm_base) { + *pdirection = 1; + pwr_val = ofdm_base - ofdm_val; + } else { + *pdirection = 2; + pwr_val = ofdm_val - ofdm_base; + } + } else if (type == 1) { + if (cck_val <= cck_base) { + *pdirection = 1; + pwr_val = cck_base - cck_val; + } else { + *pdirection = 2; + pwr_val = cck_val - cck_base; + } + } + + if (pwr_val >= TXPWRTRACK_MAX_IDX && (*pdirection == 1)) + pwr_val = TXPWRTRACK_MAX_IDX; + + *poutwrite_val = pwr_val | (pwr_val << 8) | + (pwr_val << 16) | (pwr_val << 24); +} + +static void rtl8723be_dm_diginit(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + dm_digtable->dig_enable_flag = true; + dm_digtable->cur_igvalue = rtl_get_bbreg(hw, + ROFDM0_XAAGCCORE1, 0x7f); + dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; + dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; + dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; + dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; + dm_digtable->rx_gain_max = DM_DIG_MAX; + dm_digtable->rx_gain_min = DM_DIG_MIN; + dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; + dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; + dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; + dm_digtable->pre_cck_cca_thres = 0xff; + dm_digtable->cur_cck_cca_thres = 0x83; + dm_digtable->forbidden_igi = DM_DIG_MIN; + dm_digtable->large_fa_hit = 0; + dm_digtable->recover_cnt = 0; + dm_digtable->dig_min_0 = DM_DIG_MIN; + dm_digtable->dig_min_1 = DM_DIG_MIN; + dm_digtable->media_connect_0 = false; + dm_digtable->media_connect_1 = false; + rtlpriv->dm.dm_initialgain_enable = true; + dm_digtable->bt30_cur_igi = 0x32; +} + +void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rate_adaptive *ra = &(rtlpriv->ra); + + ra->ratr_state = DM_RATR_STA_INIT; + ra->pre_ratr_state = DM_RATR_STA_INIT; + + if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER) + rtlpriv->dm.useramask = true; + else + rtlpriv->dm.useramask = false; + + ra->high_rssi_thresh_for_ra = 50; + ra->low_rssi_thresh_for_ra40m = 20; +} + +static void rtl8723be_dm_init_txpower_tracking(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.txpower_tracking = true; + rtlpriv->dm.txpower_track_control = true; + rtlpriv->dm.thermalvalue = 0; + + rtlpriv->dm.ofdm_index[0] = 30; + rtlpriv->dm.cck_index = 20; + + rtlpriv->dm.swing_idx_cck_base = rtlpriv->dm.cck_index; + + rtlpriv->dm.swing_idx_ofdm_base[0] = rtlpriv->dm.ofdm_index[0]; + rtlpriv->dm.delta_power_index[RF90_PATH_A] = 0; + rtlpriv->dm.delta_power_index_last[RF90_PATH_A] = 0; + rtlpriv->dm.power_index_offset[RF90_PATH_A] = 0; + + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + " rtlpriv->dm.txpower_tracking = %d\n", + rtlpriv->dm.txpower_tracking); +} + +static void rtl8723be_dm_init_dynamic_atc_switch(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.crystal_cap = rtlpriv->efuse.crystalcap; + rtlpriv->dm.atc_status = rtl_get_bbreg(hw, ROFDM1_CFOTRACKING, 0x800); + rtlpriv->dm.cfo_threshold = CFO_THRESHOLD_XTAL; +} + +void rtl8723be_dm_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; + rtl8723be_dm_diginit(hw); + rtl8723be_dm_init_rate_adaptive_mask(hw); + rtl8723_dm_init_edca_turbo(hw); + rtl8723_dm_init_dynamic_bb_powersaving(hw); + rtl8723_dm_init_dynamic_txpower(hw); + rtl8723be_dm_init_txpower_tracking(hw); + rtl8723be_dm_init_dynamic_atc_switch(hw); +} + +static void rtl8723be_dm_find_minimum_rssi(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *rtl_dm_dig = &(rtlpriv->dm_digtable); + struct rtl_mac *mac = rtl_mac(rtlpriv); + + /* Determine the minimum RSSI */ + if ((mac->link_state < MAC80211_LINKED) && + (rtlpriv->dm.entry_min_undec_sm_pwdb == 0)) { + rtl_dm_dig->min_undec_pwdb_for_dm = 0; + RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, + "Not connected to any\n"); + } + if (mac->link_state >= MAC80211_LINKED) { + if (mac->opmode == NL80211_IFTYPE_AP || + mac->opmode == NL80211_IFTYPE_ADHOC) { + rtl_dm_dig->min_undec_pwdb_for_dm = + rtlpriv->dm.entry_min_undec_sm_pwdb; + RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, + "AP Client PWDB = 0x%lx\n", + rtlpriv->dm.entry_min_undec_sm_pwdb); + } else { + rtl_dm_dig->min_undec_pwdb_for_dm = + rtlpriv->dm.undec_sm_pwdb; + RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, + "STA Default Port PWDB = 0x%x\n", + rtl_dm_dig->min_undec_pwdb_for_dm); + } + } else { + rtl_dm_dig->min_undec_pwdb_for_dm = + rtlpriv->dm.entry_min_undec_sm_pwdb; + RT_TRACE(rtlpriv, COMP_BB_POWERSAVING, DBG_LOUD, + "AP Ext Port or disconnet PWDB = 0x%x\n", + rtl_dm_dig->min_undec_pwdb_for_dm); + } + RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "MinUndecoratedPWDBForDM =%d\n", + rtl_dm_dig->min_undec_pwdb_for_dm); +} + +static void rtl8723be_dm_check_rssi_monitor(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_sta_info *drv_priv; + u8 h2c_parameter[3] = { 0 }; + long tmp_entry_max_pwdb = 0, tmp_entry_min_pwdb = 0xff; + + /* AP & ADHOC & MESH */ + spin_lock_bh(&rtlpriv->locks.entry_list_lock); + list_for_each_entry(drv_priv, &rtlpriv->entry_list, list) { + if (drv_priv->rssi_stat.undec_sm_pwdb < + tmp_entry_min_pwdb) + tmp_entry_min_pwdb = + drv_priv->rssi_stat.undec_sm_pwdb; + if (drv_priv->rssi_stat.undec_sm_pwdb > + tmp_entry_max_pwdb) + tmp_entry_max_pwdb = + drv_priv->rssi_stat.undec_sm_pwdb; + } + spin_unlock_bh(&rtlpriv->locks.entry_list_lock); + + /* If associated entry is found */ + if (tmp_entry_max_pwdb != 0) { + rtlpriv->dm.entry_max_undec_sm_pwdb = tmp_entry_max_pwdb; + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "EntryMaxPWDB = 0x%lx(%ld)\n", + tmp_entry_max_pwdb, tmp_entry_max_pwdb); + } else { + rtlpriv->dm.entry_max_undec_sm_pwdb = 0; + } + /* If associated entry is found */ + if (tmp_entry_min_pwdb != 0xff) { + rtlpriv->dm.entry_min_undec_sm_pwdb = tmp_entry_min_pwdb; + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "EntryMinPWDB = 0x%lx(%ld)\n", + tmp_entry_min_pwdb, tmp_entry_min_pwdb); + } else { + rtlpriv->dm.entry_min_undec_sm_pwdb = 0; + } + /* Indicate Rx signal strength to FW. */ + if (rtlpriv->dm.useramask) { + h2c_parameter[2] = (u8) (rtlpriv->dm.undec_sm_pwdb & 0xFF); + h2c_parameter[1] = 0x20; + h2c_parameter[0] = 0; + rtl8723be_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, h2c_parameter); + } else { + rtl_write_byte(rtlpriv, 0x4fe, rtlpriv->dm.undec_sm_pwdb); + } + rtl8723be_dm_find_minimum_rssi(hw); + rtlpriv->dm_digtable.rssi_val_min = + rtlpriv->dm_digtable.min_undec_pwdb_for_dm; +} + +void rtl8723be_dm_write_dig(struct ieee80211_hw *hw, u8 current_igi) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->dm_digtable.cur_igvalue != current_igi) { + rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f, current_igi); + if (rtlpriv->phy.rf_type != RF_1T1R) + rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, 0x7f, current_igi); + } + rtlpriv->dm_digtable.pre_igvalue = rtlpriv->dm_digtable.cur_igvalue; + rtlpriv->dm_digtable.cur_igvalue = current_igi; +} + +static void rtl8723be_dm_dig(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct dig_t *dm_digtable = &(rtlpriv->dm_digtable); + u8 dig_dynamic_min, dig_maxofmin; + bool firstconnect, firstdisconnect; + u8 dm_dig_max, dm_dig_min; + u8 current_igi = dm_digtable->cur_igvalue; + u8 offset; + + /* AP, BT */ + if (mac->act_scanning) + return; + + dig_dynamic_min = dm_digtable->dig_min_0; + firstconnect = (mac->link_state >= MAC80211_LINKED) && + !dm_digtable->media_connect_0; + firstdisconnect = (mac->link_state < MAC80211_LINKED) && + dm_digtable->media_connect_0; + + dm_dig_max = 0x5a; + dm_dig_min = DM_DIG_MIN; + dig_maxofmin = DM_DIG_MAX_AP; + + if (mac->link_state >= MAC80211_LINKED) { + if ((dm_digtable->rssi_val_min + 10) > dm_dig_max) + dm_digtable->rx_gain_max = dm_dig_max; + else if ((dm_digtable->rssi_val_min + 10) < dm_dig_min) + dm_digtable->rx_gain_max = dm_dig_min; + else + dm_digtable->rx_gain_max = + dm_digtable->rssi_val_min + 10; + + if (rtlpriv->dm.one_entry_only) { + offset = 12; + if (dm_digtable->rssi_val_min - offset < dm_dig_min) + dig_dynamic_min = dm_dig_min; + else if (dm_digtable->rssi_val_min - offset > + dig_maxofmin) + dig_dynamic_min = dig_maxofmin; + else + dig_dynamic_min = + dm_digtable->rssi_val_min - offset; + } else { + dig_dynamic_min = dm_dig_min; + } + } else { + dm_digtable->rx_gain_max = dm_dig_max; + dig_dynamic_min = dm_dig_min; + RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n"); + } + + if (rtlpriv->falsealm_cnt.cnt_all > 10000) { + if (dm_digtable->large_fa_hit != 3) + dm_digtable->large_fa_hit++; + if (dm_digtable->forbidden_igi < current_igi) { + dm_digtable->forbidden_igi = current_igi; + dm_digtable->large_fa_hit = 1; + } + + if (dm_digtable->large_fa_hit >= 3) { + if ((dm_digtable->forbidden_igi + 1) > + dm_digtable->rx_gain_max) + dm_digtable->rx_gain_min = + dm_digtable->rx_gain_max; + else + dm_digtable->rx_gain_min = + dm_digtable->forbidden_igi + 1; + dm_digtable->recover_cnt = 3600; + } + } else { + if (dm_digtable->recover_cnt != 0) { + dm_digtable->recover_cnt--; + } else { + if (dm_digtable->large_fa_hit < 3) { + if ((dm_digtable->forbidden_igi - 1) < + dig_dynamic_min) { + dm_digtable->forbidden_igi = + dig_dynamic_min; + dm_digtable->rx_gain_min = + dig_dynamic_min; + } else { + dm_digtable->forbidden_igi--; + dm_digtable->rx_gain_min = + dm_digtable->forbidden_igi + 1; + } + } else { + dm_digtable->large_fa_hit = 0; + } + } + } + if (dm_digtable->rx_gain_min > dm_digtable->rx_gain_max) + dm_digtable->rx_gain_min = dm_digtable->rx_gain_max; + + if (mac->link_state >= MAC80211_LINKED) { + if (firstconnect) { + if (dm_digtable->rssi_val_min <= dig_maxofmin) + current_igi = dm_digtable->rssi_val_min; + else + current_igi = dig_maxofmin; + + dm_digtable->large_fa_hit = 0; + } else { + if (rtlpriv->falsealm_cnt.cnt_all > DM_DIG_FA_TH2) + current_igi += 4; + else if (rtlpriv->falsealm_cnt.cnt_all > DM_DIG_FA_TH1) + current_igi += 2; + else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH0) + current_igi -= 2; + } + } else { + if (firstdisconnect) { + current_igi = dm_digtable->rx_gain_min; + } else { + if (rtlpriv->falsealm_cnt.cnt_all > 10000) + current_igi += 4; + else if (rtlpriv->falsealm_cnt.cnt_all > 8000) + current_igi += 2; + else if (rtlpriv->falsealm_cnt.cnt_all < 500) + current_igi -= 2; + } + } + + if (current_igi > dm_digtable->rx_gain_max) + current_igi = dm_digtable->rx_gain_max; + else if (current_igi < dm_digtable->rx_gain_min) + current_igi = dm_digtable->rx_gain_min; + + rtl8723be_dm_write_dig(hw, current_igi); + dm_digtable->media_connect_0 = + ((mac->link_state >= MAC80211_LINKED) ? true : false); + dm_digtable->dig_min_0 = dig_dynamic_min; +} + +static void rtl8723be_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) +{ + u32 ret_value; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); + + rtl_set_bbreg(hw, DM_REG_OFDM_FA_HOLDC_11N, BIT(31), 1); + rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(31), 1); + + ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE1_11N, MASKDWORD); + falsealm_cnt->cnt_fast_fsync_fail = ret_value & 0xffff; + falsealm_cnt->cnt_sb_search_fail = (ret_value & 0xffff0000) >> 16; + + ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE2_11N, MASKDWORD); + falsealm_cnt->cnt_ofdm_cca = ret_value & 0xffff; + falsealm_cnt->cnt_parity_fail = (ret_value & 0xffff0000) >> 16; + + ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE3_11N, MASKDWORD); + falsealm_cnt->cnt_rate_illegal = ret_value & 0xffff; + falsealm_cnt->cnt_crc8_fail = (ret_value & 0xffff0000) >> 16; + + ret_value = rtl_get_bbreg(hw, DM_REG_OFDM_FA_TYPE4_11N, MASKDWORD); + falsealm_cnt->cnt_mcs_fail = ret_value & 0xffff; + + falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + + falsealm_cnt->cnt_rate_illegal + + falsealm_cnt->cnt_crc8_fail + + falsealm_cnt->cnt_mcs_fail + + falsealm_cnt->cnt_fast_fsync_fail + + falsealm_cnt->cnt_sb_search_fail; + + rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(12), 1); + rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(14), 1); + + ret_value = rtl_get_bbreg(hw, DM_REG_CCK_FA_RST_11N, MASKBYTE0); + falsealm_cnt->cnt_cck_fail = ret_value; + + ret_value = rtl_get_bbreg(hw, DM_REG_CCK_FA_MSB_11N, MASKBYTE3); + falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8; + + ret_value = rtl_get_bbreg(hw, DM_REG_CCK_CCA_CNT_11N, MASKDWORD); + falsealm_cnt->cnt_cck_cca = ((ret_value & 0xff) << 8) | + ((ret_value & 0xff00) >> 8); + + falsealm_cnt->cnt_all = falsealm_cnt->cnt_fast_fsync_fail + + falsealm_cnt->cnt_sb_search_fail + + falsealm_cnt->cnt_parity_fail + + falsealm_cnt->cnt_rate_illegal + + falsealm_cnt->cnt_crc8_fail + + falsealm_cnt->cnt_mcs_fail + + falsealm_cnt->cnt_cck_fail; + + falsealm_cnt->cnt_cca_all = falsealm_cnt->cnt_ofdm_cca + + falsealm_cnt->cnt_cck_cca; + + rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTC_11N, BIT(31), 1); + rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTC_11N, BIT(31), 0); + rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(27), 1); + rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(27), 0); + + rtl_set_bbreg(hw, DM_REG_OFDM_FA_HOLDC_11N, BIT(31), 0); + rtl_set_bbreg(hw, DM_REG_OFDM_FA_RSTD_11N, BIT(31), 0); + + rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(13) | BIT(12), 0); + rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(13) | BIT(12), 2); + + rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(15) | BIT(14), 0); + rtl_set_bbreg(hw, DM_REG_CCK_FA_RST_11N, BIT(15) | BIT(14), 2); + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "cnt_parity_fail = %d, cnt_rate_illegal = %d, " + "cnt_crc8_fail = %d, cnt_mcs_fail = %d\n", + falsealm_cnt->cnt_parity_fail, + falsealm_cnt->cnt_rate_illegal, + falsealm_cnt->cnt_crc8_fail, + falsealm_cnt->cnt_mcs_fail); + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "cnt_ofdm_fail = %x, cnt_cck_fail = %x," + " cnt_all = %x\n", + falsealm_cnt->cnt_ofdm_fail, + falsealm_cnt->cnt_cck_fail, + falsealm_cnt->cnt_all); +} + +static void rtl8723be_dm_dynamic_txpower(struct ieee80211_hw *hw) +{ + /* 8723BE does not support ODM_BB_DYNAMIC_TXPWR*/ + return; +} + +static void rtl8723be_set_iqk_matrix(struct ieee80211_hw *hw, u8 ofdm_index, + u8 rfpath, long iqk_result_x, + long iqk_result_y) +{ + long ele_a = 0, ele_d, ele_c = 0, value32; + + if (ofdm_index >= 43) + ofdm_index = 43 - 1; + + ele_d = (ofdmswing_table[ofdm_index] & 0xFFC00000) >> 22; + + if (iqk_result_x != 0) { + if ((iqk_result_x & 0x00000200) != 0) + iqk_result_x = iqk_result_x | 0xFFFFFC00; + ele_a = ((iqk_result_x * ele_d) >> 8) & 0x000003FF; + + if ((iqk_result_y & 0x00000200) != 0) + iqk_result_y = iqk_result_y | 0xFFFFFC00; + ele_c = ((iqk_result_y * ele_d) >> 8) & 0x000003FF; + + switch (rfpath) { + case RF90_PATH_A: + value32 = (ele_d << 22) | + ((ele_c & 0x3F) << 16) | ele_a; + rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD, + value32); + value32 = (ele_c & 0x000003C0) >> 6; + rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, value32); + value32 = ((iqk_result_x * ele_d) >> 7) & 0x01; + rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), + value32); + break; + default: + break; + } + } else { + switch (rfpath) { + case RF90_PATH_A: + rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, MASKDWORD, + ofdmswing_table[ofdm_index]); + rtl_set_bbreg(hw, ROFDM0_XCTXAFE, MASKH4BITS, 0x00); + rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(24), 0x00); + break; + default: + break; + } + } +} + +static void rtl8723be_dm_tx_power_track_set_power(struct ieee80211_hw *hw, + enum pwr_track_control_method method, + u8 rfpath, u8 idx) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw)); + u8 swing_idx_ofdm_limit = 36; + + if (method == TXAGC) { + rtl8723be_phy_set_txpower_level(hw, rtlphy->current_channel); + } else if (method == BBSWING) { + if (rtldm->swing_idx_cck >= CCK_TABLE_SIZE) + rtldm->swing_idx_cck = CCK_TABLE_SIZE - 1; + + if (!rtldm->cck_inch14) { + rtl_write_byte(rtlpriv, 0xa22, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][0]); + rtl_write_byte(rtlpriv, 0xa23, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][1]); + rtl_write_byte(rtlpriv, 0xa24, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][2]); + rtl_write_byte(rtlpriv, 0xa25, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][3]); + rtl_write_byte(rtlpriv, 0xa26, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][4]); + rtl_write_byte(rtlpriv, 0xa27, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][5]); + rtl_write_byte(rtlpriv, 0xa28, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][6]); + rtl_write_byte(rtlpriv, 0xa29, + cckswing_table_ch1ch13[rtldm->swing_idx_cck][7]); + } else { + rtl_write_byte(rtlpriv, 0xa22, + cckswing_table_ch14[rtldm->swing_idx_cck][0]); + rtl_write_byte(rtlpriv, 0xa23, + cckswing_table_ch14[rtldm->swing_idx_cck][1]); + rtl_write_byte(rtlpriv, 0xa24, + cckswing_table_ch14[rtldm->swing_idx_cck][2]); + rtl_write_byte(rtlpriv, 0xa25, + cckswing_table_ch14[rtldm->swing_idx_cck][3]); + rtl_write_byte(rtlpriv, 0xa26, + cckswing_table_ch14[rtldm->swing_idx_cck][4]); + rtl_write_byte(rtlpriv, 0xa27, + cckswing_table_ch14[rtldm->swing_idx_cck][5]); + rtl_write_byte(rtlpriv, 0xa28, + cckswing_table_ch14[rtldm->swing_idx_cck][6]); + rtl_write_byte(rtlpriv, 0xa29, + cckswing_table_ch14[rtldm->swing_idx_cck][7]); + } + + if (rfpath == RF90_PATH_A) { + if (rtldm->swing_idx_ofdm[RF90_PATH_A] < + swing_idx_ofdm_limit) + swing_idx_ofdm_limit = + rtldm->swing_idx_ofdm[RF90_PATH_A]; + + rtl8723be_set_iqk_matrix(hw, + rtldm->swing_idx_ofdm[rfpath], rfpath, + rtlphy->iqk_matrix[idx].value[0][0], + rtlphy->iqk_matrix[idx].value[0][1]); + } else if (rfpath == RF90_PATH_B) { + if (rtldm->swing_idx_ofdm[RF90_PATH_B] < + swing_idx_ofdm_limit) + swing_idx_ofdm_limit = + rtldm->swing_idx_ofdm[RF90_PATH_B]; + + rtl8723be_set_iqk_matrix(hw, + rtldm->swing_idx_ofdm[rfpath], rfpath, + rtlphy->iqk_matrix[idx].value[0][4], + rtlphy->iqk_matrix[idx].value[0][5]); + } + } else { + return; + } +} + +static void txpwr_track_cb_therm(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw)); + u8 thermalvalue = 0, delta, delta_lck, delta_iqk; + u8 thermalvalue_avg_count = 0; + u32 thermalvalue_avg = 0; + int i = 0; + + u8 ofdm_min_index = 6; + u8 index = 0; + + char delta_swing_table_idx_tup_a[] = { + 0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, + 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 10, + 10, 11, 11, 12, 12, 13, 14, 15}; + char delta_swing_table_idx_tdown_a[] = { + 0, 0, 1, 2, 2, 2, 3, 3, 3, 4, 5, + 5, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9, + 9, 10, 10, 11, 12, 13, 14, 15}; + + /*Initilization ( 7 steps in total)*/ + rtlpriv->dm.txpower_trackinginit = true; + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "rtl8723be_dm_txpower_tracking" + "_callback_thermalmeter\n"); + + thermalvalue = (u8)rtl_get_rfreg(hw, RF90_PATH_A, RF_T_METER, 0xfc00); + if (!rtlpriv->dm.txpower_track_control || thermalvalue == 0 || + rtlefuse->eeprom_thermalmeter == 0xFF) + return; + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "Readback Thermal Meter = 0x%x pre thermal meter 0x%x " + "eeprom_thermalmeter 0x%x\n", + thermalvalue, rtldm->thermalvalue, + rtlefuse->eeprom_thermalmeter); + /*3 Initialize ThermalValues of RFCalibrateInfo*/ + if (!rtldm->thermalvalue) { + rtlpriv->dm.thermalvalue_lck = thermalvalue; + rtlpriv->dm.thermalvalue_iqk = thermalvalue; + } + + /*4 Calculate average thermal meter*/ + rtldm->thermalvalue_avg[rtldm->thermalvalue_avg_index] = thermalvalue; + rtldm->thermalvalue_avg_index++; + if (rtldm->thermalvalue_avg_index == AVG_THERMAL_NUM_8723BE) + rtldm->thermalvalue_avg_index = 0; + + for (i = 0; i < AVG_THERMAL_NUM_8723BE; i++) { + if (rtldm->thermalvalue_avg[i]) { + thermalvalue_avg += rtldm->thermalvalue_avg[i]; + thermalvalue_avg_count++; + } + } + + if (thermalvalue_avg_count) + thermalvalue = (u8)(thermalvalue_avg / thermalvalue_avg_count); + + /* 5 Calculate delta, delta_LCK, delta_IQK.*/ + delta = (thermalvalue > rtlpriv->dm.thermalvalue) ? + (thermalvalue - rtlpriv->dm.thermalvalue) : + (rtlpriv->dm.thermalvalue - thermalvalue); + delta_lck = (thermalvalue > rtlpriv->dm.thermalvalue_lck) ? + (thermalvalue - rtlpriv->dm.thermalvalue_lck) : + (rtlpriv->dm.thermalvalue_lck - thermalvalue); + delta_iqk = (thermalvalue > rtlpriv->dm.thermalvalue_iqk) ? + (thermalvalue - rtlpriv->dm.thermalvalue_iqk) : + (rtlpriv->dm.thermalvalue_iqk - thermalvalue); + + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "Readback Thermal Meter = 0x%x pre thermal meter 0x%x " + "eeprom_thermalmeter 0x%x delta 0x%x " + "delta_lck 0x%x delta_iqk 0x%x\n", + thermalvalue, rtlpriv->dm.thermalvalue, + rtlefuse->eeprom_thermalmeter, delta, delta_lck, delta_iqk); + /* 6 If necessary, do LCK.*/ + if (delta_lck >= IQK_THRESHOLD) { + rtlpriv->dm.thermalvalue_lck = thermalvalue; + rtl8723be_phy_lc_calibrate(hw); + } + + /* 7 If necessary, move the index of + * swing table to adjust Tx power. + */ + if (delta > 0 && rtlpriv->dm.txpower_track_control) { + delta = (thermalvalue > rtlefuse->eeprom_thermalmeter) ? + (thermalvalue - rtlefuse->eeprom_thermalmeter) : + (rtlefuse->eeprom_thermalmeter - thermalvalue); + + if (delta >= TXSCALE_TABLE_SIZE) + delta = TXSCALE_TABLE_SIZE - 1; + /* 7.1 Get the final CCK_index and + * OFDM_index for each swing table. + */ + if (thermalvalue > rtlefuse->eeprom_thermalmeter) { + rtldm->delta_power_index_last[RF90_PATH_A] = + rtldm->delta_power_index[RF90_PATH_A]; + rtldm->delta_power_index[RF90_PATH_A] = + delta_swing_table_idx_tup_a[delta]; + } else { + rtldm->delta_power_index_last[RF90_PATH_A] = + rtldm->delta_power_index[RF90_PATH_A]; + rtldm->delta_power_index[RF90_PATH_A] = + -1 * delta_swing_table_idx_tdown_a[delta]; + } + + /* 7.2 Handle boundary conditions of index.*/ + if (rtldm->delta_power_index[RF90_PATH_A] == + rtldm->delta_power_index_last[RF90_PATH_A]) + rtldm->power_index_offset[RF90_PATH_A] = 0; + else + rtldm->power_index_offset[RF90_PATH_A] = + rtldm->delta_power_index[RF90_PATH_A] - + rtldm->delta_power_index_last[RF90_PATH_A]; + + rtldm->ofdm_index[0] = + rtldm->swing_idx_ofdm_base[RF90_PATH_A] + + rtldm->power_index_offset[RF90_PATH_A]; + rtldm->cck_index = rtldm->swing_idx_cck_base + + rtldm->power_index_offset[RF90_PATH_A]; + + rtldm->swing_idx_cck = rtldm->cck_index; + rtldm->swing_idx_ofdm[0] = rtldm->ofdm_index[0]; + + if (rtldm->ofdm_index[0] > OFDM_TABLE_SIZE - 1) + rtldm->ofdm_index[0] = OFDM_TABLE_SIZE - 1; + else if (rtldm->ofdm_index[0] < ofdm_min_index) + rtldm->ofdm_index[0] = ofdm_min_index; + + if (rtldm->cck_index > CCK_TABLE_SIZE - 1) + rtldm->cck_index = CCK_TABLE_SIZE - 1; + else if (rtldm->cck_index < 0) + rtldm->cck_index = 0; + } else { + rtldm->power_index_offset[RF90_PATH_A] = 0; + } + + if ((rtldm->power_index_offset[RF90_PATH_A] != 0) && + (rtldm->txpower_track_control)) { + rtldm->done_txpower = true; + if (thermalvalue > rtlefuse->eeprom_thermalmeter) + rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0, + index); + else + rtl8723be_dm_tx_power_track_set_power(hw, BBSWING, 0, + index); + + rtldm->swing_idx_cck_base = rtldm->swing_idx_cck; + rtldm->swing_idx_ofdm_base[RF90_PATH_A] = + rtldm->swing_idx_ofdm[0]; + rtldm->thermalvalue = thermalvalue; + } + + if (delta_iqk >= IQK_THRESHOLD) { + rtldm->thermalvalue_iqk = thermalvalue; + rtl8723be_phy_iq_calibrate(hw, false); + } + + rtldm->txpowercount = 0; + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, "end\n"); +} + +void rtl8723be_dm_check_txpower_tracking(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + static u8 tm_trigger; + + if (!rtlpriv->dm.txpower_tracking) + return; + + if (!tm_trigger) { + rtl_set_rfreg(hw, RF90_PATH_A, RF_T_METER, BIT(17) | BIT(16), + 0x03); + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "Trigger 8723be Thermal Meter!!\n"); + tm_trigger = 1; + return; + } else { + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "Schedule TxPowerTracking !!\n"); + txpwr_track_cb_therm(hw); + tm_trigger = 0; + } +} + +static void rtl8723be_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rate_adaptive *ra = &(rtlpriv->ra); + struct ieee80211_sta *sta = NULL; + u32 low_rssithresh_for_ra = ra->low2high_rssi_thresh_for_ra40m; + u32 high_rssithresh_for_ra = ra->high_rssi_thresh_for_ra; + u8 go_up_gap = 5; + + if (is_hal_stop(rtlhal)) { + RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, + "driver is going to unload\n"); + return; + } + + if (!rtlpriv->dm.useramask) { + RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, + "driver does not control rate adaptive mask\n"); + return; + } + + if (mac->link_state == MAC80211_LINKED && + mac->opmode == NL80211_IFTYPE_STATION) { + switch (ra->pre_ratr_state) { + case DM_RATR_STA_MIDDLE: + high_rssithresh_for_ra += go_up_gap; + break; + case DM_RATR_STA_LOW: + high_rssithresh_for_ra += go_up_gap; + low_rssithresh_for_ra += go_up_gap; + break; + default: + break; + } + + if (rtlpriv->dm.undec_sm_pwdb > + (long)high_rssithresh_for_ra) + ra->ratr_state = DM_RATR_STA_HIGH; + else if (rtlpriv->dm.undec_sm_pwdb > + (long)low_rssithresh_for_ra) + ra->ratr_state = DM_RATR_STA_MIDDLE; + else + ra->ratr_state = DM_RATR_STA_LOW; + + if (ra->pre_ratr_state != ra->ratr_state) { + RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, + "RSSI = %ld\n", + rtlpriv->dm.undec_sm_pwdb); + RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, + "RSSI_LEVEL = %d\n", ra->ratr_state); + RT_TRACE(rtlpriv, COMP_RATE, DBG_LOUD, + "PreState = %d, CurState = %d\n", + ra->pre_ratr_state, ra->ratr_state); + + rcu_read_lock(); + sta = rtl_find_sta(hw, mac->bssid); + if (sta) + rtlpriv->cfg->ops->update_rate_tbl(hw, sta, + ra->ratr_state); + rcu_read_unlock(); + + ra->pre_ratr_state = ra->ratr_state; + } + } +} + +static bool rtl8723be_dm_is_edca_turbo_disable(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->cfg->ops->get_btc_status()) { + if (rtlpriv->btcoexist.btc_ops->btc_is_disable_edca_turbo(rtlpriv)) + return true; + } + if (rtlpriv->mac80211.mode == WIRELESS_MODE_B) + return true; + + return false; +} + +static void rtl8723be_dm_check_edca_turbo(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + static u64 last_txok_cnt; + static u64 last_rxok_cnt; + u64 cur_txok_cnt = 0; + u64 cur_rxok_cnt = 0; + u32 edca_be_ul = 0x6ea42b; + u32 edca_be_dl = 0x6ea42b;/*not sure*/ + u32 edca_be = 0x5ea42b; + u32 iot_peer = 0; + bool is_cur_rdlstate; + bool last_is_cur_rdlstate = false; + bool bias_on_rx = false; + bool edca_turbo_on = false; + + last_is_cur_rdlstate = rtlpriv->dm.is_cur_rdlstate; + + cur_txok_cnt = rtlpriv->stats.txbytesunicast - last_txok_cnt; + cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - last_rxok_cnt; + + iot_peer = rtlpriv->mac80211.vendor; + bias_on_rx = (iot_peer == PEER_RAL || iot_peer == PEER_ATH) ? + true : false; + edca_turbo_on = ((!rtlpriv->dm.is_any_nonbepkts) && + (!rtlpriv->dm.disable_framebursting)) ? + true : false; + + if ((iot_peer == PEER_CISCO) && + (mac->mode == WIRELESS_MODE_N_24G)) { + edca_be_dl = edca_setting_dl[iot_peer]; + edca_be_ul = edca_setting_ul[iot_peer]; + } + if (rtl8723be_dm_is_edca_turbo_disable(hw)) + goto exit; + + if (edca_turbo_on) { + if (bias_on_rx) + is_cur_rdlstate = (cur_txok_cnt > cur_rxok_cnt * 4) ? + false : true; + else + is_cur_rdlstate = (cur_rxok_cnt > cur_txok_cnt * 4) ? + true : false; + + edca_be = (is_cur_rdlstate) ? edca_be_dl : edca_be_ul; + rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, edca_be); + rtlpriv->dm.is_cur_rdlstate = is_cur_rdlstate; + rtlpriv->dm.current_turbo_edca = true; + } else { + if (rtlpriv->dm.current_turbo_edca) { + u8 tmp = AC0_BE; + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, + &tmp); + } + rtlpriv->dm.current_turbo_edca = false; + } + +exit: + rtlpriv->dm.is_any_nonbepkts = false; + last_txok_cnt = rtlpriv->stats.txbytesunicast; + last_rxok_cnt = rtlpriv->stats.rxbytesunicast; +} + +static void rtl8723be_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 cur_cck_cca_thresh; + + if (rtlpriv->mac80211.link_state >= MAC80211_LINKED) { + if (rtlpriv->dm_digtable.rssi_val_min > 25) { + cur_cck_cca_thresh = 0xcd; + } else if ((rtlpriv->dm_digtable.rssi_val_min <= 25) && + (rtlpriv->dm_digtable.rssi_val_min > 10)) { + cur_cck_cca_thresh = 0x83; + } else { + if (rtlpriv->falsealm_cnt.cnt_cck_fail > 1000) + cur_cck_cca_thresh = 0x83; + else + cur_cck_cca_thresh = 0x40; + } + } else { + if (rtlpriv->falsealm_cnt.cnt_cck_fail > 1000) + cur_cck_cca_thresh = 0x83; + else + cur_cck_cca_thresh = 0x40; + } + + if (rtlpriv->dm_digtable.cur_cck_cca_thres != cur_cck_cca_thresh) + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, cur_cck_cca_thresh); + + rtlpriv->dm_digtable.pre_cck_cca_thres = rtlpriv->dm_digtable.cur_cck_cca_thres; + rtlpriv->dm_digtable.cur_cck_cca_thres = cur_cck_cca_thresh; + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "CCK cca thresh hold =%x\n", + rtlpriv->dm_digtable.cur_cck_cca_thres); +} + +static void rtl8723be_dm_dynamic_edcca(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 reg_c50, reg_c58; + bool fw_current_in_ps_mode = false; + + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_in_ps_mode)); + if (fw_current_in_ps_mode) + return; + + reg_c50 = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0); + reg_c58 = rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0); + + if (reg_c50 > 0x28 && reg_c58 > 0x28) { + if (!rtlpriv->rtlhal.pre_edcca_enable) { + rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD, 0x03); + rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD + 2, 0x00); + } + } else if (reg_c50 < 0x25 && reg_c58 < 0x25) { + if (rtlpriv->rtlhal.pre_edcca_enable) { + rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD, 0x7f); + rtl_write_byte(rtlpriv, ROFDM0_ECCATHRESHOLD + 2, 0x7f); + } + } +} + +static void rtl8723be_dm_dynamic_atc_switch(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw)); + u8 crystal_cap; + u32 packet_count; + int cfo_khz_a, cfo_khz_b, cfo_ave = 0, adjust_xtal = 0; + int cfo_ave_diff; + + if (rtlpriv->mac80211.link_state < MAC80211_LINKED) { + if (rtldm->atc_status == ATC_STATUS_OFF) { + rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(11), + ATC_STATUS_ON); + rtldm->atc_status = ATC_STATUS_ON; + } + if (rtlpriv->cfg->ops->get_btc_status()) { + if (!rtlpriv->btcoexist.btc_ops->btc_is_bt_disabled(rtlpriv)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "odm_DynamicATCSwitch(): Disable" + " CFO tracking for BT!!\n"); + return; + } + } + + if (rtldm->crystal_cap != rtlpriv->efuse.crystalcap) { + rtldm->crystal_cap = rtlpriv->efuse.crystalcap; + crystal_cap = rtldm->crystal_cap & 0x3f; + rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000, + (crystal_cap | (crystal_cap << 6))); + } + } else { + cfo_khz_a = (int)(rtldm->cfo_tail[0] * 3125) / 1280; + cfo_khz_b = (int)(rtldm->cfo_tail[1] * 3125) / 1280; + packet_count = rtldm->packet_count; + + if (packet_count == rtldm->packet_count_pre) + return; + + rtldm->packet_count_pre = packet_count; + + if (rtlpriv->phy.rf_type == RF_1T1R) + cfo_ave = cfo_khz_a; + else + cfo_ave = (int)(cfo_khz_a + cfo_khz_b) >> 1; + + cfo_ave_diff = (rtldm->cfo_ave_pre >= cfo_ave) ? + (rtldm->cfo_ave_pre - cfo_ave) : + (cfo_ave - rtldm->cfo_ave_pre); + + if (cfo_ave_diff > 20 && rtldm->large_cfo_hit == 0) { + rtldm->large_cfo_hit = 1; + return; + } else { + rtldm->large_cfo_hit = 0; + } + + rtldm->cfo_ave_pre = cfo_ave; + + if (cfo_ave >= -rtldm->cfo_threshold && + cfo_ave <= rtldm->cfo_threshold && rtldm->is_freeze == 0) { + if (rtldm->cfo_threshold == CFO_THRESHOLD_XTAL) { + rtldm->cfo_threshold = CFO_THRESHOLD_XTAL + 10; + rtldm->is_freeze = 1; + } else { + rtldm->cfo_threshold = CFO_THRESHOLD_XTAL; + } + } + + if (cfo_ave > rtldm->cfo_threshold && rtldm->crystal_cap < 0x3f) + adjust_xtal = ((cfo_ave - CFO_THRESHOLD_XTAL) >> 1) + 1; + else if ((cfo_ave < -rtlpriv->dm.cfo_threshold) && + rtlpriv->dm.crystal_cap > 0) + adjust_xtal = ((cfo_ave + CFO_THRESHOLD_XTAL) >> 1) - 1; + + if (adjust_xtal != 0) { + rtldm->is_freeze = 0; + rtldm->crystal_cap += adjust_xtal; + + if (rtldm->crystal_cap > 0x3f) + rtldm->crystal_cap = 0x3f; + else if (rtldm->crystal_cap < 0) + rtldm->crystal_cap = 0; + + crystal_cap = rtldm->crystal_cap & 0x3f; + rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000, + (crystal_cap | (crystal_cap << 6))); + } + + if (cfo_ave < CFO_THRESHOLD_ATC && + cfo_ave > -CFO_THRESHOLD_ATC) { + if (rtldm->atc_status == ATC_STATUS_ON) { + rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(11), + ATC_STATUS_OFF); + rtldm->atc_status = ATC_STATUS_OFF; + } + } else { + if (rtldm->atc_status == ATC_STATUS_OFF) { + rtl_set_bbreg(hw, ROFDM1_CFOTRACKING, BIT(11), + ATC_STATUS_ON); + rtldm->atc_status = ATC_STATUS_ON; + } + } + } +} + +static void rtl8723be_dm_common_info_self_update(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_sta_info *drv_priv; + u8 cnt = 0; + + rtlpriv->dm.one_entry_only = false; + + if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_STATION && + rtlpriv->mac80211.link_state >= MAC80211_LINKED) { + rtlpriv->dm.one_entry_only = true; + return; + } + + if (rtlpriv->mac80211.opmode == NL80211_IFTYPE_AP || + rtlpriv->mac80211.opmode == NL80211_IFTYPE_ADHOC || + rtlpriv->mac80211.opmode == NL80211_IFTYPE_MESH_POINT) { + spin_lock_bh(&rtlpriv->locks.entry_list_lock); + list_for_each_entry(drv_priv, &rtlpriv->entry_list, list) { + cnt++; + } + spin_unlock_bh(&rtlpriv->locks.entry_list_lock); + + if (cnt == 1) + rtlpriv->dm.one_entry_only = true; + } +} + +void rtl8723be_dm_watchdog(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + bool fw_current_inpsmode = false; + bool fw_ps_awake = true; + + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_inpsmode)); + + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON, + (u8 *)(&fw_ps_awake)); + + if (ppsc->p2p_ps_info.p2p_ps_mode) + fw_ps_awake = false; + + if ((ppsc->rfpwr_state == ERFON) && + ((!fw_current_inpsmode) && fw_ps_awake) && + (!ppsc->rfchange_inprogress)) { + rtl8723be_dm_common_info_self_update(hw); + rtl8723be_dm_false_alarm_counter_statistics(hw); + rtl8723be_dm_check_rssi_monitor(hw); + rtl8723be_dm_dig(hw); + rtl8723be_dm_dynamic_edcca(hw); + rtl8723be_dm_cck_packet_detection_thresh(hw); + rtl8723be_dm_refresh_rate_adaptive_mask(hw); + rtl8723be_dm_check_edca_turbo(hw); + rtl8723be_dm_dynamic_atc_switch(hw); + rtl8723be_dm_check_txpower_tracking(hw); + rtl8723be_dm_dynamic_txpower(hw); + if (rtlpriv->cfg->ops->get_btc_status()) + rtlpriv->btcoexist.btc_ops->btc_periodical(rtlpriv); + } + rtlpriv->dm.dbginfo.num_qry_beacon_pkt = 0; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h new file mode 100644 index 00000000000..c6c2f2a78a6 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h @@ -0,0 +1,310 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_DM_H__ +#define __RTL8723BE_DM_H__ + +#define MAIN_ANT 0 +#define AUX_ANT 1 +#define MAIN_ANT_CG_TRX 1 +#define AUX_ANT_CG_TRX 0 +#define MAIN_ANT_CGCS_RX 0 +#define AUX_ANT_CGCS_RX 1 + +#define TXSCALE_TABLE_SIZE 30 + +/*RF REG LIST*/ +#define DM_REG_RF_MODE_11N 0x00 +#define DM_REG_RF_0B_11N 0x0B +#define DM_REG_CHNBW_11N 0x18 +#define DM_REG_T_METER_11N 0x24 +#define DM_REG_RF_25_11N 0x25 +#define DM_REG_RF_26_11N 0x26 +#define DM_REG_RF_27_11N 0x27 +#define DM_REG_RF_2B_11N 0x2B +#define DM_REG_RF_2C_11N 0x2C +#define DM_REG_RXRF_A3_11N 0x3C +#define DM_REG_T_METER_92D_11N 0x42 +#define DM_REG_T_METER_88E_11N 0x42 + +/*BB REG LIST*/ +/*PAGE 8 */ +#define DM_REG_BB_CTRL_11N 0x800 +#define DM_REG_RF_PIN_11N 0x804 +#define DM_REG_PSD_CTRL_11N 0x808 +#define DM_REG_TX_ANT_CTRL_11N 0x80C +#define DM_REG_BB_PWR_SAV5_11N 0x818 +#define DM_REG_CCK_RPT_FORMAT_11N 0x824 +#define DM_REG_RX_DEFUALT_A_11N 0x858 +#define DM_REG_RX_DEFUALT_B_11N 0x85A +#define DM_REG_BB_PWR_SAV3_11N 0x85C +#define DM_REG_ANTSEL_CTRL_11N 0x860 +#define DM_REG_RX_ANT_CTRL_11N 0x864 +#define DM_REG_PIN_CTRL_11N 0x870 +#define DM_REG_BB_PWR_SAV1_11N 0x874 +#define DM_REG_ANTSEL_PATH_11N 0x878 +#define DM_REG_BB_3WIRE_11N 0x88C +#define DM_REG_SC_CNT_11N 0x8C4 +#define DM_REG_PSD_DATA_11N 0x8B4 +/*PAGE 9*/ +#define DM_REG_ANT_MAPPING1_11N 0x914 +#define DM_REG_ANT_MAPPING2_11N 0x918 +/*PAGE A*/ +#define DM_REG_CCK_ANTDIV_PARA1_11N 0xA00 +#define DM_REG_CCK_CCA_11N 0xA0A +#define DM_REG_CCK_ANTDIV_PARA2_11N 0xA0C +#define DM_REG_CCK_ANTDIV_PARA3_11N 0xA10 +#define DM_REG_CCK_ANTDIV_PARA4_11N 0xA14 +#define DM_REG_CCK_FILTER_PARA1_11N 0xA22 +#define DM_REG_CCK_FILTER_PARA2_11N 0xA23 +#define DM_REG_CCK_FILTER_PARA3_11N 0xA24 +#define DM_REG_CCK_FILTER_PARA4_11N 0xA25 +#define DM_REG_CCK_FILTER_PARA5_11N 0xA26 +#define DM_REG_CCK_FILTER_PARA6_11N 0xA27 +#define DM_REG_CCK_FILTER_PARA7_11N 0xA28 +#define DM_REG_CCK_FILTER_PARA8_11N 0xA29 +#define DM_REG_CCK_FA_RST_11N 0xA2C +#define DM_REG_CCK_FA_MSB_11N 0xA58 +#define DM_REG_CCK_FA_LSB_11N 0xA5C +#define DM_REG_CCK_CCA_CNT_11N 0xA60 +#define DM_REG_BB_PWR_SAV4_11N 0xA74 +/*PAGE B */ +#define DM_REG_LNA_SWITCH_11N 0xB2C +#define DM_REG_PATH_SWITCH_11N 0xB30 +#define DM_REG_RSSI_CTRL_11N 0xB38 +#define DM_REG_CONFIG_ANTA_11N 0xB68 +#define DM_REG_RSSI_BT_11N 0xB9C +/*PAGE C */ +#define DM_REG_OFDM_FA_HOLDC_11N 0xC00 +#define DM_REG_RX_PATH_11N 0xC04 +#define DM_REG_TRMUX_11N 0xC08 +#define DM_REG_OFDM_FA_RSTC_11N 0xC0C +#define DM_REG_RXIQI_MATRIX_11N 0xC14 +#define DM_REG_TXIQK_MATRIX_LSB1_11N 0xC4C +#define DM_REG_IGI_A_11N 0xC50 +#define DM_REG_ANTDIV_PARA2_11N 0xC54 +#define DM_REG_IGI_B_11N 0xC58 +#define DM_REG_ANTDIV_PARA3_11N 0xC5C +#define DM_REG_BB_PWR_SAV2_11N 0xC70 +#define DM_REG_RX_OFF_11N 0xC7C +#define DM_REG_TXIQK_MATRIXA_11N 0xC80 +#define DM_REG_TXIQK_MATRIXB_11N 0xC88 +#define DM_REG_TXIQK_MATRIXA_LSB2_11N 0xC94 +#define DM_REG_TXIQK_MATRIXB_LSB2_11N 0xC9C +#define DM_REG_RXIQK_MATRIX_LSB_11N 0xCA0 +#define DM_REG_ANTDIV_PARA1_11N 0xCA4 +#define DM_REG_OFDM_FA_TYPE1_11N 0xCF0 +/*PAGE D */ +#define DM_REG_OFDM_FA_RSTD_11N 0xD00 +#define DM_REG_OFDM_FA_TYPE2_11N 0xDA0 +#define DM_REG_OFDM_FA_TYPE3_11N 0xDA4 +#define DM_REG_OFDM_FA_TYPE4_11N 0xDA8 +/*PAGE E */ +#define DM_REG_TXAGC_A_6_18_11N 0xE00 +#define DM_REG_TXAGC_A_24_54_11N 0xE04 +#define DM_REG_TXAGC_A_1_MCS32_11N 0xE08 +#define DM_REG_TXAGC_A_MCS0_3_11N 0xE10 +#define DM_REG_TXAGC_A_MCS4_7_11N 0xE14 +#define DM_REG_TXAGC_A_MCS8_11_11N 0xE18 +#define DM_REG_TXAGC_A_MCS12_15_11N 0xE1C +#define DM_REG_FPGA0_IQK_11N 0xE28 +#define DM_REG_TXIQK_TONE_A_11N 0xE30 +#define DM_REG_RXIQK_TONE_A_11N 0xE34 +#define DM_REG_TXIQK_PI_A_11N 0xE38 +#define DM_REG_RXIQK_PI_A_11N 0xE3C +#define DM_REG_TXIQK_11N 0xE40 +#define DM_REG_RXIQK_11N 0xE44 +#define DM_REG_IQK_AGC_PTS_11N 0xE48 +#define DM_REG_IQK_AGC_RSP_11N 0xE4C +#define DM_REG_BLUETOOTH_11N 0xE6C +#define DM_REG_RX_WAIT_CCA_11N 0xE70 +#define DM_REG_TX_CCK_RFON_11N 0xE74 +#define DM_REG_TX_CCK_BBON_11N 0xE78 +#define DM_REG_OFDM_RFON_11N 0xE7C +#define DM_REG_OFDM_BBON_11N 0xE80 +#define DM_REG_TX2RX_11N 0xE84 +#define DM_REG_TX2TX_11N 0xE88 +#define DM_REG_RX_CCK_11N 0xE8C +#define DM_REG_RX_OFDM_11N 0xED0 +#define DM_REG_RX_WAIT_RIFS_11N 0xED4 +#define DM_REG_RX2RX_11N 0xED8 +#define DM_REG_STANDBY_11N 0xEDC +#define DM_REG_SLEEP_11N 0xEE0 +#define DM_REG_PMPD_ANAEN_11N 0xEEC + +/*MAC REG LIST*/ +#define DM_REG_BB_RST_11N 0x02 +#define DM_REG_ANTSEL_PIN_11N 0x4C +#define DM_REG_EARLY_MODE_11N 0x4D0 +#define DM_REG_RSSI_MONITOR_11N 0x4FE +#define DM_REG_EDCA_VO_11N 0x500 +#define DM_REG_EDCA_VI_11N 0x504 +#define DM_REG_EDCA_BE_11N 0x508 +#define DM_REG_EDCA_BK_11N 0x50C +#define DM_REG_TXPAUSE_11N 0x522 +#define DM_REG_RESP_TX_11N 0x6D8 +#define DM_REG_ANT_TRAIN_PARA1_11N 0x7b0 +#define DM_REG_ANT_TRAIN_PARA2_11N 0x7b4 + +/*DIG Related*/ +#define DM_BIT_IGI_11N 0x0000007F + +#define HAL_DM_DIG_DISABLE BIT(0) +#define HAL_DM_HIPWR_DISABLE BIT(1) + +#define OFDM_TABLE_LENGTH 43 +#define CCK_TABLE_LENGTH 33 + +#define OFDM_TABLE_SIZE 37 +#define CCK_TABLE_SIZE 33 + +#define BW_AUTO_SWITCH_HIGH_LOW 25 +#define BW_AUTO_SWITCH_LOW_HIGH 30 + +#define DM_DIG_THRESH_HIGH 40 +#define DM_DIG_THRESH_LOW 35 + +#define DM_FALSEALARM_THRESH_LOW 400 +#define DM_FALSEALARM_THRESH_HIGH 1000 + +#define DM_DIG_MAX 0x3e +#define DM_DIG_MIN 0x1e + +#define DM_DIG_MAX_AP 0x32 +#define DM_DIG_MIN_AP 0x20 + +#define DM_DIG_FA_UPPER 0x3e +#define DM_DIG_FA_LOWER 0x1e +#define DM_DIG_FA_TH0 0x200 +#define DM_DIG_FA_TH1 0x300 +#define DM_DIG_FA_TH2 0x400 + +#define DM_DIG_BACKOFF_MAX 12 +#define DM_DIG_BACKOFF_MIN -4 +#define DM_DIG_BACKOFF_DEFAULT 10 + +#define RXPATHSELECTION_DIFF_TH 18 + +#define DM_RATR_STA_INIT 0 +#define DM_RATR_STA_HIGH 1 +#define DM_RATR_STA_MIDDLE 2 +#define DM_RATR_STA_LOW 3 + +#define CTS2SELF_THVAL 30 +#define REGC38_TH 20 + +#define TXHIGHPWRLEVEL_NORMAL 0 +#define TXHIGHPWRLEVEL_LEVEL1 1 +#define TXHIGHPWRLEVEL_LEVEL2 2 +#define TXHIGHPWRLEVEL_BT1 3 +#define TXHIGHPWRLEVEL_BT2 4 + +#define DM_TYPE_BYFW 0 +#define DM_TYPE_BYDRIVER 1 + +#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 +#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 +#define TXPWRTRACK_MAX_IDX 6 + +/* Dynamic ATC switch */ +#define ATC_STATUS_OFF 0x0 /* enable */ +#define ATC_STATUS_ON 0x1 /* disable */ +#define CFO_THRESHOLD_XTAL 10 /* kHz */ +#define CFO_THRESHOLD_ATC 80 /* kHz */ + +enum FAT_STATE { + FAT_NORMAL_STATE = 0, + FAT_TRAINING_STATE = 1, +}; + +enum tag_dynamic_init_gain_operation_type_definition { + DIG_TYPE_THRESH_HIGH = 0, + DIG_TYPE_THRESH_LOW = 1, + DIG_TYPE_BACKOFF = 2, + DIG_TYPE_RX_GAIN_MIN = 3, + DIG_TYPE_RX_GAIN_MAX = 4, + DIG_TYPE_ENABLE = 5, + DIG_TYPE_DISABLE = 6, + DIG_OP_TYPE_MAX +}; + +enum dm_1r_cca_e { + CCA_1R = 0, + CCA_2R = 1, + CCA_MAX = 2, +}; + +enum dm_rf_e { + RF_SAVE = 0, + RF_NORMAL = 1, + RF_MAX = 2, +}; + +enum dm_sw_ant_switch_e { + ANS_ANTENNA_B = 1, + ANS_ANTENNA_A = 2, + ANS_ANTENNA_MAX = 3, +}; + +enum dm_dig_ext_port_alg_e { + DIG_EXT_PORT_STAGE_0 = 0, + DIG_EXT_PORT_STAGE_1 = 1, + DIG_EXT_PORT_STAGE_2 = 2, + DIG_EXT_PORT_STAGE_3 = 3, + DIG_EXT_PORT_STAGE_MAX = 4, +}; + +enum dm_dig_connect_e { + DIG_STA_DISCONNECT = 0, + DIG_STA_CONNECT = 1, + DIG_STA_BEFORE_CONNECT = 2, + DIG_MULTISTA_DISCONNECT = 3, + DIG_MULTISTA_CONNECT = 4, + DIG_CONNECT_MAX +}; + +enum pwr_track_control_method { + BBSWING, + TXAGC +}; + +#define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) +#define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) +#define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1) +#define BT_RSSI_STATE_BG_EDCA_LOW BIT_OFFSET_LEN_MASK_32(3, 1) +#define BT_RSSI_STATE_TXPOWER_LOW BIT_OFFSET_LEN_MASK_32(4, 1) + +void rtl8723be_dm_set_tx_ant_by_tx_info(struct ieee80211_hw *hw, u8 *pdesc, + u32 mac_id); +void rtl8723be_dm_ant_sel_statistics(struct ieee80211_hw *hw, u8 antsel_tr_mux, + u32 mac_id, u32 rx_pwdb_all); +void rtl8723be_dm_fast_antenna_trainning_callback(unsigned long data); +void rtl8723be_dm_init(struct ieee80211_hw *hw); +void rtl8723be_dm_watchdog(struct ieee80211_hw *hw); +void rtl8723be_dm_write_dig(struct ieee80211_hw *hw, u8 current_igi); +void rtl8723be_dm_check_txpower_tracking(struct ieee80211_hw *hw); +void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); +void rtl8723be_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 type, + u8 *pdirection, u32 *poutwrite_val); +void rtl8723be_dm_init_edca_turbo(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/fw.c b/drivers/net/wireless/rtlwifi/rtl8723be/fw.c new file mode 100644 index 00000000000..f856be6fc13 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/fw.c @@ -0,0 +1,620 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "../base.h" +#include "reg.h" +#include "def.h" +#include "fw.h" +#include "../rtl8723com/fw_common.h" + +static bool _rtl8723be_check_fw_read_last_h2c(struct ieee80211_hw *hw, + u8 boxnum) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 val_hmetfr; + bool result = false; + + val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR); + if (((val_hmetfr >> boxnum) & BIT(0)) == 0) + result = true; + return result; +} + +static void _rtl8723be_fill_h2c_command(struct ieee80211_hw *hw, u8 element_id, + u32 cmd_len, u8 *p_cmdbuffer) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 boxnum; + u16 box_reg = 0, box_extreg = 0; + u8 u1b_tmp; + bool isfw_read = false; + u8 buf_index = 0; + bool bwrite_sucess = false; + u8 wait_h2c_limit = 100; + u8 wait_writeh2c_limit = 100; + u8 boxcontent[4], boxextcontent[4]; + u32 h2c_waitcounter = 0; + unsigned long flag; + u8 idx; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n"); + + while (true) { + spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); + if (rtlhal->h2c_setinprogress) { + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "H2C set in progress! Wait to set.." + "element_id(%d).\n", element_id); + + while (rtlhal->h2c_setinprogress) { + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, + flag); + h2c_waitcounter++; + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Wait 100 us (%d times)...\n", + h2c_waitcounter); + udelay(100); + + if (h2c_waitcounter > 1000) + return; + spin_lock_irqsave(&rtlpriv->locks.h2c_lock, + flag); + } + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); + } else { + rtlhal->h2c_setinprogress = true; + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); + break; + } + } + while (!bwrite_sucess) { + wait_writeh2c_limit--; + if (wait_writeh2c_limit == 0) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Write H2C fail because no trigger " + "for FW INT!\n"); + break; + } + boxnum = rtlhal->last_hmeboxnum; + switch (boxnum) { + case 0: + box_reg = REG_HMEBOX_0; + box_extreg = REG_HMEBOX_EXT_0; + break; + case 1: + box_reg = REG_HMEBOX_1; + box_extreg = REG_HMEBOX_EXT_1; + break; + case 2: + box_reg = REG_HMEBOX_2; + box_extreg = REG_HMEBOX_EXT_2; + break; + case 3: + box_reg = REG_HMEBOX_3; + box_extreg = REG_HMEBOX_EXT_3; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } + isfw_read = _rtl8723be_check_fw_read_last_h2c(hw, boxnum); + while (!isfw_read) { + wait_h2c_limit--; + if (wait_h2c_limit == 0) { + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Wating too long for FW read " + "clear HMEBox(%d)!\n", boxnum); + break; + } + udelay(10); + + isfw_read = _rtl8723be_check_fw_read_last_h2c(hw, + boxnum); + u1b_tmp = rtl_read_byte(rtlpriv, 0x130); + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Wating for FW read clear HMEBox(%d)!!! 0x130 = %2x\n", + boxnum, u1b_tmp); + } + if (!isfw_read) { + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Write H2C register BOX[%d] fail!!!!! " + "Fw do not read.\n", boxnum); + break; + } + memset(boxcontent, 0, sizeof(boxcontent)); + memset(boxextcontent, 0, sizeof(boxextcontent)); + boxcontent[0] = element_id; + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Write element_id box_reg(%4x) = %2x\n", + box_reg, element_id); + + switch (cmd_len) { + case 1: + case 2: + case 3: + /*boxcontent[0] &= ~(BIT(7));*/ + memcpy((u8 *)(boxcontent) + 1, + p_cmdbuffer + buf_index, cmd_len); + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + case 4: + case 5: + case 6: + case 7: + /*boxcontent[0] |= (BIT(7));*/ + memcpy((u8 *)(boxextcontent), + p_cmdbuffer + buf_index+3, cmd_len-3); + memcpy((u8 *)(boxcontent) + 1, + p_cmdbuffer + buf_index, 3); + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_extreg + idx, + boxextcontent[idx]); + } + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + bwrite_sucess = true; + + rtlhal->last_hmeboxnum = boxnum + 1; + if (rtlhal->last_hmeboxnum == 4) + rtlhal->last_hmeboxnum = 0; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "pHalData->last_hmeboxnum = %d\n", + rtlhal->last_hmeboxnum); + } + spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); + rtlhal->h2c_setinprogress = false; + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); + + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n"); +} + +void rtl8723be_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, + u32 cmd_len, u8 *p_cmdbuffer) +{ + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u32 tmp_cmdbuf[2]; + + if (!rtlhal->fw_ready) { + RT_ASSERT(false, + "return H2C cmd because of Fw download fail!!!\n"); + return; + } + memset(tmp_cmdbuf, 0, 8); + memcpy(tmp_cmdbuf, p_cmdbuffer, cmd_len); + _rtl8723be_fill_h2c_command(hw, element_id, cmd_len, + (u8 *)&tmp_cmdbuf); + return; +} + +void rtl8723be_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 u1_h2c_set_pwrmode[H2C_8723BE_PWEMODE_LENGTH] = { 0 }; + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + u8 rlbm, power_state = 0; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); + + SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, ((mode) ? 1 : 0)); + rlbm = 0;/*YJ, temp, 120316. FW now not support RLBM = 2.*/ + SET_H2CCMD_PWRMODE_PARM_RLBM(u1_h2c_set_pwrmode, rlbm); + SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, + (rtlpriv->mac80211.p2p) ? + ppsc->smart_ps : 1); + SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(u1_h2c_set_pwrmode, + ppsc->reg_max_lps_awakeintvl); + SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(u1_h2c_set_pwrmode, 0); + if (mode == FW_PS_ACTIVE_MODE) + power_state |= FW_PWR_STATE_ACTIVE; + else + power_state |= FW_PWR_STATE_RF_OFF; + SET_H2CCMD_PWRMODE_PARM_PWR_STATE(u1_h2c_set_pwrmode, power_state); + + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, + "rtl92c_set_fw_pwrmode(): u1_h2c_set_pwrmode\n", + u1_h2c_set_pwrmode, H2C_8723BE_PWEMODE_LENGTH); + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_SETPWRMODE, + H2C_8723BE_PWEMODE_LENGTH, + u1_h2c_set_pwrmode); +} + +static bool _rtl8723be_cmd_send_packet(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring; + struct rtl_tx_desc *pdesc; + struct sk_buff *pskb = NULL; + u8 own; + unsigned long flags; + + ring = &rtlpci->tx_ring[BEACON_QUEUE]; + + pskb = __skb_dequeue(&ring->queue); + if (pskb) + kfree_skb(pskb); + + spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); + + pdesc = &ring->desc[0]; + own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, true, HW_DESC_OWN); + + rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb); + + __skb_queue_tail(&ring->queue, skb); + + spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); + + rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE); + + return true; +} +#define BEACON_PG 0 /* ->1 */ +#define PSPOLL_PG 2 +#define NULL_PG 3 +#define PROBERSP_PG 4 /* ->5 */ + +#define TOTAL_RESERVED_PKT_LEN 768 + +static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { + /* page 0 beacon */ + 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x02, 0xB1, 0x78, + 0xEC, 0x1A, 0x59, 0x0B, 0xAD, 0xD4, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x10, 0x04, 0x00, 0x05, 0x54, 0x65, + 0x73, 0x74, 0x32, 0x01, 0x08, 0x82, 0x84, 0x0B, + 0x16, 0x24, 0x30, 0x48, 0x6C, 0x03, 0x01, 0x06, + 0x06, 0x02, 0x00, 0x00, 0x2A, 0x01, 0x02, 0x32, + 0x04, 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, + 0x09, 0x03, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3D, 0x00, 0xDD, 0x07, 0x00, 0xE0, 0x4C, + 0x02, 0x02, 0x00, 0x00, 0xDD, 0x18, 0x00, 0x50, + 0xF2, 0x01, 0x01, 0x00, 0x00, 0x50, 0xF2, 0x04, + 0x01, 0x00, 0x00, 0x50, 0xF2, 0x04, 0x01, 0x00, + + /* page 1 beacon */ + 0x00, 0x50, 0xF2, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 2 ps-poll */ + 0xA4, 0x10, 0x01, 0xC0, 0xEC, 0x1A, 0x59, 0x0B, + 0xAD, 0xD4, 0x00, 0xE0, 0x4C, 0x02, 0xB1, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 3 null */ + 0x48, 0x01, 0x00, 0x00, 0xEC, 0x1A, 0x59, 0x0B, + 0xAD, 0xD4, 0x00, 0xE0, 0x4C, 0x02, 0xB1, 0x78, + 0xEC, 0x1A, 0x59, 0x0B, 0xAD, 0xD4, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x28, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 4 probe_resp */ + 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, + 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, + 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, + 0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00, + 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, + 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, + 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, + 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, + 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, + 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, + 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 5 probe_resp */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, + bool dl_finished) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct sk_buff *skb = NULL; + + u32 totalpacketlen; + bool rtstatus; + u8 u1rsvdpageloc[5] = { 0 }; + bool dlok = false; + + u8 *beacon; + u8 *p_pspoll; + u8 *nullfunc; + u8 *p_probersp; + /*--------------------------------------------------------- + * (1) beacon + *--------------------------------------------------------- + */ + beacon = &reserved_page_packet[BEACON_PG * 128]; + SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr); + SET_80211_HDR_ADDRESS3(beacon, mac->bssid); + + /*------------------------------------------------------- + * (2) ps-poll + *------------------------------------------------------- + */ + p_pspoll = &reserved_page_packet[PSPOLL_PG * 128]; + SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000)); + SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid); + SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr); + + SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1rsvdpageloc, PSPOLL_PG); + + /*-------------------------------------------------------- + * (3) null data + *-------------------------------------------------------- + */ + nullfunc = &reserved_page_packet[NULL_PG * 128]; + SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid); + SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr); + SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1rsvdpageloc, NULL_PG); + + /*--------------------------------------------------------- + * (4) probe response + *--------------------------------------------------------- + */ + p_probersp = &reserved_page_packet[PROBERSP_PG * 128]; + SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid); + SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr); + SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1rsvdpageloc, PROBERSP_PG); + + totalpacketlen = TOTAL_RESERVED_PKT_LEN; + + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, + "rtl8723be_set_fw_rsvdpagepkt(): " + "HW_VAR_SET_TX_CMD: ALL\n", + &reserved_page_packet[0], totalpacketlen); + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, + "rtl8723be_set_fw_rsvdpagepkt(): " + "HW_VAR_SET_TX_CMD: ALL\n", u1rsvdpageloc, 3); + + + skb = dev_alloc_skb(totalpacketlen); + memcpy((u8 *)skb_put(skb, totalpacketlen), + &reserved_page_packet, totalpacketlen); + + rtstatus = _rtl8723be_cmd_send_packet(hw, skb); + + if (rtstatus) + dlok = true; + + if (dlok) { + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "Set RSVD page location to Fw.\n"); + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, "H2C_RSVDPAGE:\n", + u1rsvdpageloc, 3); + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_RSVDPAGE, + sizeof(u1rsvdpageloc), u1rsvdpageloc); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Set RSVD page location to Fw FAIL!!!!!!.\n"); + } +} + +/*Should check FW support p2p or not.*/ +static void rtl8723be_set_p2p_ctw_period_cmd(struct ieee80211_hw *hw, + u8 ctwindow) +{ + u8 u1_ctwindow_period[1] = {ctwindow}; + + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_P2P_PS_CTW_CMD, 1, + u1_ctwindow_period); +} + +void rtl8723be_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, + u8 p2p_ps_state) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_p2p_ps_info *p2pinfo = &(rtlps->p2p_ps_info); + struct p2p_ps_offload_t *p2p_ps_offload = &rtlhal->p2p_ps_offload; + u8 i; + u16 ctwindow; + u32 start_time, tsf_low; + + switch (p2p_ps_state) { + case P2P_PS_DISABLE: + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_DISABLE\n"); + memset(p2p_ps_offload, 0, sizeof(struct p2p_ps_offload_t)); + break; + case P2P_PS_ENABLE: + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_ENABLE\n"); + /* update CTWindow value. */ + if (p2pinfo->ctwindow > 0) { + p2p_ps_offload->ctwindow_en = 1; + ctwindow = p2pinfo->ctwindow; + rtl8723be_set_p2p_ctw_period_cmd(hw, ctwindow); + } + /* hw only support 2 set of NoA */ + for (i = 0; i < p2pinfo->noa_num; i++) { + /* To control the register setting + * for which NOA + */ + rtl_write_byte(rtlpriv, 0x5cf, (i << 4)); + if (i == 0) + p2p_ps_offload->noa0_en = 1; + else + p2p_ps_offload->noa1_en = 1; + + /* config P2P NoA Descriptor Register */ + rtl_write_dword(rtlpriv, 0x5E0, + p2pinfo->noa_duration[i]); + rtl_write_dword(rtlpriv, 0x5E4, + p2pinfo->noa_interval[i]); + + /*Get Current TSF value */ + tsf_low = rtl_read_dword(rtlpriv, REG_TSFTR); + + start_time = p2pinfo->noa_start_time[i]; + if (p2pinfo->noa_count_type[i] != 1) { + while (start_time <= (tsf_low + (50 * 1024))) { + start_time += p2pinfo->noa_interval[i]; + if (p2pinfo->noa_count_type[i] != 255) + p2pinfo->noa_count_type[i]--; + } + } + rtl_write_dword(rtlpriv, 0x5E8, start_time); + rtl_write_dword(rtlpriv, 0x5EC, + p2pinfo->noa_count_type[i]); + } + if ((p2pinfo->opp_ps == 1) || + (p2pinfo->noa_num > 0)) { + /* rst p2p circuit */ + rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, BIT(4)); + + p2p_ps_offload->offload_en = 1; + + if (P2P_ROLE_GO == rtlpriv->mac80211.p2p) { + p2p_ps_offload->role = 1; + p2p_ps_offload->allstasleep = 0; + } else { + p2p_ps_offload->role = 0; + } + p2p_ps_offload->discovery = 0; + } + break; + case P2P_PS_SCAN: + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN\n"); + p2p_ps_offload->discovery = 1; + break; + case P2P_PS_SCAN_DONE: + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "P2P_PS_SCAN_DONE\n"); + p2p_ps_offload->discovery = 0; + p2pinfo->p2p_ps_state = P2P_PS_ENABLE; + break; + default: + break; + } + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_P2P_PS_OFFLOAD, 1, + (u8 *)p2p_ps_offload); +} + +void rtl8723be_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus) +{ + u8 u1_joinbssrpt_parm[1] = { 0 }; + + SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus); + + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_JOINBSSRPT, 1, + u1_joinbssrpt_parm); +} + +void rtl8723be_set_fw_ap_off_load_cmd(struct ieee80211_hw *hw, + u8 ap_offload_enable) +{ + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u8 u1_apoffload_parm[H2C_8723BE_AP_OFFLOAD_LENGTH] = { 0 }; + + SET_H2CCMD_AP_OFFLOAD_ON(u1_apoffload_parm, ap_offload_enable); + SET_H2CCMD_AP_OFFLOAD_HIDDEN(u1_apoffload_parm, mac->hiddenssid); + SET_H2CCMD_AP_OFFLOAD_DENYANY(u1_apoffload_parm, 0); + + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_AP_OFFLOAD, + H2C_8723BE_AP_OFFLOAD_LENGTH, u1_apoffload_parm); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/fw.h b/drivers/net/wireless/rtlwifi/rtl8723be/fw.h new file mode 100644 index 00000000000..31eec281e44 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/fw.h @@ -0,0 +1,248 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE__FW__H__ +#define __RTL8723BE__FW__H__ + +#define FW_8192C_SIZE 0x8000 +#define FW_8192C_START_ADDRESS 0x1000 +#define FW_8192C_END_ADDRESS 0x5FFF +#define FW_8192C_PAGE_SIZE 4096 +#define FW_8192C_POLLING_DELAY 5 +#define FW_8192C_POLLING_TIMEOUT_COUNT 6000 + +#define IS_FW_HEADER_EXIST(_pfwhdr) \ + ((_pfwhdr->signature&0xFFF0) == 0x5300) +#define USE_OLD_WOWLAN_DEBUG_FW 0 + +#define H2C_8723BE_RSVDPAGE_LOC_LEN 5 +#define H2C_8723BE_PWEMODE_LENGTH 5 +#define H2C_8723BE_JOINBSSRPT_LENGTH 1 +#define H2C_8723BE_AP_OFFLOAD_LENGTH 3 +#define H2C_8723BE_WOWLAN_LENGTH 3 +#define H2C_8723BE_KEEP_ALIVE_CTRL_LENGTH 3 +#if (USE_OLD_WOWLAN_DEBUG_FW == 0) +#define H2C_8723BE_REMOTE_WAKE_CTRL_LEN 1 +#else +#define H2C_8723BE_REMOTE_WAKE_CTRL_LEN 3 +#endif +#define H2C_8723BE_AOAC_GLOBAL_INFO_LEN 2 +#define H2C_8723BE_AOAC_RSVDPAGE_LOC_LEN 7 + + +/* Fw PS state for RPWM. +*BIT[2:0] = HW state +*BIT[3] = Protocol PS state, 1: register active state , 0: register sleep state +*BIT[4] = sub-state +*/ +#define FW_PS_GO_ON BIT(0) +#define FW_PS_TX_NULL BIT(1) +#define FW_PS_RF_ON BIT(2) +#define FW_PS_REGISTER_ACTIVE BIT(3) + +#define FW_PS_DPS BIT(0) +#define FW_PS_LCLK (FW_PS_DPS) +#define FW_PS_RF_OFF BIT(1) +#define FW_PS_ALL_ON BIT(2) +#define FW_PS_ST_ACTIVE BIT(3) +#define FW_PS_ISR_ENABLE BIT(4) +#define FW_PS_IMR_ENABLE BIT(5) + + +#define FW_PS_ACK BIT(6) +#define FW_PS_TOGGLE BIT(7) + + /* 88E RPWM value*/ + /* BIT[0] = 1: 32k, 0: 40M*/ +#define FW_PS_CLOCK_OFF BIT(0) /* 32k*/ +#define FW_PS_CLOCK_ON 0 /*40M*/ + +#define FW_PS_STATE_MASK (0x0F) +#define FW_PS_STATE_HW_MASK (0x07) +/*ISR_ENABLE, IMR_ENABLE, and PS mode should be inherited.*/ +#define FW_PS_STATE_INT_MASK (0x3F) + +#define FW_PS_STATE(x) (FW_PS_STATE_MASK & (x)) +#define FW_PS_STATE_HW(x) (FW_PS_STATE_HW_MASK & (x)) +#define FW_PS_STATE_INT(x) (FW_PS_STATE_INT_MASK & (x)) +#define FW_PS_ISR_VAL(x) ((x) & 0x70) +#define FW_PS_IMR_MASK(x) ((x) & 0xDF) +#define FW_PS_KEEP_IMR(x) ((x) & 0x20) + + +#define FW_PS_STATE_S0 (FW_PS_DPS) +#define FW_PS_STATE_S1 (FW_PS_LCLK) +#define FW_PS_STATE_S2 (FW_PS_RF_OFF) +#define FW_PS_STATE_S3 (FW_PS_ALL_ON) +#define FW_PS_STATE_S4 ((FW_PS_ST_ACTIVE) | (FW_PS_ALL_ON)) + +/* ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE))*/ +#define FW_PS_STATE_ALL_ON_88E (FW_PS_CLOCK_ON) +/* (FW_PS_RF_ON)*/ +#define FW_PS_STATE_RF_ON_88E (FW_PS_CLOCK_ON) +/* 0x0*/ +#define FW_PS_STATE_RF_OFF_88E (FW_PS_CLOCK_ON) +/* (FW_PS_STATE_RF_OFF)*/ +#define FW_PS_STATE_RF_OFF_LOW_PWR_88E (FW_PS_CLOCK_OFF) + +#define FW_PS_STATE_ALL_ON_92C (FW_PS_STATE_S4) +#define FW_PS_STATE_RF_ON_92C (FW_PS_STATE_S3) +#define FW_PS_STATE_RF_OFF_92C (FW_PS_STATE_S2) +#define FW_PS_STATE_RF_OFF_LOW_PWR_92C (FW_PS_STATE_S1) + + +/* For 88E H2C PwrMode Cmd ID 5.*/ +#define FW_PWR_STATE_ACTIVE ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE)) +#define FW_PWR_STATE_RF_OFF 0 + +#define FW_PS_IS_ACK(x) ((x) & FW_PS_ACK) +#define FW_PS_IS_CLK_ON(x) ((x) & (FW_PS_RF_OFF | FW_PS_ALL_ON)) +#define FW_PS_IS_RF_ON(x) ((x) & (FW_PS_ALL_ON)) +#define FW_PS_IS_ACTIVE(x) ((x) & (FW_PS_ST_ACTIVE)) +#define FW_PS_IS_CPWM_INT(x) ((x) & 0x40) + +#define FW_CLR_PS_STATE(x) ((x) = ((x) & (0xF0))) + +#define IS_IN_LOW_POWER_STATE_88E(fwpsstate) \ + (FW_PS_STATE(fwpsstate) == FW_PS_CLOCK_OFF) + +#define FW_PWR_STATE_ACTIVE ((FW_PS_RF_ON) | (FW_PS_REGISTER_ACTIVE)) +#define FW_PWR_STATE_RF_OFF 0 + +#define pagenum_128(_len) (u32)(((_len)>>7) + ((_len)&0x7F ? 1 : 0)) + +#define SET_88E_H2CCMD_WOWLAN_FUNC_ENABLE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_PATTERN_MATCH_ENABLE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_MAGIC_PKT_ENABLE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 2, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_UNICAST_PKT_ENABLE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 3, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_ALL_PKT_DROP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 4, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_GPIO_ACTIVE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 5, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_REKEY_WAKE_UP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 6, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_DISCONNECT_WAKE_UP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 7, 1, __val) +#define SET_88E_H2CCMD_WOWLAN_GPIONUM(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_88E_H2CCMD_WOWLAN_GPIO_DURATION(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + + +#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_PWRMODE_PARM_RLBM(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 4, __val) +#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 4, 4, __val) +#define SET_H2CCMD_PWRMODE_PARM_AWAKE_INTERVAL(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#define SET_H2CCMD_PWRMODE_PARM_ALL_QUEUE_UAPSD(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val) +#define SET_H2CCMD_PWRMODE_PARM_PWR_STATE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+4, 0, 8, __val) +#define GET_88E_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd) \ + LE_BITS_TO_1BYTE(__ph2ccmd, 0, 8) + +#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + +/* AP_OFFLOAD */ +#define SET_H2CCMD_AP_OFFLOAD_ON(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_AP_OFFLOAD_HIDDEN(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_H2CCMD_AP_OFFLOAD_DENYANY(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#define SET_H2CCMD_AP_OFFLOAD_WAKEUP_EVT_RPT(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val) + +/* Keep Alive Control*/ +#define SET_88E_H2CCMD_KEEP_ALIVE_ENABLE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val) +#define SET_88E_H2CCMD_KEEP_ALIVE_ACCPEPT_USER_DEFINED(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val) +#define SET_88E_H2CCMD_KEEP_ALIVE_PERIOD(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + +/*REMOTE_WAKE_CTRL */ +#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_EN(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 1, __val) +#if (USE_OLD_WOWLAN_DEBUG_FW == 0) +#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_ARP_OFFLOAD_EN(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 1, 1, __val) +#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_NDP_OFFLOAD_EN(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 2, 1, __val) +#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GTK_OFFLOAD_EN(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 3, 1, __val) +#else +#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_PAIRWISE_ENC_ALG(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_88E_H2CCMD_REMOTE_WAKE_CTRL_GROUP_ENC_ALG(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#endif + +/* GTK_OFFLOAD */ +#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_88E_H2CCMD_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) + +/* AOAC_RSVDPAGE_LOC */ +#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_REM_WAKE_CTRL_INFO(__ph2ccmd, __val)\ + SET_BITS_TO_LE_1BYTE((__ph2ccmd), 0, 8, __val) +#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_ARP_RSP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_NEIGHBOR_ADV(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_RSP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+3, 0, 8, __val) +#define SET_88E_H2CCMD_AOAC_RSVDPAGE_LOC_GTK_INFO(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+4, 0, 8, __val) + +void rtl8723be_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); +void rtl8723be_set_fw_ap_off_load_cmd(struct ieee80211_hw *hw, + u8 ap_offload_enable); +void rtl8723be_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, + u32 cmd_len, u8 *p_cmdbuffer); +void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw); +void rtl8723be_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, + bool dl_finished); +void rtl8723be_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); +int rtl8723be_download_fw(struct ieee80211_hw *hw, + bool buse_wake_on_wlan_fw); +void rtl8723be_set_p2p_ps_offload_cmd(struct ieee80211_hw *hw, + u8 p2p_ps_state); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c new file mode 100644 index 00000000000..3d555495b45 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c @@ -0,0 +1,2503 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../efuse.h" +#include "../base.h" +#include "../regd.h" +#include "../cam.h" +#include "../ps.h" +#include "../pci.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "dm.h" +#include "../rtl8723com/dm_common.h" +#include "fw.h" +#include "../rtl8723com/fw_common.h" +#include "led.h" +#include "hw.h" +#include "pwrseq.h" +#include "../btcoexist/rtl_btc.h" + +#define LLT_CONFIG 5 + +static void _rtl8723be_return_beacon_queue_skb(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[BEACON_QUEUE]; + + while (skb_queue_len(&ring->queue)) { + struct rtl_tx_desc *entry = &ring->desc[ring->idx]; + struct sk_buff *skb = __skb_dequeue(&ring->queue); + + pci_unmap_single(rtlpci->pdev, + rtlpriv->cfg->ops->get_desc( + (u8 *)entry, true, HW_DESC_TXBUFF_ADDR), + skb->len, PCI_DMA_TODEVICE); + kfree_skb(skb); + ring->idx = (ring->idx + 1) % ring->entries; + } +} + +static void _rtl8723be_set_bcn_ctrl_reg(struct ieee80211_hw *hw, + u8 set_bits, u8 clear_bits) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpci->reg_bcn_ctrl_val |= set_bits; + rtlpci->reg_bcn_ctrl_val &= ~clear_bits; + + rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val); +} + +static void _rtl8723be_stop_tx_beacon(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmp1byte; + + tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6))); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64); + tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); + tmp1byte &= ~(BIT(0)); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); +} + +static void _rtl8723be_resume_tx_beacon(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmp1byte; + + tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6)); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); + tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); + tmp1byte |= BIT(1); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); +} + +static void _rtl8723be_enable_bcn_sub_func(struct ieee80211_hw *hw) +{ + _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(1)); +} + +static void _rtl8723be_disable_bcn_sub_func(struct ieee80211_hw *hw) +{ + _rtl8723be_set_bcn_ctrl_reg(hw, BIT(1), 0); +} + +static void _rtl8723be_set_fw_clock_on(struct ieee80211_hw *hw, u8 rpwm_val, + bool need_turn_off_ckk) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + bool support_remote_wake_up; + u32 count = 0, isr_regaddr, content; + bool schedule_timer = need_turn_off_ckk; + rtlpriv->cfg->ops->get_hw_reg(hw, HAL_DEF_WOWLAN, + (u8 *)(&support_remote_wake_up)); + + if (!rtlhal->fw_ready) + return; + if (!rtlpriv->psc.fw_current_inpsmode) + return; + + while (1) { + spin_lock_bh(&rtlpriv->locks.fw_ps_lock); + if (rtlhal->fw_clk_change_in_progress) { + while (rtlhal->fw_clk_change_in_progress) { + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + count++; + udelay(100); + if (count > 1000) + return; + spin_lock_bh(&rtlpriv->locks.fw_ps_lock); + } + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + } else { + rtlhal->fw_clk_change_in_progress = false; + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + break; + } + } + if (IS_IN_LOW_POWER_STATE_88E(rtlhal->fw_ps_state)) { + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_SET_RPWM, + &rpwm_val); + if (FW_PS_IS_ACK(rpwm_val)) { + isr_regaddr = REG_HISR; + content = rtl_read_dword(rtlpriv, isr_regaddr); + while (!(content & IMR_CPWM) && (count < 500)) { + udelay(50); + count++; + content = rtl_read_dword(rtlpriv, isr_regaddr); + } + + if (content & IMR_CPWM) { + rtl_write_word(rtlpriv, isr_regaddr, 0x0100); + rtlhal->fw_ps_state = FW_PS_STATE_RF_ON_88E; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "Receive CPWM INT!!! Set " + "pHalData->FwPSState = %X\n", + rtlhal->fw_ps_state); + } + } + spin_lock_bh(&rtlpriv->locks.fw_ps_lock); + rtlhal->fw_clk_change_in_progress = false; + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + if (schedule_timer) { + mod_timer(&rtlpriv->works.fw_clockoff_timer, + jiffies + MSECS(10)); + } + } else { + spin_lock_bh(&rtlpriv->locks.fw_ps_lock); + rtlhal->fw_clk_change_in_progress = false; + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + } +} + +static void _rtl8723be_set_fw_clock_off(struct ieee80211_hw *hw, u8 rpwm_val) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring; + enum rf_pwrstate rtstate; + bool schedule_timer = false; + u8 queue; + + if (!rtlhal->fw_ready) + return; + if (!rtlpriv->psc.fw_current_inpsmode) + return; + if (!rtlhal->allow_sw_to_change_hwclc) + return; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, (u8 *)(&rtstate)); + if (rtstate == ERFOFF || rtlpriv->psc.inactive_pwrstate == ERFOFF) + return; + + for (queue = 0; queue < RTL_PCI_MAX_TX_QUEUE_COUNT; queue++) { + ring = &rtlpci->tx_ring[queue]; + if (skb_queue_len(&ring->queue)) { + schedule_timer = true; + break; + } + } + if (schedule_timer) { + mod_timer(&rtlpriv->works.fw_clockoff_timer, + jiffies + MSECS(10)); + return; + } + if (FW_PS_STATE(rtlhal->fw_ps_state) != + FW_PS_STATE_RF_OFF_LOW_PWR_88E) { + spin_lock_bh(&rtlpriv->locks.fw_ps_lock); + if (!rtlhal->fw_clk_change_in_progress) { + rtlhal->fw_clk_change_in_progress = true; + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + rtlhal->fw_ps_state = FW_PS_STATE(rpwm_val); + rtl_write_word(rtlpriv, REG_HISR, 0x0100); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, + &rpwm_val); + spin_lock_bh(&rtlpriv->locks.fw_ps_lock); + rtlhal->fw_clk_change_in_progress = false; + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + } else { + spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + mod_timer(&rtlpriv->works.fw_clockoff_timer, + jiffies + MSECS(10)); + } + } +} + +static void _rtl8723be_set_fw_ps_rf_on(struct ieee80211_hw *hw) +{ + u8 rpwm_val = 0; + rpwm_val |= (FW_PS_STATE_RF_OFF_88E | FW_PS_ACK); + _rtl8723be_set_fw_clock_on(hw, rpwm_val, true); +} + +static void _rtl8723be_fwlps_leave(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + bool fw_current_inps = false; + u8 rpwm_val = 0, fw_pwrmode = FW_PS_ACTIVE_MODE; + + if (ppsc->low_power_enable) { + rpwm_val = (FW_PS_STATE_ALL_ON_88E | FW_PS_ACK);/* RF on */ + _rtl8723be_set_fw_clock_on(hw, rpwm_val, false); + rtlhal->allow_sw_to_change_hwclc = false; + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, + &fw_pwrmode); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_inps)); + } else { + rpwm_val = FW_PS_STATE_ALL_ON_88E; /* RF on */ + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, + &fw_pwrmode); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_inps)); + } +} + +static void _rtl8723be_fwlps_enter(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + bool fw_current_inps = true; + u8 rpwm_val; + + if (ppsc->low_power_enable) { + rpwm_val = FW_PS_STATE_RF_OFF_LOW_PWR_88E; /* RF off */ + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_inps)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, + &ppsc->fwctrl_psmode); + rtlhal->allow_sw_to_change_hwclc = true; + _rtl8723be_set_fw_clock_off(hw, rpwm_val); + + } else { + rpwm_val = FW_PS_STATE_RF_OFF_88E; /* RF off */ + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_inps)); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_PWRMODE, + &ppsc->fwctrl_psmode); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM, &rpwm_val); + } +} + +void rtl8723be_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + switch (variable) { + case HW_VAR_RCR: + *((u32 *)(val)) = rtlpci->receive_config; + break; + case HW_VAR_RF_STATE: + *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state; + break; + case HW_VAR_FWLPS_RF_ON: { + enum rf_pwrstate rfstate; + u32 val_rcr; + + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_RF_STATE, + (u8 *)(&rfstate)); + if (rfstate == ERFOFF) { + *((bool *)(val)) = true; + } else { + val_rcr = rtl_read_dword(rtlpriv, REG_RCR); + val_rcr &= 0x00070000; + if (val_rcr) + *((bool *)(val)) = false; + else + *((bool *)(val)) = true; + } + break; } + case HW_VAR_FW_PSMODE_STATUS: + *((bool *)(val)) = ppsc->fw_current_inpsmode; + break; + case HW_VAR_CORRECT_TSF: { + u64 tsf; + u32 *ptsf_low = (u32 *)&tsf; + u32 *ptsf_high = ((u32 *)&tsf) + 1; + + *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4)); + *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR); + + *((u64 *)(val)) = tsf; + + break; } + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process %x\n", variable); + break; + } +} + +void rtl8723be_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + u8 idx; + + switch (variable) { + case HW_VAR_ETHER_ADDR: + for (idx = 0; idx < ETH_ALEN; idx++) + rtl_write_byte(rtlpriv, (REG_MACID + idx), val[idx]); + break; + case HW_VAR_BASIC_RATE: { + u16 rate_cfg = ((u16 *)val)[0]; + u8 rate_index = 0; + rate_cfg = rate_cfg & 0x15f; + rate_cfg |= 0x01; + rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff); + rtl_write_byte(rtlpriv, REG_RRSR + 1, (rate_cfg >> 8) & 0xff); + while (rate_cfg > 0x1) { + rate_cfg = (rate_cfg >> 1); + rate_index++; + } + rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, rate_index); + break; } + case HW_VAR_BSSID: + for (idx = 0; idx < ETH_ALEN; idx++) + rtl_write_byte(rtlpriv, (REG_BSSID + idx), val[idx]); + break; + case HW_VAR_SIFS: + rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]); + rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]); + + rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]); + rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]); + + if (!mac->ht_enable) + rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, 0x0e0e); + else + rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, + *((u16 *)val)); + break; + case HW_VAR_SLOT_TIME: { + u8 e_aci; + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "HW_VAR_SLOT_TIME %x\n", val[0]); + + rtl_write_byte(rtlpriv, REG_SLOT, val[0]); + + for (e_aci = 0; e_aci < AC_MAX; e_aci++) { + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AC_PARAM, + &e_aci); + } + break; } + case HW_VAR_ACK_PREAMBLE: { + u8 reg_tmp; + u8 short_preamble = (bool)*val; + reg_tmp = rtl_read_byte(rtlpriv, REG_TRXPTCL_CTL + 2); + if (short_preamble) { + reg_tmp |= 0x02; + rtl_write_byte(rtlpriv, REG_TRXPTCL_CTL + 2, reg_tmp); + } else { + reg_tmp &= 0xFD; + rtl_write_byte(rtlpriv, REG_TRXPTCL_CTL + 2, reg_tmp); + } + break; } + case HW_VAR_WPA_CONFIG: + rtl_write_byte(rtlpriv, REG_SECCFG, *val); + break; + case HW_VAR_AMPDU_MIN_SPACE: { + u8 min_spacing_to_set; + u8 sec_min_space; + + min_spacing_to_set = *val; + if (min_spacing_to_set <= 7) { + sec_min_space = 0; + + if (min_spacing_to_set < sec_min_space) + min_spacing_to_set = sec_min_space; + + mac->min_space_cfg = ((mac->min_space_cfg & 0xf8) | + min_spacing_to_set); + + *val = min_spacing_to_set; + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n", + mac->min_space_cfg); + + rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, + mac->min_space_cfg); + } + break; } + case HW_VAR_SHORTGI_DENSITY: { + u8 density_to_set; + + density_to_set = *val; + mac->min_space_cfg |= (density_to_set << 3); + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "Set HW_VAR_SHORTGI_DENSITY: %#x\n", + mac->min_space_cfg); + + rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, + mac->min_space_cfg); + break; } + case HW_VAR_AMPDU_FACTOR: { + u8 regtoset_normal[4] = {0x41, 0xa8, 0x72, 0xb9}; + u8 factor_toset; + u8 *p_regtoset = NULL; + u8 index = 0; + + p_regtoset = regtoset_normal; + + factor_toset = *val; + if (factor_toset <= 3) { + factor_toset = (1 << (factor_toset + 2)); + if (factor_toset > 0xf) + factor_toset = 0xf; + + for (index = 0; index < 4; index++) { + if ((p_regtoset[index] & 0xf0) > + (factor_toset << 4)) + p_regtoset[index] = + (p_regtoset[index] & 0x0f) | + (factor_toset << 4); + + if ((p_regtoset[index] & 0x0f) > factor_toset) + p_regtoset[index] = + (p_regtoset[index] & 0xf0) | + (factor_toset); + + rtl_write_byte(rtlpriv, + (REG_AGGLEN_LMT + index), + p_regtoset[index]); + } + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "Set HW_VAR_AMPDU_FACTOR: %#x\n", + factor_toset); + } + break; } + case HW_VAR_AC_PARAM: { + u8 e_aci = *val; + rtl8723_dm_init_edca_turbo(hw); + + if (rtlpci->acm_method != EACMWAY2_SW) + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ACM_CTRL, + &e_aci); + break; } + case HW_VAR_ACM_CTRL: { + u8 e_aci = *val; + union aci_aifsn *p_aci_aifsn = + (union aci_aifsn *)(&(mac->ac[0].aifs)); + u8 acm = p_aci_aifsn->f.acm; + u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL); + + acm_ctrl = + acm_ctrl | ((rtlpci->acm_method == 2) ? 0x0 : 0x1); + + if (acm) { + switch (e_aci) { + case AC0_BE: + acm_ctrl |= ACMHW_BEQEN; + break; + case AC2_VI: + acm_ctrl |= ACMHW_VIQEN; + break; + case AC3_VO: + acm_ctrl |= ACMHW_VOQEN; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "HW_VAR_ACM_CTRL acm set " + "failed: eACI is %d\n", acm); + break; + } + } else { + switch (e_aci) { + case AC0_BE: + acm_ctrl &= (~ACMHW_BEQEN); + break; + case AC2_VI: + acm_ctrl &= (~ACMHW_VIQEN); + break; + case AC3_VO: + acm_ctrl &= (~ACMHW_BEQEN); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + } + RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE, + "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] " + "Write 0x%X\n", acm_ctrl); + rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl); + break; } + case HW_VAR_RCR: + rtl_write_dword(rtlpriv, REG_RCR, ((u32 *)(val))[0]); + rtlpci->receive_config = ((u32 *)(val))[0]; + break; + case HW_VAR_RETRY_LIMIT: { + u8 retry_limit = *val; + + rtl_write_word(rtlpriv, REG_RL, + retry_limit << RETRY_LIMIT_SHORT_SHIFT | + retry_limit << RETRY_LIMIT_LONG_SHIFT); + break; } + case HW_VAR_DUAL_TSF_RST: + rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1))); + break; + case HW_VAR_EFUSE_BYTES: + rtlefuse->efuse_usedbytes = *((u16 *)val); + break; + case HW_VAR_EFUSE_USAGE: + rtlefuse->efuse_usedpercentage = *val; + break; + case HW_VAR_IO_CMD: + rtl8723be_phy_set_io_cmd(hw, (*(enum io_type *)val)); + break; + case HW_VAR_SET_RPWM: { + u8 rpwm_val; + + rpwm_val = rtl_read_byte(rtlpriv, REG_PCIE_HRPWM); + udelay(1); + + if (rpwm_val & BIT(7)) { + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val); + } else { + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, *val | BIT(7)); + } + break; } + case HW_VAR_H2C_FW_PWRMODE: + rtl8723be_set_fw_pwrmode_cmd(hw, *val); + break; + case HW_VAR_FW_PSMODE_STATUS: + ppsc->fw_current_inpsmode = *((bool *)val); + break; + case HW_VAR_RESUME_CLK_ON: + _rtl8723be_set_fw_ps_rf_on(hw); + break; + case HW_VAR_FW_LPS_ACTION: { + bool enter_fwlps = *((bool *)val); + + if (enter_fwlps) + _rtl8723be_fwlps_enter(hw); + else + _rtl8723be_fwlps_leave(hw); + + break; } + case HW_VAR_H2C_FW_JOINBSSRPT: { + u8 mstatus = *val; + u8 tmp_regcr, tmp_reg422, bcnvalid_reg; + u8 count = 0, dlbcn_count = 0; + bool recover = false; + + if (mstatus == RT_MEDIA_CONNECT) { + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL); + + tmp_regcr = rtl_read_byte(rtlpriv, REG_CR + 1); + rtl_write_byte(rtlpriv, REG_CR + 1, + (tmp_regcr | BIT(0))); + + _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(3)); + _rtl8723be_set_bcn_ctrl_reg(hw, BIT(4), 0); + + tmp_reg422 = rtl_read_byte(rtlpriv, + REG_FWHW_TXQ_CTRL + 2); + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, + tmp_reg422 & (~BIT(6))); + if (tmp_reg422 & BIT(6)) + recover = true; + + do { + bcnvalid_reg = rtl_read_byte(rtlpriv, + REG_TDECTRL + 2); + rtl_write_byte(rtlpriv, REG_TDECTRL + 2, + (bcnvalid_reg | BIT(0))); + _rtl8723be_return_beacon_queue_skb(hw); + + rtl8723be_set_fw_rsvdpagepkt(hw, 0); + bcnvalid_reg = rtl_read_byte(rtlpriv, + REG_TDECTRL + 2); + count = 0; + while (!(bcnvalid_reg & BIT(0)) && count < 20) { + count++; + udelay(10); + bcnvalid_reg = rtl_read_byte(rtlpriv, + REG_TDECTRL + 2); + } + dlbcn_count++; + } while (!(bcnvalid_reg & BIT(0)) && dlbcn_count < 5); + + if (bcnvalid_reg & BIT(0)) + rtl_write_byte(rtlpriv, REG_TDECTRL+2, BIT(0)); + + _rtl8723be_set_bcn_ctrl_reg(hw, BIT(3), 0); + _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(4)); + + if (recover) { + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, + tmp_reg422); + } + rtl_write_byte(rtlpriv, REG_CR + 1, + (tmp_regcr & ~(BIT(0)))); + } + rtl8723be_set_fw_joinbss_report_cmd(hw, *val); + break; } + case HW_VAR_H2C_FW_P2P_PS_OFFLOAD: + rtl8723be_set_p2p_ps_offload_cmd(hw, *val); + break; + case HW_VAR_AID: { + u16 u2btmp; + u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT); + u2btmp &= 0xC000; + rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, + (u2btmp | mac->assoc_id)); + break; } + case HW_VAR_CORRECT_TSF: { + u8 btype_ibss = *val; + + if (btype_ibss) + _rtl8723be_stop_tx_beacon(hw); + + _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(3)); + + rtl_write_dword(rtlpriv, REG_TSFTR, + (u32) (mac->tsf & 0xffffffff)); + rtl_write_dword(rtlpriv, REG_TSFTR + 4, + (u32) ((mac->tsf >> 32) & 0xffffffff)); + + _rtl8723be_set_bcn_ctrl_reg(hw, BIT(3), 0); + + if (btype_ibss) + _rtl8723be_resume_tx_beacon(hw); + break; } + case HW_VAR_KEEP_ALIVE: { + u8 array[2]; + array[0] = 0xff; + array[1] = *val; + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_KEEP_ALIVE_CTRL, + 2, array); + break; } + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process %x\n", + variable); + break; + } +} + +static bool _rtl8723be_llt_write(struct ieee80211_hw *hw, u32 address, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + bool status = true; + int count = 0; + u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) | + _LLT_OP(_LLT_WRITE_ACCESS); + + rtl_write_dword(rtlpriv, REG_LLT_INIT, value); + + do { + value = rtl_read_dword(rtlpriv, REG_LLT_INIT); + if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value)) + break; + + if (count > POLLING_LLT_THRESHOLD) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Failed to polling write LLT done at " + "address %d!\n", address); + status = false; + break; + } + } while (++count); + + return status; +} + +static bool _rtl8723be_llt_table_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + unsigned short i; + u8 txpktbuf_bndy; + u8 maxpage; + bool status; + + maxpage = 255; + txpktbuf_bndy = 245; + + rtl_write_dword(rtlpriv, REG_TRXFF_BNDY, + (0x27FF0000 | txpktbuf_bndy)); + rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy); + + rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy); + rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy); + + rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy); + rtl_write_byte(rtlpriv, REG_PBP, 0x31); + rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4); + + for (i = 0; i < (txpktbuf_bndy - 1); i++) { + status = _rtl8723be_llt_write(hw, i, i + 1); + if (!status) + return status; + } + status = _rtl8723be_llt_write(hw, (txpktbuf_bndy - 1), 0xFF); + + if (!status) + return status; + + for (i = txpktbuf_bndy; i < maxpage; i++) { + status = _rtl8723be_llt_write(hw, i, (i + 1)); + if (!status) + return status; + } + status = _rtl8723be_llt_write(hw, maxpage, txpktbuf_bndy); + if (!status) + return status; + + rtl_write_dword(rtlpriv, REG_RQPN, 0x80e40808); + rtl_write_byte(rtlpriv, REG_RQPN_NPQ, 0x00); + + return true; +} + +static void _rtl8723be_gen_refresh_led_state(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_led *pled0 = &(pcipriv->ledctl.sw_led0); + + if (rtlpriv->rtlhal.up_first_time) + return; + + if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) + rtl8723be_sw_led_on(hw, pled0); + else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT) + rtl8723be_sw_led_on(hw, pled0); + else + rtl8723be_sw_led_off(hw, pled0); +} + +static bool _rtl8723be_init_mac(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + unsigned char bytetmp; + unsigned short wordtmp; + u16 retry = 0; + bool mac_func_enable; + + rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00); + + /*Auto Power Down to CHIP-off State*/ + bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1) & (~BIT(7)); + rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, bytetmp); + + bytetmp = rtl_read_byte(rtlpriv, REG_CR); + if (bytetmp == 0xFF) + mac_func_enable = true; + else + mac_func_enable = false; + + /* HW Power on sequence */ + if (!rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, + PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, + RTL8723_NIC_ENABLE_FLOW)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "init MAC Fail as power on failure\n"); + return false; + } + bytetmp = rtl_read_byte(rtlpriv, REG_APS_FSMCO) | BIT(4); + rtl_write_byte(rtlpriv, REG_APS_FSMCO, bytetmp); + + bytetmp = rtl_read_byte(rtlpriv, REG_CR); + bytetmp = 0xff; + rtl_write_byte(rtlpriv, REG_CR, bytetmp); + mdelay(2); + + bytetmp = rtl_read_byte(rtlpriv, REG_HWSEQ_CTRL); + bytetmp |= 0x7f; + rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, bytetmp); + mdelay(2); + + bytetmp = rtl_read_byte(rtlpriv, REG_SYS_CFG + 3); + if (bytetmp & BIT(0)) { + bytetmp = rtl_read_byte(rtlpriv, 0x7c); + bytetmp |= BIT(6); + rtl_write_byte(rtlpriv, 0x7c, bytetmp); + } + bytetmp = rtl_read_byte(rtlpriv, REG_SYS_CLKR); + bytetmp |= BIT(3); + rtl_write_byte(rtlpriv, REG_SYS_CLKR, bytetmp); + bytetmp = rtl_read_byte(rtlpriv, REG_GPIO_MUXCFG + 1); + bytetmp &= ~BIT(4); + rtl_write_byte(rtlpriv, REG_GPIO_MUXCFG + 1, bytetmp); + + bytetmp = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG+3); + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+3, bytetmp | 0x77); + + rtl_write_word(rtlpriv, REG_CR, 0x2ff); + + if (!mac_func_enable) { + if (!_rtl8723be_llt_table_init(hw)) + return false; + } + rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff); + rtl_write_dword(rtlpriv, REG_HISRE, 0xffffffff); + + /* Enable FW Beamformer Interrupt */ + bytetmp = rtl_read_byte(rtlpriv, REG_FWIMR + 3); + rtl_write_byte(rtlpriv, REG_FWIMR + 3, bytetmp | BIT(6)); + + wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL); + wordtmp &= 0xf; + wordtmp |= 0xF5B1; + rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp); + + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 1, 0x1F); + rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); + rtl_write_word(rtlpriv, REG_RXFLTMAP2, 0xFFFF); + rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config); + + rtl_write_byte(rtlpriv, 0x4d0, 0x0); + + rtl_write_dword(rtlpriv, REG_BCNQ_DESA, + ((u64) rtlpci->tx_ring[BEACON_QUEUE].dma) & + DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_MGQ_DESA, + (u64) rtlpci->tx_ring[MGNT_QUEUE].dma & + DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_VOQ_DESA, + (u64) rtlpci->tx_ring[VO_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_VIQ_DESA, + (u64) rtlpci->tx_ring[VI_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_BEQ_DESA, + (u64) rtlpci->tx_ring[BE_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_BKQ_DESA, + (u64) rtlpci->tx_ring[BK_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_HQ_DESA, + (u64) rtlpci->tx_ring[HIGH_QUEUE].dma & + DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_RX_DESA, + (u64) rtlpci->rx_ring[RX_MPDU_QUEUE].dma & + DMA_BIT_MASK(32)); + + bytetmp = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG + 3); + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, bytetmp | 0x77); + + rtl_write_dword(rtlpriv, REG_INT_MIG, 0); + + bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); + rtl_write_byte(rtlpriv, REG_APSD_CTRL, bytetmp & ~BIT(6)); + + rtl_write_byte(rtlpriv, REG_SECONDARY_CCA_CTRL, 0x3); + + do { + retry++; + bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); + } while ((retry < 200) && (bytetmp & BIT(7))); + + _rtl8723be_gen_refresh_led_state(hw); + + rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0); + + bytetmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL); + rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, bytetmp & ~BIT(2)); + + return true; +} + +static void _rtl8723be_hw_configure(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 reg_bw_opmode; + u32 reg_ratr, reg_prsr; + + reg_bw_opmode = BW_OPMODE_20MHZ; + reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG | + RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS; + reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; + + rtl_write_dword(rtlpriv, REG_RRSR, reg_prsr); + rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF); +} + +static void _rtl8723be_enable_aspm_back_door(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + rtl_write_byte(rtlpriv, 0x34b, 0x93); + rtl_write_word(rtlpriv, 0x350, 0x870c); + rtl_write_byte(rtlpriv, 0x352, 0x1); + + if (ppsc->support_backdoor) + rtl_write_byte(rtlpriv, 0x349, 0x1b); + else + rtl_write_byte(rtlpriv, 0x349, 0x03); + + rtl_write_word(rtlpriv, 0x350, 0x2718); + rtl_write_byte(rtlpriv, 0x352, 0x1); +} + +void rtl8723be_enable_hw_security_config(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 sec_reg_value; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n", + rtlpriv->sec.pairwise_enc_algorithm, + rtlpriv->sec.group_enc_algorithm); + + if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "not open hw encryption\n"); + return; + } + sec_reg_value = SCR_TXENCENABLE | SCR_RXDECENABLE; + + if (rtlpriv->sec.use_defaultkey) { + sec_reg_value |= SCR_TXUSEDK; + sec_reg_value |= SCR_RXUSEDK; + } + sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK); + + rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); + + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "The SECR-value %x\n", + sec_reg_value); + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); +} + +int rtl8723be_hw_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + bool rtstatus = true; + int err; + u8 tmp_u1b; + unsigned long flags; + + /* reenable interrupts to not interfere with other devices */ + local_save_flags(flags); + local_irq_enable(); + + rtlpriv->rtlhal.being_init_adapter = true; + rtlpriv->intf_ops->disable_aspm(hw); + rtstatus = _rtl8723be_init_mac(hw); + if (!rtstatus) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); + err = 1; + goto exit; + } + tmp_u1b = rtl_read_byte(rtlpriv, REG_SYS_CFG); + tmp_u1b &= 0x7F; + rtl_write_byte(rtlpriv, REG_SYS_CFG, tmp_u1b); + + err = rtl8723_download_fw(hw, true); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Failed to download FW. Init HW without FW now..\n"); + err = 1; + rtlhal->fw_ready = false; + goto exit; + } else { + rtlhal->fw_ready = true; + } + rtlhal->last_hmeboxnum = 0; + rtl8723be_phy_mac_config(hw); + /* because last function modify RCR, so we update + * rcr var here, or TP will unstable for receive_config + * is wrong, RX RCR_ACRC32 will cause TP unstabel & Rx + * RCR_APP_ICV will cause mac80211 unassoc for cisco 1252 + */ + rtlpci->receive_config = rtl_read_dword(rtlpriv, REG_RCR); + rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV); + rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); + + rtl8723be_phy_bb_config(hw); + rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; + rtl8723be_phy_rf_config(hw); + + rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, + RF_CHNLBW, RFREG_OFFSET_MASK); + rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, + RF_CHNLBW, RFREG_OFFSET_MASK); + rtlphy->rfreg_chnlval[0] &= 0xFFF03FF; + rtlphy->rfreg_chnlval[0] |= (BIT(10) | BIT(11)); + + rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); + rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); + rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); + _rtl8723be_hw_configure(hw); + rtl_cam_reset_all_entry(hw); + rtl8723be_enable_hw_security_config(hw); + + ppsc->rfpwr_state = ERFON; + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); + _rtl8723be_enable_aspm_back_door(hw); + rtlpriv->intf_ops->enable_aspm(hw); + + rtl8723be_bt_hw_init(hw); + + rtl_set_bbreg(hw, 0x64, BIT(20), 0); + rtl_set_bbreg(hw, 0x64, BIT(24), 0); + + rtl_set_bbreg(hw, 0x40, BIT(4), 0); + rtl_set_bbreg(hw, 0x40, BIT(3), 1); + + rtl_set_bbreg(hw, 0x944, BIT(0)|BIT(1), 0x3); + rtl_set_bbreg(hw, 0x930, 0xff, 0x77); + + rtl_set_bbreg(hw, 0x38, BIT(11), 0x1); + + rtl_set_bbreg(hw, 0xb2c, 0xffffffff, 0x80000000); + + if (ppsc->rfpwr_state == ERFON) { + rtl8723be_dm_check_txpower_tracking(hw); + rtl8723be_phy_lc_calibrate(hw); + } + tmp_u1b = efuse_read_1byte(hw, 0x1FA); + if (!(tmp_u1b & BIT(0))) { + rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0F, 0x05); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "PA BIAS path A\n"); + } + if (!(tmp_u1b & BIT(4))) { + tmp_u1b = rtl_read_byte(rtlpriv, 0x16); + tmp_u1b &= 0x0F; + rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x80); + udelay(10); + rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x90); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n"); + } + rtl8723be_dm_init(hw); +exit: + local_irq_restore(flags); + rtlpriv->rtlhal.being_init_adapter = false; + return err; +} + +static enum version_8723e _rtl8723be_read_chip_version(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + enum version_8723e version = VERSION_UNKNOWN; + u8 count = 0; + u8 value8; + u32 value32; + + rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0); + + value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 2); + rtl_write_byte(rtlpriv, REG_APS_FSMCO + 2, value8 | BIT(0)); + + value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); + rtl_write_byte(rtlpriv, REG_APS_FSMCO + 1, value8 | BIT(0)); + + value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); + while (((value8 & BIT(0))) && (count++ < 100)) { + udelay(10); + value8 = rtl_read_byte(rtlpriv, REG_APS_FSMCO + 1); + } + count = 0; + value8 = rtl_read_byte(rtlpriv, REG_ROM_VERSION); + while ((value8 == 0) && (count++ < 50)) { + value8 = rtl_read_byte(rtlpriv, REG_ROM_VERSION); + mdelay(1); + } + value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG1); + if ((value32 & (CHIP_8723B)) != CHIP_8723B) + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "unkown chip version\n"); + else + version = (enum version_8723e) VERSION_TEST_CHIP_1T1R_8723B; + + rtlphy->rf_type = RF_1T1R; + + value8 = rtl_read_byte(rtlpriv, REG_ROM_VERSION); + if (value8 >= 0x02) + version |= BIT(3); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Chip RF Type: %s\n", (rtlphy->rf_type == RF_2T2R) ? + "RF_2T2R" : "RF_1T1R"); + + return version; +} + +static int _rtl8723be_set_media_status(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 bt_msr = rtl_read_byte(rtlpriv, MSR) & 0xfc; + enum led_ctl_mode ledaction = LED_CTL_NO_LINK; + + rtl_write_dword(rtlpriv, REG_BCN_CTRL, 0); + RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD, + "clear 0x550 when set HW_VAR_MEDIA_STATUS\n"); + + if (type == NL80211_IFTYPE_UNSPECIFIED || + type == NL80211_IFTYPE_STATION) { + _rtl8723be_stop_tx_beacon(hw); + _rtl8723be_enable_bcn_sub_func(hw); + } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP) { + _rtl8723be_resume_tx_beacon(hw); + _rtl8723be_disable_bcn_sub_func(hw); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Set HW_VAR_MEDIA_STATUS: " + "No such media status(%x).\n", type); + } + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + bt_msr |= MSR_NOLINK; + ledaction = LED_CTL_LINK; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to NO LINK!\n"); + break; + case NL80211_IFTYPE_ADHOC: + bt_msr |= MSR_ADHOC; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to Ad Hoc!\n"); + break; + case NL80211_IFTYPE_STATION: + bt_msr |= MSR_INFRA; + ledaction = LED_CTL_LINK; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to STA!\n"); + break; + case NL80211_IFTYPE_AP: + bt_msr |= MSR_AP; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to AP!\n"); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Network type %d not support!\n", type); + return 1; + } + rtl_write_byte(rtlpriv, (MSR), bt_msr); + rtlpriv->cfg->ops->led_control(hw, ledaction); + if ((bt_msr & 0x03) == MSR_AP) + rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); + else + rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); + return 0; +} + +void rtl8723be_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + u32 reg_rcr = rtlpci->receive_config; + + if (rtlpriv->psc.rfpwr_state != ERFON) + return; + + if (check_bssid) { + reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, + (u8 *)(®_rcr)); + _rtl8723be_set_bcn_ctrl_reg(hw, 0, BIT(4)); + } else if (!check_bssid) { + reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); + _rtl8723be_set_bcn_ctrl_reg(hw, BIT(4), 0); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, + (u8 *)(®_rcr)); + } +} + +int rtl8723be_set_network_type(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (_rtl8723be_set_media_status(hw, type)) + return -EOPNOTSUPP; + + if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { + if (type != NL80211_IFTYPE_AP) + rtl8723be_set_check_bssid(hw, true); + } else { + rtl8723be_set_check_bssid(hw, false); + } + return 0; +} + +/* don't set REG_EDCA_BE_PARAM here + * because mac80211 will send pkt when scan + */ +void rtl8723be_set_qos(struct ieee80211_hw *hw, int aci) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + rtl8723_dm_init_edca_turbo(hw); + switch (aci) { + case AC1_BK: + rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); + break; + case AC0_BE: + break; + case AC2_VI: + rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322); + break; + case AC3_VO: + rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222); + break; + default: + RT_ASSERT(false, "invalid aci: %d !\n", aci); + break; + } +} + +void rtl8723be_enable_interrupt(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + rtl_write_dword(rtlpriv, REG_HIMR, rtlpci->irq_mask[0] & 0xFFFFFFFF); + rtl_write_dword(rtlpriv, REG_HIMRE, rtlpci->irq_mask[1] & 0xFFFFFFFF); + rtlpci->irq_enabled = true; + /* there are some C2H CMDs have been sent + * before system interrupt is enabled, e.g., C2H, CPWM. + * So we need to clear all C2H events that FW has notified, + * otherwise FW won't schedule any commands anymore. + */ + rtl_write_byte(rtlpriv, REG_C2HEVT_CLEAR, 0); + /*enable system interrupt*/ + rtl_write_dword(rtlpriv, REG_HSIMR, rtlpci->sys_irq_mask & 0xFFFFFFFF); +} + +void rtl8723be_disable_interrupt(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + rtl_write_dword(rtlpriv, REG_HIMR, IMR_DISABLED); + rtl_write_dword(rtlpriv, REG_HIMRE, IMR_DISABLED); + rtlpci->irq_enabled = false; + synchronize_irq(rtlpci->pdev->irq); +} + +static void _rtl8723be_poweroff_adapter(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 u1b_tmp; + + /* Combo (PCIe + USB) Card and PCIe-MF Card */ + /* 1. Run LPS WL RFOFF flow */ + rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, RTL8723_NIC_LPS_ENTER_FLOW); + + /* 2. 0x1F[7:0] = 0 */ + /* turn off RF */ + rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00); + if ((rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) && + rtlhal->fw_ready) + rtl8723be_firmware_selfreset(hw); + + /* Reset MCU. Suggested by Filen. */ + u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2)))); + + /* g. MCUFWDL 0x80[1:0]= 0 */ + /* reset MCU ready status */ + rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); + + /* HW card disable configuration. */ + rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, RTL8723_NIC_DISABLE_FLOW); + + /* Reset MCU IO Wrapper */ + u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); + rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp & (~BIT(0)))); + u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); + rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, u1b_tmp | BIT(0)); + + /* 7. RSV_CTRL 0x1C[7:0] = 0x0E */ + /* lock ISO/CLK/Power control register */ + rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0e); +} + +void rtl8723be_card_disable(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + enum nl80211_iftype opmode; + + mac->link_state = MAC80211_NOLINK; + opmode = NL80211_IFTYPE_UNSPECIFIED; + _rtl8723be_set_media_status(hw, opmode); + if (rtlpriv->rtlhal.driver_is_goingto_unload || + ppsc->rfoff_reason > RF_CHANGE_BY_PS) + rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); + RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); + _rtl8723be_poweroff_adapter(hw); + + /* after power off we should do iqk again */ + rtlpriv->phy.iqk_initialized = false; +} + +void rtl8723be_interrupt_recognized(struct ieee80211_hw *hw, + u32 *p_inta, u32 *p_intb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + *p_inta = rtl_read_dword(rtlpriv, ISR) & rtlpci->irq_mask[0]; + rtl_write_dword(rtlpriv, ISR, *p_inta); + + *p_intb = rtl_read_dword(rtlpriv, REG_HISRE) & + rtlpci->irq_mask[1]; + rtl_write_dword(rtlpriv, REG_HISRE, *p_intb); +} + +void rtl8723be_set_beacon_related_registers(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u16 bcn_interval, atim_window; + + bcn_interval = mac->beacon_interval; + atim_window = 2; /*FIX MERGE */ + rtl8723be_disable_interrupt(hw); + rtl_write_word(rtlpriv, REG_ATIMWND, atim_window); + rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); + rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f); + rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x18); + rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x18); + rtl_write_byte(rtlpriv, 0x606, 0x30); + rtl8723be_enable_interrupt(hw); +} + +void rtl8723be_set_beacon_interval(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u16 bcn_interval = mac->beacon_interval; + + RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG, + "beacon_interval:%d\n", bcn_interval); + rtl8723be_disable_interrupt(hw); + rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); + rtl8723be_enable_interrupt(hw); +} + +void rtl8723be_update_interrupt_mask(struct ieee80211_hw *hw, + u32 add_msr, u32 rm_msr) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, + "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr); + + if (add_msr) + rtlpci->irq_mask[0] |= add_msr; + if (rm_msr) + rtlpci->irq_mask[0] &= (~rm_msr); + rtl8723be_disable_interrupt(hw); + rtl8723be_enable_interrupt(hw); +} + +static u8 _rtl8723be_get_chnl_group(u8 chnl) +{ + u8 group; + + if (chnl < 3) + group = 0; + else if (chnl < 9) + group = 1; + else + group = 2; + return group; +} + +static void _rtl8723be_read_power_value_fromprom(struct ieee80211_hw *hw, + struct txpower_info_2g *pw2g, + struct txpower_info_5g *pw5g, + bool autoload_fail, u8 *hwinfo) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 path, addr = EEPROM_TX_PWR_INX, group, cnt = 0; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "hal_ReadPowerValueFromPROM8723BE(): " + "PROMContent[0x%x]= 0x%x\n", + (addr + 1), hwinfo[addr + 1]); + if (0xFF == hwinfo[addr + 1]) /*YJ, add, 120316*/ + autoload_fail = true; + + if (autoload_fail) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "auto load fail : Use Default value!\n"); + for (path = 0; path < MAX_RF_PATH; path++) { + /* 2.4G default value */ + for (group = 0; group < MAX_CHNL_GROUP_24G; group++) { + pw2g->index_cck_base[path][group] = 0x2D; + pw2g->index_bw40_base[path][group] = 0x2D; + } + for (cnt = 0; cnt < MAX_TX_COUNT; cnt++) { + if (cnt == 0) { + pw2g->bw20_diff[path][0] = 0x02; + pw2g->ofdm_diff[path][0] = 0x04; + } else { + pw2g->bw20_diff[path][cnt] = 0xFE; + pw2g->bw40_diff[path][cnt] = 0xFE; + pw2g->cck_diff[path][cnt] = 0xFE; + pw2g->ofdm_diff[path][cnt] = 0xFE; + } + } + } + return; + } + for (path = 0; path < MAX_RF_PATH; path++) { + /*2.4G default value*/ + for (group = 0; group < MAX_CHNL_GROUP_24G; group++) { + pw2g->index_cck_base[path][group] = hwinfo[addr++]; + if (pw2g->index_cck_base[path][group] == 0xFF) + pw2g->index_cck_base[path][group] = 0x2D; + } + for (group = 0; group < MAX_CHNL_GROUP_24G - 1; group++) { + pw2g->index_bw40_base[path][group] = hwinfo[addr++]; + if (pw2g->index_bw40_base[path][group] == 0xFF) + pw2g->index_bw40_base[path][group] = 0x2D; + } + for (cnt = 0; cnt < MAX_TX_COUNT; cnt++) { + if (cnt == 0) { + pw2g->bw40_diff[path][cnt] = 0; + if (hwinfo[addr] == 0xFF) { + pw2g->bw20_diff[path][cnt] = 0x02; + } else { + pw2g->bw20_diff[path][cnt] = + (hwinfo[addr] & 0xf0) >> 4; + /*bit sign number to 8 bit sign number*/ + if (pw2g->bw20_diff[path][cnt] & BIT(3)) + pw2g->bw20_diff[path][cnt] |= 0xF0; + } + if (hwinfo[addr] == 0xFF) { + pw2g->ofdm_diff[path][cnt] = 0x04; + } else { + pw2g->ofdm_diff[path][cnt] = + (hwinfo[addr] & 0x0f); + /*bit sign number to 8 bit sign number*/ + if (pw2g->ofdm_diff[path][cnt] & BIT(3)) + pw2g->ofdm_diff[path][cnt] |= + 0xF0; + } + pw2g->cck_diff[path][cnt] = 0; + addr++; + } else { + if (hwinfo[addr] == 0xFF) { + pw2g->bw40_diff[path][cnt] = 0xFE; + } else { + pw2g->bw40_diff[path][cnt] = + (hwinfo[addr] & 0xf0) >> 4; + if (pw2g->bw40_diff[path][cnt] & BIT(3)) + pw2g->bw40_diff[path][cnt] |= + 0xF0; + } + if (hwinfo[addr] == 0xFF) { + pw2g->bw20_diff[path][cnt] = 0xFE; + } else { + pw2g->bw20_diff[path][cnt] = + (hwinfo[addr] & 0x0f); + if (pw2g->bw20_diff[path][cnt] & BIT(3)) + pw2g->bw20_diff[path][cnt] |= + 0xF0; + } + addr++; + + if (hwinfo[addr] == 0xFF) { + pw2g->ofdm_diff[path][cnt] = 0xFE; + } else { + pw2g->ofdm_diff[path][cnt] = + (hwinfo[addr] & 0xf0) >> 4; + if (pw2g->ofdm_diff[path][cnt] & BIT(3)) + pw2g->ofdm_diff[path][cnt] |= + 0xF0; + } + if (hwinfo[addr] == 0xFF) { + pw2g->cck_diff[path][cnt] = 0xFE; + } else { + pw2g->cck_diff[path][cnt] = + (hwinfo[addr] & 0x0f); + if (pw2g->cck_diff[path][cnt] & BIT(3)) + pw2g->cck_diff[path][cnt] |= + 0xF0; + } + addr++; + } + } + /*5G default value*/ + for (group = 0; group < MAX_CHNL_GROUP_5G; group++) { + pw5g->index_bw40_base[path][group] = hwinfo[addr++]; + if (pw5g->index_bw40_base[path][group] == 0xFF) + pw5g->index_bw40_base[path][group] = 0xFE; + } + for (cnt = 0; cnt < MAX_TX_COUNT; cnt++) { + if (cnt == 0) { + pw5g->bw40_diff[path][cnt] = 0; + + if (hwinfo[addr] == 0xFF) { + pw5g->bw20_diff[path][cnt] = 0; + } else { + pw5g->bw20_diff[path][0] = + (hwinfo[addr] & 0xf0) >> 4; + if (pw5g->bw20_diff[path][cnt] & BIT(3)) + pw5g->bw20_diff[path][cnt] |= + 0xF0; + } + if (hwinfo[addr] == 0xFF) { + pw5g->ofdm_diff[path][cnt] = 0x04; + } else { + pw5g->ofdm_diff[path][0] = + (hwinfo[addr] & 0x0f); + if (pw5g->ofdm_diff[path][cnt] & BIT(3)) + pw5g->ofdm_diff[path][cnt] |= + 0xF0; + } + addr++; + } else { + if (hwinfo[addr] == 0xFF) { + pw5g->bw40_diff[path][cnt] = 0xFE; + } else { + pw5g->bw40_diff[path][cnt] = + (hwinfo[addr] & 0xf0) >> 4; + if (pw5g->bw40_diff[path][cnt] & BIT(3)) + pw5g->bw40_diff[path][cnt] |= 0xF0; + } + if (hwinfo[addr] == 0xFF) { + pw5g->bw20_diff[path][cnt] = 0xFE; + } else { + pw5g->bw20_diff[path][cnt] = + (hwinfo[addr] & 0x0f); + if (pw5g->bw20_diff[path][cnt] & BIT(3)) + pw5g->bw20_diff[path][cnt] |= 0xF0; + } + addr++; + } + } + if (hwinfo[addr] == 0xFF) { + pw5g->ofdm_diff[path][1] = 0xFE; + pw5g->ofdm_diff[path][2] = 0xFE; + } else { + pw5g->ofdm_diff[path][1] = (hwinfo[addr] & 0xf0) >> 4; + pw5g->ofdm_diff[path][2] = (hwinfo[addr] & 0x0f); + } + addr++; + + if (hwinfo[addr] == 0xFF) + pw5g->ofdm_diff[path][3] = 0xFE; + else + pw5g->ofdm_diff[path][3] = (hwinfo[addr] & 0x0f); + addr++; + + for (cnt = 1; cnt < MAX_TX_COUNT; cnt++) { + if (pw5g->ofdm_diff[path][cnt] == 0xFF) + pw5g->ofdm_diff[path][cnt] = 0xFE; + else if (pw5g->ofdm_diff[path][cnt] & BIT(3)) + pw5g->ofdm_diff[path][cnt] |= 0xF0; + } + } +} + +static void _rtl8723be_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, + bool autoload_fail, + u8 *hwinfo) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct txpower_info_2g pw2g; + struct txpower_info_5g pw5g; + u8 rf_path, index; + u8 i; + + _rtl8723be_read_power_value_fromprom(hw, &pw2g, &pw5g, autoload_fail, + hwinfo); + + for (rf_path = 0; rf_path < 2; rf_path++) { + for (i = 0; i < 14; i++) { + index = _rtl8723be_get_chnl_group(i+1); + + rtlefuse->txpwrlevel_cck[rf_path][i] = + pw2g.index_cck_base[rf_path][index]; + rtlefuse->txpwrlevel_ht40_1s[rf_path][i] = + pw2g.index_bw40_base[rf_path][index]; + } + for (i = 0; i < MAX_TX_COUNT; i++) { + rtlefuse->txpwr_ht20diff[rf_path][i] = + pw2g.bw20_diff[rf_path][i]; + rtlefuse->txpwr_ht40diff[rf_path][i] = + pw2g.bw40_diff[rf_path][i]; + rtlefuse->txpwr_legacyhtdiff[rf_path][i] = + pw2g.ofdm_diff[rf_path][i]; + } + for (i = 0; i < 14; i++) { + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "RF(%d)-Ch(%d) [CCK / HT40_1S ] = " + "[0x%x / 0x%x ]\n", rf_path, i, + rtlefuse->txpwrlevel_cck[rf_path][i], + rtlefuse->txpwrlevel_ht40_1s[rf_path][i]); + } + } + if (!autoload_fail) + rtlefuse->eeprom_thermalmeter = + hwinfo[EEPROM_THERMAL_METER_88E]; + else + rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER; + + if (rtlefuse->eeprom_thermalmeter == 0xff || autoload_fail) { + rtlefuse->apk_thermalmeterignore = true; + rtlefuse->eeprom_thermalmeter = EEPROM_DEFAULT_THERMALMETER; + } + rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter; + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); + + if (!autoload_fail) { + rtlefuse->eeprom_regulatory = + hwinfo[EEPROM_RF_BOARD_OPTION_88E] & 0x07;/*bit0~2*/ + if (hwinfo[EEPROM_RF_BOARD_OPTION_88E] == 0xFF) + rtlefuse->eeprom_regulatory = 0; + } else { + rtlefuse->eeprom_regulatory = 0; + } + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory); +} + +static void _rtl8723be_read_adapter_info(struct ieee80211_hw *hw, + bool pseudo_test) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u16 i, usvalue; + u8 hwinfo[HWSET_MAX_SIZE]; + u16 eeprom_id; + bool is_toshiba_smid1 = false; + bool is_toshiba_smid2 = false; + bool is_samsung_smid = false; + bool is_lenovo_smid = false; + u16 toshiba_smid1[] = { + 0x6151, 0x6152, 0x6154, 0x6155, 0x6177, 0x6178, 0x6179, 0x6180, + 0x7151, 0x7152, 0x7154, 0x7155, 0x7177, 0x7178, 0x7179, 0x7180, + 0x8151, 0x8152, 0x8154, 0x8155, 0x8181, 0x8182, 0x8184, 0x8185, + 0x9151, 0x9152, 0x9154, 0x9155, 0x9181, 0x9182, 0x9184, 0x9185 + }; + u16 toshiba_smid2[] = { + 0x6181, 0x6184, 0x6185, 0x7181, 0x7182, 0x7184, 0x7185, 0x8181, + 0x8182, 0x8184, 0x8185, 0x9181, 0x9182, 0x9184, 0x9185 + }; + u16 samsung_smid[] = { + 0x6191, 0x6192, 0x6193, 0x7191, 0x7192, 0x7193, 0x8191, 0x8192, + 0x8193, 0x9191, 0x9192, 0x9193 + }; + u16 lenovo_smid[] = { + 0x8195, 0x9195, 0x7194, 0x8200, 0x8201, 0x8202, 0x9199, 0x9200 + }; + + if (pseudo_test) { + /* needs to be added */ + return; + } + if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { + rtl_efuse_shadow_map_update(hw); + + memcpy(hwinfo, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], + HWSET_MAX_SIZE); + } else if (rtlefuse->epromtype == EEPROM_93C46) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "RTL819X Not boot from eeprom, check it !!"); + } + RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, ("MAP\n"), + hwinfo, HWSET_MAX_SIZE); + + eeprom_id = *((u16 *)&hwinfo[0]); + if (eeprom_id != RTL8723BE_EEPROM_ID) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "EEPROM ID(%#x) is invalid!!\n", eeprom_id); + rtlefuse->autoload_failflag = true; + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); + rtlefuse->autoload_failflag = false; + } + if (rtlefuse->autoload_failflag) + return; + + rtlefuse->eeprom_vid = *(u16 *)&hwinfo[EEPROM_VID]; + rtlefuse->eeprom_did = *(u16 *)&hwinfo[EEPROM_DID]; + rtlefuse->eeprom_svid = *(u16 *)&hwinfo[EEPROM_SVID]; + rtlefuse->eeprom_smid = *(u16 *)&hwinfo[EEPROM_SMID]; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROMId = 0x%4x\n", eeprom_id); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid); + + for (i = 0; i < 6; i += 2) { + usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i]; + *((u16 *)(&rtlefuse->dev_addr[i])) = usvalue; + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "dev_addr: %pM\n", + rtlefuse->dev_addr); + + /*parse xtal*/ + rtlefuse->crystalcap = hwinfo[EEPROM_XTAL_8723BE]; + if (rtlefuse->crystalcap == 0xFF) + rtlefuse->crystalcap = 0x20; + + _rtl8723be_read_txpower_info_from_hwpg(hw, rtlefuse->autoload_failflag, + hwinfo); + + rtl8723be_read_bt_coexist_info_from_hwpg(hw, + rtlefuse->autoload_failflag, + hwinfo); + + rtlefuse->eeprom_channelplan = hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; + rtlefuse->txpwr_fromeprom = true; + rtlefuse->eeprom_oemid = hwinfo[EEPROM_CUSTOMER_ID]; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); + + /* set channel plan to world wide 13 */ + rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13; + + if (rtlhal->oem_id == RT_CID_DEFAULT) { + /* Does this one have a Toshiba SMID from group 1? */ + for (i = 0; i < sizeof(toshiba_smid1) / sizeof(u16); i++) { + if (rtlefuse->eeprom_smid == toshiba_smid1[i]) { + is_toshiba_smid1 = true; + break; + } + } + /* Does this one have a Toshiba SMID from group 2? */ + for (i = 0; i < sizeof(toshiba_smid2) / sizeof(u16); i++) { + if (rtlefuse->eeprom_smid == toshiba_smid2[i]) { + is_toshiba_smid2 = true; + break; + } + } + /* Does this one have a Samsung SMID? */ + for (i = 0; i < sizeof(samsung_smid) / sizeof(u16); i++) { + if (rtlefuse->eeprom_smid == samsung_smid[i]) { + is_samsung_smid = true; + break; + } + } + /* Does this one have a Lenovo SMID? */ + for (i = 0; i < sizeof(lenovo_smid) / sizeof(u16); i++) { + if (rtlefuse->eeprom_smid == lenovo_smid[i]) { + is_lenovo_smid = true; + break; + } + } + switch (rtlefuse->eeprom_oemid) { + case EEPROM_CID_DEFAULT: + if (rtlefuse->eeprom_did == 0x8176) { + if (rtlefuse->eeprom_svid == 0x10EC && + is_toshiba_smid1) { + rtlhal->oem_id = RT_CID_TOSHIBA; + } else if (rtlefuse->eeprom_svid == 0x1025) { + rtlhal->oem_id = RT_CID_819X_ACER; + } else if (rtlefuse->eeprom_svid == 0x10EC && + is_samsung_smid) { + rtlhal->oem_id = RT_CID_819X_SAMSUNG; + } else if (rtlefuse->eeprom_svid == 0x10EC && + is_lenovo_smid) { + rtlhal->oem_id = RT_CID_819X_LENOVO; + } else if ((rtlefuse->eeprom_svid == 0x10EC && + rtlefuse->eeprom_smid == 0x8197) || + (rtlefuse->eeprom_svid == 0x10EC && + rtlefuse->eeprom_smid == 0x9196)) { + rtlhal->oem_id = RT_CID_819X_CLEVO; + } else if ((rtlefuse->eeprom_svid == 0x1028 && + rtlefuse->eeprom_smid == 0x8194) || + (rtlefuse->eeprom_svid == 0x1028 && + rtlefuse->eeprom_smid == 0x8198) || + (rtlefuse->eeprom_svid == 0x1028 && + rtlefuse->eeprom_smid == 0x9197) || + (rtlefuse->eeprom_svid == 0x1028 && + rtlefuse->eeprom_smid == 0x9198)) { + rtlhal->oem_id = RT_CID_819X_DELL; + } else if ((rtlefuse->eeprom_svid == 0x103C && + rtlefuse->eeprom_smid == 0x1629)) { + rtlhal->oem_id = RT_CID_819X_HP; + } else if ((rtlefuse->eeprom_svid == 0x1A32 && + rtlefuse->eeprom_smid == 0x2315)) { + rtlhal->oem_id = RT_CID_819X_QMI; + } else if ((rtlefuse->eeprom_svid == 0x10EC && + rtlefuse->eeprom_smid == 0x8203)) { + rtlhal->oem_id = RT_CID_819X_PRONETS; + } else if ((rtlefuse->eeprom_svid == 0x1043 && + rtlefuse->eeprom_smid == 0x84B5)) { + rtlhal->oem_id = RT_CID_819X_EDIMAX_ASUS; + } else { + rtlhal->oem_id = RT_CID_DEFAULT; + } + } else if (rtlefuse->eeprom_did == 0x8178) { + if (rtlefuse->eeprom_svid == 0x10EC && + is_toshiba_smid2) + rtlhal->oem_id = RT_CID_TOSHIBA; + else if (rtlefuse->eeprom_svid == 0x1025) + rtlhal->oem_id = RT_CID_819X_ACER; + else if ((rtlefuse->eeprom_svid == 0x10EC && + rtlefuse->eeprom_smid == 0x8186)) + rtlhal->oem_id = RT_CID_819X_PRONETS; + else if ((rtlefuse->eeprom_svid == 0x1043 && + rtlefuse->eeprom_smid == 0x84B6)) + rtlhal->oem_id = + RT_CID_819X_EDIMAX_ASUS; + else + rtlhal->oem_id = RT_CID_DEFAULT; + } else { + rtlhal->oem_id = RT_CID_DEFAULT; + } + break; + case EEPROM_CID_TOSHIBA: + rtlhal->oem_id = RT_CID_TOSHIBA; + break; + case EEPROM_CID_CCX: + rtlhal->oem_id = RT_CID_CCX; + break; + case EEPROM_CID_QMI: + rtlhal->oem_id = RT_CID_819X_QMI; + break; + case EEPROM_CID_WHQL: + break; + default: + rtlhal->oem_id = RT_CID_DEFAULT; + break; + } + } +} + +static void _rtl8723be_hal_customized_behavior(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + pcipriv->ledctl.led_opendrain = true; + switch (rtlhal->oem_id) { + case RT_CID_819X_HP: + pcipriv->ledctl.led_opendrain = true; + break; + case RT_CID_819X_LENOVO: + case RT_CID_DEFAULT: + case RT_CID_TOSHIBA: + case RT_CID_CCX: + case RT_CID_819X_ACER: + case RT_CID_WHQL: + default: + break; + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "RT Customized ID: 0x%02X\n", rtlhal->oem_id); +} + +void rtl8723be_read_eeprom_info(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp_u1b; + + rtlhal->version = _rtl8723be_read_chip_version(hw); + if (get_rf_type(rtlphy) == RF_1T1R) + rtlpriv->dm.rfpath_rxenable[0] = true; + else + rtlpriv->dm.rfpath_rxenable[0] = + rtlpriv->dm.rfpath_rxenable[1] = true; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "VersionID = 0x%4x\n", + rtlhal->version); + tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR); + if (tmp_u1b & BIT(4)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n"); + rtlefuse->epromtype = EEPROM_93C46; + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n"); + rtlefuse->epromtype = EEPROM_BOOT_EFUSE; + } + if (tmp_u1b & BIT(5)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); + rtlefuse->autoload_failflag = false; + _rtl8723be_read_adapter_info(hw, false); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n"); + } + _rtl8723be_hal_customized_behavior(hw); +} + +static void rtl8723be_update_hal_rate_table(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u32 ratr_value; + u8 ratr_index = 0; + u8 nmode = mac->ht_enable; + u8 mimo_ps = IEEE80211_SMPS_OFF; + u16 shortgi_rate; + u32 tmp_ratr_value; + u8 curtxbw_40mhz = mac->bw_40; + u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? + 1 : 0; + u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? + 1 : 0; + enum wireless_mode wirelessmode = mac->mode; + + if (rtlhal->current_bandtype == BAND_ON_5G) + ratr_value = sta->supp_rates[1] << 4; + else + ratr_value = sta->supp_rates[0]; + if (mac->opmode == NL80211_IFTYPE_ADHOC) + ratr_value = 0xfff; + ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 | + sta->ht_cap.mcs.rx_mask[0] << 12); + switch (wirelessmode) { + case WIRELESS_MODE_B: + if (ratr_value & 0x0000000c) + ratr_value &= 0x0000000d; + else + ratr_value &= 0x0000000f; + break; + case WIRELESS_MODE_G: + ratr_value &= 0x00000FF5; + break; + case WIRELESS_MODE_N_24G: + case WIRELESS_MODE_N_5G: + nmode = 1; + if (mimo_ps == IEEE80211_SMPS_STATIC) { + ratr_value &= 0x0007F005; + } else { + u32 ratr_mask; + + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) + ratr_mask = 0x000ff005; + else + ratr_mask = 0x0f0ff005; + ratr_value &= ratr_mask; + } + break; + default: + if (rtlphy->rf_type == RF_1T2R) + ratr_value &= 0x000ff0ff; + else + ratr_value &= 0x0f0ff0ff; + break; + } + if ((rtlpriv->btcoexist.bt_coexistence) && + (rtlpriv->btcoexist.bt_coexist_type == BT_CSR_BC4) && + (rtlpriv->btcoexist.bt_cur_state) && + (rtlpriv->btcoexist.bt_ant_isolation) && + ((rtlpriv->btcoexist.bt_service == BT_SCO) || + (rtlpriv->btcoexist.bt_service == BT_BUSY))) + ratr_value &= 0x0fffcfc0; + else + ratr_value &= 0x0FFFFFFF; + + if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) || + (!curtxbw_40mhz && curshortgi_20mhz))) { + ratr_value |= 0x10000000; + tmp_ratr_value = (ratr_value >> 12); + + for (shortgi_rate = 15; shortgi_rate > 0; shortgi_rate--) { + if ((1 << shortgi_rate) & tmp_ratr_value) + break; + } + shortgi_rate = (shortgi_rate << 12) | (shortgi_rate << 8) | + (shortgi_rate << 4) | (shortgi_rate); + } + rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value); + + RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, + "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)); +} + +static u8 _rtl8723be_mrate_idx_to_arfr_id(struct ieee80211_hw *hw, + u8 rate_index) +{ + u8 ret = 0; + + switch (rate_index) { + case RATR_INX_WIRELESS_NGB: + ret = 1; + break; + case RATR_INX_WIRELESS_N: + case RATR_INX_WIRELESS_NG: + ret = 5; + break; + case RATR_INX_WIRELESS_NB: + ret = 3; + break; + case RATR_INX_WIRELESS_GB: + ret = 6; + break; + case RATR_INX_WIRELESS_G: + ret = 7; + break; + case RATR_INX_WIRELESS_B: + ret = 8; + break; + default: + ret = 0; + break; + } + return ret; +} + +static void rtl8723be_update_hal_rate_mask(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 rssi_level) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_sta_info *sta_entry = NULL; + u32 ratr_bitmap; + u8 ratr_index; + u8 curtxbw_40mhz = (sta->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; + u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? + 1 : 0; + u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? + 1 : 0; + enum wireless_mode wirelessmode = 0; + bool shortgi = false; + u8 rate_mask[7]; + u8 macid = 0; + u8 mimo_ps = IEEE80211_SMPS_OFF; + + sta_entry = (struct rtl_sta_info *)sta->drv_priv; + wirelessmode = sta_entry->wireless_mode; + if (mac->opmode == NL80211_IFTYPE_STATION || + mac->opmode == NL80211_IFTYPE_MESH_POINT) + curtxbw_40mhz = mac->bw_40; + else if (mac->opmode == NL80211_IFTYPE_AP || + mac->opmode == NL80211_IFTYPE_ADHOC) + macid = sta->aid + 1; + + ratr_bitmap = sta->supp_rates[0]; + + if (mac->opmode == NL80211_IFTYPE_ADHOC) + ratr_bitmap = 0xfff; + + ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 | + sta->ht_cap.mcs.rx_mask[0] << 12); + switch (wirelessmode) { + case WIRELESS_MODE_B: + ratr_index = RATR_INX_WIRELESS_B; + if (ratr_bitmap & 0x0000000c) + ratr_bitmap &= 0x0000000d; + else + ratr_bitmap &= 0x0000000f; + break; + case WIRELESS_MODE_G: + ratr_index = RATR_INX_WIRELESS_GB; + + if (rssi_level == 1) + ratr_bitmap &= 0x00000f00; + else if (rssi_level == 2) + ratr_bitmap &= 0x00000ff0; + else + ratr_bitmap &= 0x00000ff5; + break; + case WIRELESS_MODE_A: + ratr_index = RATR_INX_WIRELESS_A; + ratr_bitmap &= 0x00000ff0; + break; + case WIRELESS_MODE_N_24G: + case WIRELESS_MODE_N_5G: + ratr_index = RATR_INX_WIRELESS_NGB; + + if (mimo_ps == IEEE80211_SMPS_STATIC || + mimo_ps == IEEE80211_SMPS_DYNAMIC) { + if (rssi_level == 1) + ratr_bitmap &= 0x00070000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0007f000; + else + ratr_bitmap &= 0x0007f005; + } else { + if (rtlphy->rf_type == RF_1T1R) { + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff015; + } else { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff005; + } + } else { + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x0f8f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f8ff000; + else + ratr_bitmap &= 0x0f8ff015; + } else { + if (rssi_level == 1) + ratr_bitmap &= 0x0f8f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f8ff000; + else + ratr_bitmap &= 0x0f8ff005; + } + } + } + if ((curtxbw_40mhz && curshortgi_40mhz) || + (!curtxbw_40mhz && curshortgi_20mhz)) { + if (macid == 0) + shortgi = true; + else if (macid == 1) + shortgi = false; + } + break; + default: + ratr_index = RATR_INX_WIRELESS_NGB; + + if (rtlphy->rf_type == RF_1T2R) + ratr_bitmap &= 0x000ff0ff; + else + ratr_bitmap &= 0x0f0ff0ff; + break; + } + sta_entry->ratr_index = ratr_index; + + RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, + "ratr_bitmap :%x\n", ratr_bitmap); + *(u32 *)&rate_mask = (ratr_bitmap & 0x0fffffff) | (ratr_index << 28); + rate_mask[0] = macid; + rate_mask[1] = _rtl8723be_mrate_idx_to_arfr_id(hw, ratr_index) | + (shortgi ? 0x80 : 0x00); + rate_mask[2] = curtxbw_40mhz; + /* if (prox_priv->proxim_modeinfo->power_output > 0) + * rate_mask[2] |= BIT(6); + */ + + rate_mask[3] = (u8)(ratr_bitmap & 0x000000ff); + rate_mask[4] = (u8)((ratr_bitmap & 0x0000ff00) >> 8); + rate_mask[5] = (u8)((ratr_bitmap & 0x00ff0000) >> 16); + rate_mask[6] = (u8)((ratr_bitmap & 0xff000000) >> 24); + + RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, + "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x:%x:%x\n", + ratr_index, ratr_bitmap, + rate_mask[0], rate_mask[1], + rate_mask[2], rate_mask[3], + rate_mask[4], rate_mask[5], + rate_mask[6]); + rtl8723be_fill_h2c_cmd(hw, H2C_8723BE_RA_MASK, 7, rate_mask); + _rtl8723be_set_bcn_ctrl_reg(hw, BIT(3), 0); +} + +void rtl8723be_update_hal_rate_tbl(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 rssi_level) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + if (rtlpriv->dm.useramask) + rtl8723be_update_hal_rate_mask(hw, sta, rssi_level); + else + rtl8723be_update_hal_rate_table(hw, sta); +} + +void rtl8723be_update_channel_access_setting(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u16 sifs_timer; + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, &mac->slot_time); + if (!mac->ht_enable) + sifs_timer = 0x0a0a; + else + sifs_timer = 0x0e0e; + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); +} + +bool rtl8723be_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate; + u8 u1tmp; + bool actuallyset = false; + + if (rtlpriv->rtlhal.being_init_adapter) + return false; + + if (ppsc->swrf_processing) + return false; + + spin_lock(&rtlpriv->locks.rf_ps_lock); + if (ppsc->rfchange_inprogress) { + spin_unlock(&rtlpriv->locks.rf_ps_lock); + return false; + } else { + ppsc->rfchange_inprogress = true; + spin_unlock(&rtlpriv->locks.rf_ps_lock); + } + cur_rfstate = ppsc->rfpwr_state; + + rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL_2, + rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL_2) & ~(BIT(1))); + + u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL_2); + + if (rtlphy->polarity_ctl) + e_rfpowerstate_toset = (u1tmp & BIT(1)) ? ERFOFF : ERFON; + else + e_rfpowerstate_toset = (u1tmp & BIT(1)) ? ERFON : ERFOFF; + + if (ppsc->hwradiooff && + (e_rfpowerstate_toset == ERFON)) { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "GPIOChangeRF - HW Radio ON, RF ON\n"); + + e_rfpowerstate_toset = ERFON; + ppsc->hwradiooff = false; + actuallyset = true; + } else if (!ppsc->hwradiooff && + (e_rfpowerstate_toset == ERFOFF)) { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "GPIOChangeRF - HW Radio OFF, RF OFF\n"); + + e_rfpowerstate_toset = ERFOFF; + ppsc->hwradiooff = true; + actuallyset = true; + } + if (actuallyset) { + spin_lock(&rtlpriv->locks.rf_ps_lock); + ppsc->rfchange_inprogress = false; + spin_unlock(&rtlpriv->locks.rf_ps_lock); + } else { + if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) + RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); + + spin_lock(&rtlpriv->locks.rf_ps_lock); + ppsc->rfchange_inprogress = false; + spin_unlock(&rtlpriv->locks.rf_ps_lock); + } + *valid = 1; + return !ppsc->hwradiooff; +} + +void rtl8723be_set_key(struct ieee80211_hw *hw, u32 key_index, + u8 *p_macaddr, bool is_group, u8 enc_algo, + bool is_wepkey, bool clear_all) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 *macaddr = p_macaddr; + u32 entry_id = 0; + bool is_pairwise = false; + + static u8 cam_const_addr[4][6] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} + }; + static u8 cam_const_broad[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + if (clear_all) { + u8 idx = 0; + u8 cam_offset = 0; + u8 clear_number = 5; + + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n"); + + for (idx = 0; idx < clear_number; idx++) { + rtl_cam_mark_invalid(hw, cam_offset + idx); + rtl_cam_empty_entry(hw, cam_offset + idx); + + if (idx < 5) { + memset(rtlpriv->sec.key_buf[idx], 0, + MAX_KEY_LEN); + rtlpriv->sec.key_len[idx] = 0; + } + } + } else { + switch (enc_algo) { + case WEP40_ENCRYPTION: + enc_algo = CAM_WEP40; + break; + case WEP104_ENCRYPTION: + enc_algo = CAM_WEP104; + break; + case TKIP_ENCRYPTION: + enc_algo = CAM_TKIP; + break; + case AESCCMP_ENCRYPTION: + enc_algo = CAM_AES; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + enc_algo = CAM_TKIP; + break; + } + + if (is_wepkey || rtlpriv->sec.use_defaultkey) { + macaddr = cam_const_addr[key_index]; + entry_id = key_index; + } else { + if (is_group) { + macaddr = cam_const_broad; + entry_id = key_index; + } else { + if (mac->opmode == NL80211_IFTYPE_AP) { + entry_id = rtl_cam_get_free_entry(hw, + p_macaddr); + if (entry_id >= TOTAL_CAM_ENTRY) { + RT_TRACE(rtlpriv, COMP_SEC, + DBG_EMERG, + "Can not find free" + " hw security cam " + "entry\n"); + return; + } + } else { + entry_id = CAM_PAIRWISE_KEY_POSITION; + } + key_index = PAIRWISE_KEYIDX; + is_pairwise = true; + } + } + if (rtlpriv->sec.key_len[key_index] == 0) { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "delete one entry, entry_id is %d\n", + entry_id); + if (mac->opmode == NL80211_IFTYPE_AP) + rtl_cam_del_entry(hw, p_macaddr); + rtl_cam_delete_one_entry(hw, p_macaddr, entry_id); + } else { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "add one entry\n"); + if (is_pairwise) { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "set Pairwise key\n"); + + rtl_cam_add_one_entry(hw, macaddr, key_index, + entry_id, enc_algo, + CAM_CONFIG_NO_USEDK, + rtlpriv->sec.key_buf[key_index]); + } else { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "set group key\n"); + + if (mac->opmode == NL80211_IFTYPE_ADHOC) { + rtl_cam_add_one_entry(hw, + rtlefuse->dev_addr, + PAIRWISE_KEYIDX, + CAM_PAIRWISE_KEY_POSITION, + enc_algo, + CAM_CONFIG_NO_USEDK, + rtlpriv->sec.key_buf + [entry_id]); + } + rtl_cam_add_one_entry(hw, macaddr, key_index, + entry_id, enc_algo, + CAM_CONFIG_NO_USEDK, + rtlpriv->sec.key_buf[entry_id]); + } + } + } +} + +void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, + bool auto_load_fail, u8 *hwinfo) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 value; + u32 tmpu_32; + + if (!auto_load_fail) { + tmpu_32 = rtl_read_dword(rtlpriv, REG_MULTI_FUNC_CTRL); + if (tmpu_32 & BIT(18)) + rtlpriv->btcoexist.btc_info.btcoexist = 1; + else + rtlpriv->btcoexist.btc_info.btcoexist = 0; + value = hwinfo[RF_OPTION4]; + rtlpriv->btcoexist.btc_info.bt_type = BT_RTL8723B; + rtlpriv->btcoexist.btc_info.ant_num = (value & 0x1); + } else { + rtlpriv->btcoexist.btc_info.btcoexist = 0; + rtlpriv->btcoexist.btc_info.bt_type = BT_RTL8723B; + rtlpriv->btcoexist.btc_info.ant_num = ANT_X2; + } +} + +void rtl8723be_bt_reg_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + /* 0:Low, 1:High, 2:From Efuse. */ + rtlpriv->btcoexist.reg_bt_iso = 2; + /* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter. */ + rtlpriv->btcoexist.reg_bt_sco = 3; + /* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */ + rtlpriv->btcoexist.reg_bt_sco = 0; +} + +void rtl8723be_bt_hw_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->cfg->ops->get_btc_status()) + rtlpriv->btcoexist.btc_ops->btc_init_hw_config(rtlpriv); +} + +void rtl8723be_suspend(struct ieee80211_hw *hw) +{ +} + +void rtl8723be_resume(struct ieee80211_hw *hw) +{ +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.h b/drivers/net/wireless/rtlwifi/rtl8723be/hw.h new file mode 100644 index 00000000000..64c7551af6b --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_HW_H__ +#define __RTL8723BE_HW_H__ + +void rtl8723be_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); +void rtl8723be_read_eeprom_info(struct ieee80211_hw *hw); + +void rtl8723be_interrupt_recognized(struct ieee80211_hw *hw, + u32 *p_inta, u32 *p_intb); +int rtl8723be_hw_init(struct ieee80211_hw *hw); +void rtl8723be_card_disable(struct ieee80211_hw *hw); +void rtl8723be_enable_interrupt(struct ieee80211_hw *hw); +void rtl8723be_disable_interrupt(struct ieee80211_hw *hw); +int rtl8723be_set_network_type(struct ieee80211_hw *hw, + enum nl80211_iftype type); +void rtl8723be_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); +void rtl8723be_set_qos(struct ieee80211_hw *hw, int aci); +void rtl8723be_set_beacon_related_registers(struct ieee80211_hw *hw); +void rtl8723be_set_beacon_interval(struct ieee80211_hw *hw); +void rtl8723be_update_interrupt_mask(struct ieee80211_hw *hw, + u32 add_msr, u32 rm_msr); +void rtl8723be_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); +void rtl8723be_update_hal_rate_tbl(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 rssi_level); +void rtl8723be_update_channel_access_setting(struct ieee80211_hw *hw); +bool rtl8723be_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid); +void rtl8723be_enable_hw_security_config(struct ieee80211_hw *hw); +void rtl8723be_set_key(struct ieee80211_hw *hw, u32 key_index, + u8 *p_macaddr, bool is_group, u8 enc_algo, + bool is_wepkey, bool clear_all); +void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, + bool autoload_fail, u8 *hwinfo); +void rtl8723be_bt_reg_init(struct ieee80211_hw *hw); +void rtl8723be_bt_hw_init(struct ieee80211_hw *hw); +void rtl8723be_suspend(struct ieee80211_hw *hw); +void rtl8723be_resume(struct ieee80211_hw *hw); +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/led.c b/drivers/net/wireless/rtlwifi/rtl8723be/led.c new file mode 100644 index 00000000000..cb931a38dc4 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/led.c @@ -0,0 +1,153 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "reg.h" +#include "led.h" + +static void _rtl8723be_init_led(struct ieee80211_hw *hw, struct rtl_led *pled, + enum rtl_led_pin ledpin) +{ + pled->hw = hw; + pled->ledpin = ledpin; + pled->ledon = false; +} + +void rtl8723be_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled) +{ + u8 ledcfg; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, + "LedAddr:%X ledpin =%d\n", REG_LEDCFG2, pled->ledpin); + + switch (pled->ledpin) { + case LED_PIN_GPIO0: + break; + case LED_PIN_LED0: + ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); + ledcfg &= ~BIT(6); + rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5)); + break; + case LED_PIN_LED1: + ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1); + rtl_write_byte(rtlpriv, REG_LEDCFG1, ledcfg & 0x10); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + pled->ledon = true; +} + +void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + u8 ledcfg; + + RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, + "LedAddr:%X ledpin =%d\n", REG_LEDCFG2, pled->ledpin); + + ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); + + switch (pled->ledpin) { + case LED_PIN_GPIO0: + break; + case LED_PIN_LED0: + ledcfg &= 0xf0; + if (pcipriv->ledctl.led_opendrain) { + ledcfg &= 0x90; /* Set to software control. */ + rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg|BIT(3))); + ledcfg = rtl_read_byte(rtlpriv, REG_MAC_PINMUX_CFG); + ledcfg &= 0xFE; + rtl_write_byte(rtlpriv, REG_MAC_PINMUX_CFG, ledcfg); + } else { + ledcfg &= ~BIT(6); + rtl_write_byte(rtlpriv, REG_LEDCFG2, + (ledcfg | BIT(3) | BIT(5))); + } + break; + case LED_PIN_LED1: + ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG1); + ledcfg &= 0x10; /* Set to software control. */ + rtl_write_byte(rtlpriv, REG_LEDCFG1, ledcfg|BIT(3)); + + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } + pled->ledon = false; +} + +void rtl8723be_init_sw_leds(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + _rtl8723be_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0); + _rtl8723be_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1); +} + +static void _rtl8723be_sw_led_control(struct ieee80211_hw *hw, + enum led_ctl_mode ledaction) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_led *pled0 = &(pcipriv->ledctl.sw_led0); + switch (ledaction) { + case LED_CTL_POWER_ON: + case LED_CTL_LINK: + case LED_CTL_NO_LINK: + rtl8723be_sw_led_on(hw, pled0); + break; + case LED_CTL_POWER_OFF: + rtl8723be_sw_led_off(hw, pled0); + break; + default: + break; + } +} + +void rtl8723be_led_control(struct ieee80211_hw *hw, + enum led_ctl_mode ledaction) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) && + (ledaction == LED_CTL_TX || + ledaction == LED_CTL_RX || + ledaction == LED_CTL_SITE_SURVEY || + ledaction == LED_CTL_LINK || + ledaction == LED_CTL_NO_LINK || + ledaction == LED_CTL_START_TO_LINK || + ledaction == LED_CTL_POWER_ON)) { + return; + } + RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d,\n", ledaction); + _rtl8723be_sw_led_control(hw, ledaction); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/led.h b/drivers/net/wireless/rtlwifi/rtl8723be/led.h new file mode 100644 index 00000000000..c57de379ee8 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/led.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_LED_H__ +#define __RTL8723BE_LED_H__ + +void rtl8723be_init_sw_leds(struct ieee80211_hw *hw); +void rtl8723be_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled); +void rtl8723be_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled); +void rtl8723be_led_control(struct ieee80211_hw *hw, + enum led_ctl_mode ledaction); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c new file mode 100644 index 00000000000..1575ef9ece9 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c @@ -0,0 +1,2156 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "../ps.h" +#include "../core.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "../rtl8723com/phy_common.h" +#include "rf.h" +#include "dm.h" +#include "table.h" +#include "trx.h" + +static bool _rtl8723be_phy_bb8723b_config_parafile(struct ieee80211_hw *hw); +static bool _rtl8723be_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, + u8 configtype); +static bool rtl8723be_phy_sw_chn_step_by_step(struct ieee80211_hw *hw, + u8 channel, u8 *stage, + u8 *step, u32 *delay); +static bool _rtl8723be_check_condition(struct ieee80211_hw *hw, + const u32 condition) +{ + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u32 _board = rtlefuse->board_type; /*need efuse define*/ + u32 _interface = rtlhal->interface; + u32 _platform = 0x08;/*SupportPlatform */ + u32 cond = condition; + + if (condition == 0xCDCDCDCD) + return true; + + cond = condition & 0xFF; + if ((_board & cond) == 0 && cond != 0x1F) + return false; + + cond = condition & 0xFF00; + cond = cond >> 8; + if ((_interface & cond) == 0 && cond != 0x07) + return false; + + cond = condition & 0xFF0000; + cond = cond >> 16; + if ((_platform & cond) == 0 && cond != 0x0F) + return false; + return true; +} + +static bool _rtl8723be_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i; + u32 arraylength; + u32 *ptrarray; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read rtl8723beMACPHY_Array\n"); + arraylength = RTL8723BEMAC_1T_ARRAYLEN; + ptrarray = RTL8723BEMAC_1T_ARRAY; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Img:RTL8723bEMAC_1T_ARRAY LEN %d\n", arraylength); + for (i = 0; i < arraylength; i = i + 2) + rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]); + return true; +} + +static bool _rtl8723be_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, + u8 configtype) +{ + #define READ_NEXT_PAIR(v1, v2, i) \ + do { \ + i += 2; \ + v1 = array_table[i];\ + v2 = array_table[i+1]; \ + } while (0) + + int i; + u32 *array_table; + u16 arraylen; + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 v1 = 0, v2 = 0; + + if (configtype == BASEBAND_CONFIG_PHY_REG) { + arraylen = RTL8723BEPHY_REG_1TARRAYLEN; + array_table = RTL8723BEPHY_REG_1TARRAY; + + for (i = 0; i < arraylen; i = i + 2) { + v1 = array_table[i]; + v2 = array_table[i+1]; + if (v1 < 0xcdcdcdcd) { + rtl_bb_delay(hw, v1, v2); + } else {/*This line is the start line of branch.*/ + if (!_rtl8723be_check_condition(hw, array_table[i])) { + /*Discard the following (offset, data) pairs*/ + READ_NEXT_PAIR(v1, v2, i); + while (v2 != 0xDEAD && + v2 != 0xCDEF && + v2 != 0xCDCD && + i < arraylen - 2) { + READ_NEXT_PAIR(v1, v2, i); + } + i -= 2; /* prevent from for-loop += 2*/ + /* Configure matched pairs and + * skip to end of if-else. + */ + } else { + READ_NEXT_PAIR(v1, v2, i); + while (v2 != 0xDEAD && + v2 != 0xCDEF && + v2 != 0xCDCD && + i < arraylen - 2) { + rtl_bb_delay(hw, + v1, v2); + READ_NEXT_PAIR(v1, v2, i); + } + + while (v2 != 0xDEAD && i < arraylen - 2) + READ_NEXT_PAIR(v1, v2, i); + } + } + } + } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { + arraylen = RTL8723BEAGCTAB_1TARRAYLEN; + array_table = RTL8723BEAGCTAB_1TARRAY; + + for (i = 0; i < arraylen; i = i + 2) { + v1 = array_table[i]; + v2 = array_table[i+1]; + if (v1 < 0xCDCDCDCD) { + rtl_set_bbreg(hw, array_table[i], + MASKDWORD, + array_table[i + 1]); + udelay(1); + continue; + } else {/*This line is the start line of branch.*/ + if (!_rtl8723be_check_condition(hw, array_table[i])) { + /* Discard the following + * (offset, data) pairs + */ + READ_NEXT_PAIR(v1, v2, i); + while (v2 != 0xDEAD && + v2 != 0xCDEF && + v2 != 0xCDCD && + i < arraylen - 2) { + READ_NEXT_PAIR(v1, v2, i); + } + i -= 2; /* prevent from for-loop += 2*/ + /*Configure matched pairs and + *skip to end of if-else. + */ + } else { + READ_NEXT_PAIR(v1, v2, i); + while (v2 != 0xDEAD && + v2 != 0xCDEF && + v2 != 0xCDCD && + i < arraylen - 2) { + rtl_set_bbreg(hw, array_table[i], + MASKDWORD, + array_table[i + 1]); + udelay(1); + READ_NEXT_PAIR(v1, v2, i); + } + + while (v2 != 0xDEAD && i < arraylen - 2) + READ_NEXT_PAIR(v1, v2, i); + } + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "The agctab_array_table[0] is " + "%x Rtl818EEPHY_REGArray[1] is %x\n", + array_table[i], array_table[i + 1]); + } + } + return true; +} + +static u8 _rtl8723be_get_rate_section_index(u32 regaddr) +{ + u8 index = 0; + + switch (regaddr) { + case RTXAGC_A_RATE18_06: + case RTXAGC_B_RATE18_06: + index = 0; + break; + case RTXAGC_A_RATE54_24: + case RTXAGC_B_RATE54_24: + index = 1; + break; + case RTXAGC_A_CCK1_MCS32: + case RTXAGC_B_CCK1_55_MCS32: + index = 2; + break; + case RTXAGC_B_CCK11_A_CCK2_11: + index = 3; + break; + case RTXAGC_A_MCS03_MCS00: + case RTXAGC_B_MCS03_MCS00: + index = 4; + break; + case RTXAGC_A_MCS07_MCS04: + case RTXAGC_B_MCS07_MCS04: + index = 5; + break; + case RTXAGC_A_MCS11_MCS08: + case RTXAGC_B_MCS11_MCS08: + index = 6; + break; + case RTXAGC_A_MCS15_MCS12: + case RTXAGC_B_MCS15_MCS12: + index = 7; + break; + default: + regaddr &= 0xFFF; + if (regaddr >= 0xC20 && regaddr <= 0xC4C) + index = (u8) ((regaddr - 0xC20) / 4); + else if (regaddr >= 0xE20 && regaddr <= 0xE4C) + index = (u8) ((regaddr - 0xE20) / 4); + break; + }; + return index; +} + +u32 rtl8723be_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 regaddr, u32 bitmask) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 original_value, readback_value, bitshift; + unsigned long flags; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n", + regaddr, rfpath, bitmask); + + spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); + + original_value = rtl8723_phy_rf_serial_read(hw, rfpath, regaddr); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); + readback_value = (original_value & bitmask) >> bitshift; + + spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), rfpath(%#x), " + "bitmask(%#x), original_value(%#x)\n", + regaddr, rfpath, bitmask, original_value); + + return readback_value; +} + +void rtl8723be_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path path, + u32 regaddr, u32 bitmask, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 original_value, bitshift; + unsigned long flags; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", + regaddr, bitmask, data, path); + + spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); + + if (bitmask != RFREG_OFFSET_MASK) { + original_value = rtl8723_phy_rf_serial_read(hw, path, + regaddr); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); + data = ((original_value & (~bitmask)) | + (data << bitshift)); + } + + rtl8723_phy_rf_serial_write(hw, path, regaddr, data); + + spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", + regaddr, bitmask, data, path); +} + +bool rtl8723be_phy_mac_config(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + bool rtstatus = _rtl8723be_phy_config_mac_with_headerfile(hw); + + rtl_write_byte(rtlpriv, 0x04CA, 0x0B); + return rtstatus; +} + +bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw) +{ + bool rtstatus = true; + struct rtl_priv *rtlpriv = rtl_priv(hw); + u16 regval; + u8 reg_hwparafile = 1; + u32 tmp; + u8 crystalcap = rtlpriv->efuse.crystalcap; + rtl8723_phy_init_bb_rf_reg_def(hw); + regval = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN); + rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, + regval | BIT(13) | BIT(0) | BIT(1)); + + rtl_write_byte(rtlpriv, REG_RF_CTRL, RF_EN | RF_RSTB | RF_SDMRSTB); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, + FEN_PPLL | FEN_PCIEA | FEN_DIO_PCIE | + FEN_BB_GLB_RSTN | FEN_BBRSTB); + tmp = rtl_read_dword(rtlpriv, 0x4c); + rtl_write_dword(rtlpriv, 0x4c, tmp | BIT(23)); + + rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL + 1, 0x80); + + if (reg_hwparafile == 1) + rtstatus = _rtl8723be_phy_bb8723b_config_parafile(hw); + + crystalcap = crystalcap & 0x3F; + rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000, + (crystalcap | crystalcap << 6)); + + return rtstatus; +} + +bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw) +{ + return rtl8723be_phy_rf6052_config(hw); +} + +static void _rtl8723be_config_rf_reg(struct ieee80211_hw *hw, u32 addr, + u32 data, enum radio_path rfpath, + u32 regaddr) +{ + if (addr == 0xfe || addr == 0xffe) { + mdelay(50); + } else { + rtl_set_rfreg(hw, rfpath, regaddr, RFREG_OFFSET_MASK, data); + udelay(1); + } +} + +static void _rtl8723be_config_rf_radio_a(struct ieee80211_hw *hw, + u32 addr, u32 data) +{ + u32 content = 0x1000; /*RF Content: radio_a_txt*/ + u32 maskforphyset = (u32)(content & 0xE000); + + _rtl8723be_config_rf_reg(hw, addr, data, RF90_PATH_A, + addr | maskforphyset); +} + +static void _rtl8723be_phy_init_tx_power_by_rate(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + u8 band, path, txnum, section; + + for (band = BAND_ON_2_4G; band <= BAND_ON_5G; ++band) + for (path = 0; path < TX_PWR_BY_RATE_NUM_RF; ++path) + for (txnum = 0; txnum < TX_PWR_BY_RATE_NUM_RF; ++txnum) + for (section = 0; + section < TX_PWR_BY_RATE_NUM_SECTION; + ++section) + rtlphy->tx_power_by_rate_offset[band] + [path][txnum][section] = 0; +} + +static void phy_set_txpwr_by_rate_base(struct ieee80211_hw *hw, u8 band, + u8 path, u8 rate_section, + u8 txnum, u8 value) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + if (path > RF90_PATH_D) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Invalid Rf Path %d in phy_SetTxPowerByRatBase()\n", + path); + return; + } + + if (band == BAND_ON_2_4G) { + switch (rate_section) { + case CCK: + rtlphy->txpwr_by_rate_base_24g[path][txnum][0] = value; + break; + case OFDM: + rtlphy->txpwr_by_rate_base_24g[path][txnum][1] = value; + break; + case HT_MCS0_MCS7: + rtlphy->txpwr_by_rate_base_24g[path][txnum][2] = value; + break; + case HT_MCS8_MCS15: + rtlphy->txpwr_by_rate_base_24g[path][txnum][3] = value; + break; + default: + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Invalid RateSection %d in Band 2.4G, Rf Path" + " %d, %dTx in PHY_SetTxPowerByRateBase()\n", + rate_section, path, txnum); + break; + }; + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Invalid Band %d in PHY_SetTxPowerByRateBase()\n", + band); + } +} + +static u8 phy_get_txpwr_by_rate_base(struct ieee80211_hw *hw, u8 band, u8 path, + u8 txnum, u8 rate_section) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 value = 0; + if (path > RF90_PATH_D) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Invalid Rf Path %d in PHY_GetTxPowerByRateBase()\n", + path); + return 0; + } + + if (band == BAND_ON_2_4G) { + switch (rate_section) { + case CCK: + value = rtlphy->txpwr_by_rate_base_24g[path][txnum][0]; + break; + case OFDM: + value = rtlphy->txpwr_by_rate_base_24g[path][txnum][1]; + break; + case HT_MCS0_MCS7: + value = rtlphy->txpwr_by_rate_base_24g[path][txnum][2]; + break; + case HT_MCS8_MCS15: + value = rtlphy->txpwr_by_rate_base_24g[path][txnum][3]; + break; + default: + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Invalid RateSection %d in Band 2.4G, Rf Path" + " %d, %dTx in PHY_GetTxPowerByRateBase()\n", + rate_section, path, txnum); + break; + }; + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Invalid Band %d in PHY_GetTxPowerByRateBase()\n", + band); + } + + return value; +} + +static void _rtl8723be_phy_store_txpower_by_rate_base(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u16 raw_value = 0; + u8 base = 0, path = 0; + + for (path = RF90_PATH_A; path <= RF90_PATH_B; ++path) { + if (path == RF90_PATH_A) { + raw_value = (u16) (rtlphy->tx_power_by_rate_offset + [BAND_ON_2_4G][path][RF_1TX][3] >> 24) & 0xFF; + base = (raw_value >> 4) * 10 + (raw_value & 0xF); + phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, CCK, + RF_1TX, base); + } else if (path == RF90_PATH_B) { + raw_value = (u16) (rtlphy->tx_power_by_rate_offset + [BAND_ON_2_4G][path][RF_1TX][3] >> 0) & 0xFF; + base = (raw_value >> 4) * 10 + (raw_value & 0xF); + phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, + CCK, RF_1TX, base); + } + raw_value = (u16) (rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [path][RF_1TX][1] >> 24) & 0xFF; + base = (raw_value >> 4) * 10 + (raw_value & 0xF); + phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, OFDM, RF_1TX, + base); + + raw_value = (u16) (rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [path][RF_1TX][5] >> 24) & 0xFF; + base = (raw_value >> 4) * 10 + (raw_value & 0xF); + phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, HT_MCS0_MCS7, + RF_1TX, base); + + raw_value = (u16) (rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [path][RF_2TX][7] >> 24) & 0xFF; + base = (raw_value >> 4) * 10 + (raw_value & 0xF); + phy_set_txpwr_by_rate_base(hw, BAND_ON_2_4G, path, + HT_MCS8_MCS15, RF_2TX, base); + } +} + +static void phy_conv_dbm_to_rel(u32 *data, u8 start, u8 end, u8 base_val) +{ + char i = 0; + u8 temp_value = 0; + u32 temp_data = 0; + + for (i = 3; i >= 0; --i) { + if (i >= start && i <= end) { + /* Get the exact value */ + temp_value = (u8) (*data >> (i * 8)) & 0xF; + temp_value += ((u8) ((*data >> (i*8 + 4)) & 0xF)) * 10; + + /* Change the value to a relative value */ + temp_value = (temp_value > base_val) ? + temp_value - base_val : + base_val - temp_value; + } else { + temp_value = (u8) (*data >> (i * 8)) & 0xFF; + } + temp_data <<= 8; + temp_data |= temp_value; + } + *data = temp_data; +} + +static void conv_dbm_to_rel(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 base = 0, rfpath = RF90_PATH_A; + + base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath, + RF_1TX, CCK); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_1TX][2]), 1, 1, base); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_1TX][3]), 1, 3, base); + + base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath, + RF_1TX, OFDM); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_1TX][0]), 0, 3, base); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_1TX][1]), 0, 3, base); + + base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath, + RF_1TX, HT_MCS0_MCS7); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_1TX][4]), 0, 3, base); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_1TX][5]), 0, 3, base); + + base = phy_get_txpwr_by_rate_base(hw, BAND_ON_2_4G, rfpath, + RF_2TX, HT_MCS8_MCS15); + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_2TX][6]), 0, 3, base); + + phy_conv_dbm_to_rel(&(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G] + [rfpath][RF_2TX][7]), 0, 3, base); + + RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, + "<=== conv_dbm_to_rel()\n"); +} + +static void _rtl8723be_phy_txpower_by_rate_configuration( + struct ieee80211_hw *hw) +{ + _rtl8723be_phy_store_txpower_by_rate_base(hw); + conv_dbm_to_rel(hw); +} + +static bool _rtl8723be_phy_bb8723b_config_parafile(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + bool rtstatus; + + rtstatus = _rtl8723be_phy_config_bb_with_headerfile(hw, + BASEBAND_CONFIG_PHY_REG); + if (!rtstatus) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write BB Reg Fail!!"); + return false; + } + _rtl8723be_phy_init_tx_power_by_rate(hw); + if (!rtlefuse->autoload_failflag) { + rtlphy->pwrgroup_cnt = 0; + rtstatus = _rtl8723be_phy_config_bb_with_pgheaderfile(hw, + BASEBAND_CONFIG_PHY_REG); + } + _rtl8723be_phy_txpower_by_rate_configuration(hw); + if (!rtstatus) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "BB_PG Reg Fail!!"); + return false; + } + rtstatus = _rtl8723be_phy_config_bb_with_headerfile(hw, + BASEBAND_CONFIG_AGC_TAB); + if (!rtstatus) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AGC Table Fail\n"); + return false; + } + rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw, + RFPGA0_XA_HSSIPARAMETER2, + 0x200)); + return true; +} + +static void _rtl8723be_store_tx_power_by_rate(struct ieee80211_hw *hw, + u32 band, u32 rfpath, + u32 txnum, u32 regaddr, + u32 bitmask, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 rate_section = _rtl8723be_get_rate_section_index(regaddr); + + if (band != BAND_ON_2_4G && band != BAND_ON_5G) { + RT_TRACE(rtlpriv, COMP_POWER, PHY_TXPWR, + "Invalid Band %d\n", band); + return; + } + + if (rfpath > TX_PWR_BY_RATE_NUM_RF) { + RT_TRACE(rtlpriv, COMP_POWER, PHY_TXPWR, + "Invalid RfPath %d\n", rfpath); + return; + } + if (txnum > TX_PWR_BY_RATE_NUM_RF) { + RT_TRACE(rtlpriv, COMP_POWER, PHY_TXPWR, + "Invalid TxNum %d\n", txnum); + return; + } + rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] = + data; +} + +static bool _rtl8723be_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, + u8 configtype) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + int i; + u32 *phy_regarray_table_pg; + u16 phy_regarray_pg_len; + u32 v1 = 0, v2 = 0, v3 = 0, v4 = 0, v5 = 0, v6 = 0; + + phy_regarray_pg_len = RTL8723BEPHY_REG_ARRAY_PGLEN; + phy_regarray_table_pg = RTL8723BEPHY_REG_ARRAY_PG; + + if (configtype == BASEBAND_CONFIG_PHY_REG) { + for (i = 0; i < phy_regarray_pg_len; i = i + 6) { + v1 = phy_regarray_table_pg[i]; + v2 = phy_regarray_table_pg[i+1]; + v3 = phy_regarray_table_pg[i+2]; + v4 = phy_regarray_table_pg[i+3]; + v5 = phy_regarray_table_pg[i+4]; + v6 = phy_regarray_table_pg[i+5]; + + if (v1 < 0xcdcdcdcd) { + if (phy_regarray_table_pg[i] == 0xfe || + phy_regarray_table_pg[i] == 0xffe) + mdelay(50); + else + _rtl8723be_store_tx_power_by_rate(hw, + v1, v2, v3, v4, v5, v6); + continue; + } else { + /*don't need the hw_body*/ + if (!_rtl8723be_check_condition(hw, + phy_regarray_table_pg[i])) { + i += 2; /* skip the pair of expression*/ + v1 = phy_regarray_table_pg[i]; + v2 = phy_regarray_table_pg[i+1]; + v3 = phy_regarray_table_pg[i+2]; + while (v2 != 0xDEAD) { + i += 3; + v1 = phy_regarray_table_pg[i]; + v2 = phy_regarray_table_pg[i+1]; + v3 = phy_regarray_table_pg[i+2]; + } + } + } + } + } else { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "configtype != BaseBand_Config_PHY_REG\n"); + } + return true; +} + +bool rtl8723be_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath) +{ + #define READ_NEXT_RF_PAIR(v1, v2, i) \ + do { \ + i += 2; \ + v1 = radioa_array_table[i]; \ + v2 = radioa_array_table[i+1]; \ + } while (0) + + int i; + bool rtstatus = true; + u32 *radioa_array_table; + u16 radioa_arraylen; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u32 v1 = 0, v2 = 0; + + radioa_arraylen = RTL8723BE_RADIOA_1TARRAYLEN; + radioa_array_table = RTL8723BE_RADIOA_1TARRAY; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Radio_A:RTL8723BE_RADIOA_1TARRAY %d\n", radioa_arraylen); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath); + rtstatus = true; + switch (rfpath) { + case RF90_PATH_A: + for (i = 0; i < radioa_arraylen; i = i + 2) { + v1 = radioa_array_table[i]; + v2 = radioa_array_table[i+1]; + if (v1 < 0xcdcdcdcd) { + _rtl8723be_config_rf_radio_a(hw, v1, v2); + } else { /*This line is the start line of branch.*/ + if (!_rtl8723be_check_condition(hw, + radioa_array_table[i])) { + /* Discard the following + * (offset, data) pairs + */ + READ_NEXT_RF_PAIR(v1, v2, i); + while (v2 != 0xDEAD && + v2 != 0xCDEF && + v2 != 0xCDCD && + i < radioa_arraylen - 2) + READ_NEXT_RF_PAIR(v1, v2, i); + i -= 2; /* prevent from for-loop += 2*/ + } else { + /* Configure matched pairs + * and skip to end of if-else. + */ + READ_NEXT_RF_PAIR(v1, v2, i); + while (v2 != 0xDEAD && + v2 != 0xCDEF && + v2 != 0xCDCD && + i < radioa_arraylen - 2) { + _rtl8723be_config_rf_radio_a(hw, + v1, v2); + READ_NEXT_RF_PAIR(v1, v2, i); + } + + while (v2 != 0xDEAD && + i < radioa_arraylen - 2) { + READ_NEXT_RF_PAIR(v1, v2, i); + } + } + } + } + + if (rtlhal->oem_id == RT_CID_819X_HP) + _rtl8723be_config_rf_radio_a(hw, 0x52, 0x7E4BD); + + break; + case RF90_PATH_B: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + case RF90_PATH_C: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + case RF90_PATH_D: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + return true; +} + +void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + rtlphy->default_initialgain[0] = + (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0); + rtlphy->default_initialgain[1] = + (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0); + rtlphy->default_initialgain[2] = + (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0); + rtlphy->default_initialgain[3] = + (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Default initial gain (c50 = 0x%x, " + "c58 = 0x%x, c60 = 0x%x, c68 = 0x%x\n", + rtlphy->default_initialgain[0], + rtlphy->default_initialgain[1], + rtlphy->default_initialgain[2], + rtlphy->default_initialgain[3]); + + rtlphy->framesync = (u8) rtl_get_bbreg(hw, ROFDM0_RXDETECTOR3, + MASKBYTE0); + rtlphy->framesync_c34 = rtl_get_bbreg(hw, ROFDM0_RXDETECTOR2, + MASKDWORD); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Default framesync (0x%x) = 0x%x\n", + ROFDM0_RXDETECTOR3, rtlphy->framesync); +} + +void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 txpwr_level; + long txpwr_dbm; + + txpwr_level = rtlphy->cur_cck_txpwridx; + txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, + txpwr_level); + txpwr_level = rtlphy->cur_ofdm24g_txpwridx; + if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > + txpwr_dbm) + txpwr_dbm = + rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, + txpwr_level); + txpwr_level = rtlphy->cur_ofdm24g_txpwridx; + if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, + txpwr_level) > txpwr_dbm) + txpwr_dbm = + rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, + txpwr_level); + *powerlevel = txpwr_dbm; +} + +static u8 _rtl8723be_phy_get_ratesection_intxpower_byrate(enum radio_path path, + u8 rate) +{ + u8 rate_section = 0; + + switch (rate) { + case DESC92C_RATE1M: + rate_section = 2; + break; + case DESC92C_RATE2M: + case DESC92C_RATE5_5M: + if (path == RF90_PATH_A) + rate_section = 3; + else if (path == RF90_PATH_B) + rate_section = 2; + break; + case DESC92C_RATE11M: + rate_section = 3; + break; + case DESC92C_RATE6M: + case DESC92C_RATE9M: + case DESC92C_RATE12M: + case DESC92C_RATE18M: + rate_section = 0; + break; + case DESC92C_RATE24M: + case DESC92C_RATE36M: + case DESC92C_RATE48M: + case DESC92C_RATE54M: + rate_section = 1; + break; + case DESC92C_RATEMCS0: + case DESC92C_RATEMCS1: + case DESC92C_RATEMCS2: + case DESC92C_RATEMCS3: + rate_section = 4; + break; + case DESC92C_RATEMCS4: + case DESC92C_RATEMCS5: + case DESC92C_RATEMCS6: + case DESC92C_RATEMCS7: + rate_section = 5; + break; + case DESC92C_RATEMCS8: + case DESC92C_RATEMCS9: + case DESC92C_RATEMCS10: + case DESC92C_RATEMCS11: + rate_section = 6; + break; + case DESC92C_RATEMCS12: + case DESC92C_RATEMCS13: + case DESC92C_RATEMCS14: + case DESC92C_RATEMCS15: + rate_section = 7; + break; + default: + RT_ASSERT(true, "Rate_Section is Illegal\n"); + break; + } + return rate_section; +} + +static u8 _rtl8723be_get_txpower_by_rate(struct ieee80211_hw *hw, + enum band_type band, + enum radio_path rfpath, u8 rate) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 shift = 0, rate_section, tx_num; + char tx_pwr_diff = 0; + + rate_section = _rtl8723be_phy_get_ratesection_intxpower_byrate(rfpath, + rate); + tx_num = RF_TX_NUM_NONIMPLEMENT; + + if (tx_num == RF_TX_NUM_NONIMPLEMENT) { + if (rate >= DESC92C_RATEMCS8 && rate <= DESC92C_RATEMCS15) + tx_num = RF_2TX; + else + tx_num = RF_1TX; + } + + switch (rate) { + case DESC92C_RATE6M: + case DESC92C_RATE24M: + case DESC92C_RATEMCS0: + case DESC92C_RATEMCS4: + case DESC92C_RATEMCS8: + case DESC92C_RATEMCS12: + shift = 0; + break; + case DESC92C_RATE1M: + case DESC92C_RATE2M: + case DESC92C_RATE9M: + case DESC92C_RATE36M: + case DESC92C_RATEMCS1: + case DESC92C_RATEMCS5: + case DESC92C_RATEMCS9: + case DESC92C_RATEMCS13: + shift = 8; + break; + case DESC92C_RATE5_5M: + case DESC92C_RATE12M: + case DESC92C_RATE48M: + case DESC92C_RATEMCS2: + case DESC92C_RATEMCS6: + case DESC92C_RATEMCS10: + case DESC92C_RATEMCS14: + shift = 16; + break; + case DESC92C_RATE11M: + case DESC92C_RATE18M: + case DESC92C_RATE54M: + case DESC92C_RATEMCS3: + case DESC92C_RATEMCS7: + case DESC92C_RATEMCS11: + case DESC92C_RATEMCS15: + shift = 24; + break; + default: + RT_ASSERT(true, "Rate_Section is Illegal\n"); + break; + } + tx_pwr_diff = (u8)(rtlphy->tx_power_by_rate_offset[band][rfpath][tx_num] + [rate_section] >> shift) & 0xff; + + return tx_pwr_diff; +} + +static u8 _rtl8723be_get_txpower_index(struct ieee80211_hw *hw, u8 path, + u8 rate, u8 bandwidth, u8 channel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 index = (channel - 1); + u8 txpower; + u8 power_diff_byrate = 0; + + if (channel > 14 || channel < 1) { + index = 0; + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "Illegal channel!\n"); + } + if (RTL8723E_RX_HAL_IS_CCK_RATE(rate)) + txpower = rtlefuse->txpwrlevel_cck[path][index]; + else if (DESC92C_RATE6M <= rate) + txpower = rtlefuse->txpwrlevel_ht40_1s[path][index]; + else + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "invalid rate\n"); + + if (DESC92C_RATE6M <= rate && rate <= DESC92C_RATE54M && + !RTL8723E_RX_HAL_IS_CCK_RATE(rate)) + txpower += rtlefuse->txpwr_legacyhtdiff[0][TX_1S]; + + if (bandwidth == HT_CHANNEL_WIDTH_20) { + if (DESC92C_RATEMCS0 <= rate && rate <= DESC92C_RATEMCS15) + txpower += rtlefuse->txpwr_ht20diff[0][TX_1S]; + if (DESC92C_RATEMCS8 <= rate && rate <= DESC92C_RATEMCS15) + txpower += rtlefuse->txpwr_ht20diff[0][TX_2S]; + } else if (bandwidth == HT_CHANNEL_WIDTH_20_40) { + if (DESC92C_RATEMCS0 <= rate && rate <= DESC92C_RATEMCS15) + txpower += rtlefuse->txpwr_ht40diff[0][TX_1S]; + if (DESC92C_RATEMCS8 <= rate && rate <= DESC92C_RATEMCS15) + txpower += rtlefuse->txpwr_ht40diff[0][TX_2S]; + } + if (rtlefuse->eeprom_regulatory != 2) + power_diff_byrate = _rtl8723be_get_txpower_by_rate(hw, + BAND_ON_2_4G, + path, rate); + + txpower += power_diff_byrate; + + if (txpower > MAX_POWER_INDEX) + txpower = MAX_POWER_INDEX; + + return txpower; +} + +static void _rtl8723be_phy_set_txpower_index(struct ieee80211_hw *hw, + u8 power_index, u8 path, u8 rate) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + if (path == RF90_PATH_A) { + switch (rate) { + case DESC92C_RATE1M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_CCK1_MCS32, + MASKBYTE1, power_index); + break; + case DESC92C_RATE2M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_B_CCK11_A_CCK2_11, + MASKBYTE1, power_index); + break; + case DESC92C_RATE5_5M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_B_CCK11_A_CCK2_11, + MASKBYTE2, power_index); + break; + case DESC92C_RATE11M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_B_CCK11_A_CCK2_11, + MASKBYTE3, power_index); + break; + case DESC92C_RATE6M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06, + MASKBYTE0, power_index); + break; + case DESC92C_RATE9M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06, + MASKBYTE1, power_index); + break; + case DESC92C_RATE12M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06, + MASKBYTE2, power_index); + break; + case DESC92C_RATE18M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE18_06, + MASKBYTE3, power_index); + break; + case DESC92C_RATE24M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24, + MASKBYTE0, power_index); + break; + case DESC92C_RATE36M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24, + MASKBYTE1, power_index); + break; + case DESC92C_RATE48M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24, + MASKBYTE2, power_index); + break; + case DESC92C_RATE54M: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_RATE54_24, + MASKBYTE3, power_index); + break; + case DESC92C_RATEMCS0: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00, + MASKBYTE0, power_index); + break; + case DESC92C_RATEMCS1: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00, + MASKBYTE1, power_index); + break; + case DESC92C_RATEMCS2: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00, + MASKBYTE2, power_index); + break; + case DESC92C_RATEMCS3: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS03_MCS00, + MASKBYTE3, power_index); + break; + case DESC92C_RATEMCS4: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04, + MASKBYTE0, power_index); + break; + case DESC92C_RATEMCS5: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04, + MASKBYTE1, power_index); + break; + case DESC92C_RATEMCS6: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04, + MASKBYTE2, power_index); + break; + case DESC92C_RATEMCS7: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS07_MCS04, + MASKBYTE3, power_index); + break; + case DESC92C_RATEMCS8: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08, + MASKBYTE0, power_index); + break; + case DESC92C_RATEMCS9: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08, + MASKBYTE1, power_index); + break; + case DESC92C_RATEMCS10: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08, + MASKBYTE2, power_index); + break; + case DESC92C_RATEMCS11: + rtl8723_phy_set_bb_reg(hw, RTXAGC_A_MCS11_MCS08, + MASKBYTE3, power_index); + break; + default: + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "Invalid Rate!!\n"); + break; + } + } else { + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Invalid RFPath!!\n"); + } +} + +void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) +{ + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 cck_rates[] = {DESC92C_RATE1M, DESC92C_RATE2M, + DESC92C_RATE5_5M, DESC92C_RATE11M}; + u8 ofdm_rates[] = {DESC92C_RATE6M, DESC92C_RATE9M, + DESC92C_RATE12M, DESC92C_RATE18M, + DESC92C_RATE24M, DESC92C_RATE36M, + DESC92C_RATE48M, DESC92C_RATE54M}; + u8 ht_rates_1t[] = {DESC92C_RATEMCS0, DESC92C_RATEMCS1, + DESC92C_RATEMCS2, DESC92C_RATEMCS3, + DESC92C_RATEMCS4, DESC92C_RATEMCS5, + DESC92C_RATEMCS6, DESC92C_RATEMCS7}; + u8 i, size; + u8 power_index; + + if (!rtlefuse->txpwr_fromeprom) + return; + + size = sizeof(cck_rates) / sizeof(u8); + for (i = 0; i < size; i++) { + power_index = _rtl8723be_get_txpower_index(hw, RF90_PATH_A, + cck_rates[i], + rtl_priv(hw)->phy.current_chan_bw, + channel); + _rtl8723be_phy_set_txpower_index(hw, power_index, RF90_PATH_A, + cck_rates[i]); + } + size = sizeof(ofdm_rates) / sizeof(u8); + for (i = 0; i < size; i++) { + power_index = _rtl8723be_get_txpower_index(hw, RF90_PATH_A, + ofdm_rates[i], + rtl_priv(hw)->phy.current_chan_bw, + channel); + _rtl8723be_phy_set_txpower_index(hw, power_index, RF90_PATH_A, + ofdm_rates[i]); + } + size = sizeof(ht_rates_1t) / sizeof(u8); + for (i = 0; i < size; i++) { + power_index = _rtl8723be_get_txpower_index(hw, RF90_PATH_A, + ht_rates_1t[i], + rtl_priv(hw)->phy.current_chan_bw, + channel); + _rtl8723be_phy_set_txpower_index(hw, power_index, RF90_PATH_A, + ht_rates_1t[i]); + } +} + +void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + enum io_type iotype; + + if (!is_hal_stop(rtlhal)) { + switch (operation) { + case SCAN_OPT_BACKUP: + iotype = IO_CMD_PAUSE_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + case SCAN_OPT_RESTORE: + iotype = IO_CMD_RESUME_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Unknown Scan Backup operation.\n"); + break; + } + } +} + +void rtl8723be_phy_set_bw_mode_callback(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u8 reg_bw_opmode; + u8 reg_prsr_rsc; + + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, + "Switch to %s bandwidth\n", + rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ? + "20MHz" : "40MHz"); + + if (is_hal_stop(rtlhal)) { + rtlphy->set_bwmode_inprogress = false; + return; + } + + reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE); + reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2); + + switch (rtlphy->current_chan_bw) { + case HT_CHANNEL_WIDTH_20: + reg_bw_opmode |= BW_OPMODE_20MHZ; + rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); + break; + case HT_CHANNEL_WIDTH_20_40: + reg_bw_opmode &= ~BW_OPMODE_20MHZ; + rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); + reg_prsr_rsc = (reg_prsr_rsc & 0x90) | + (mac->cur_40_prime_sc << 5); + rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); + break; + } + + switch (rtlphy->current_chan_bw) { + case HT_CHANNEL_WIDTH_20: + rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0); + rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0); + break; + case HT_CHANNEL_WIDTH_20_40: + rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1); + rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1); + rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND, + (mac->cur_40_prime_sc >> 1)); + rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc); + rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)), + (mac->cur_40_prime_sc == + HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); + break; + } + rtl8723be_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); + rtlphy->set_bwmode_inprogress = false; + RT_TRACE(rtlpriv, COMP_SCAN, DBG_LOUD, "\n"); +} + +void rtl8723be_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp_bw = rtlphy->current_chan_bw; + + if (rtlphy->set_bwmode_inprogress) + return; + rtlphy->set_bwmode_inprogress = true; + if ((!is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { + rtl8723be_phy_set_bw_mode_callback(hw); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "false driver sleep or unload\n"); + rtlphy->set_bwmode_inprogress = false; + rtlphy->current_chan_bw = tmp_bw; + } +} + +void rtl8723be_phy_sw_chnl_callback(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 delay; + + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, + "switch to channel%d\n", rtlphy->current_channel); + if (is_hal_stop(rtlhal)) + return; + do { + if (!rtlphy->sw_chnl_inprogress) + break; + if (!rtl8723be_phy_sw_chn_step_by_step(hw, + rtlphy->current_channel, + &rtlphy->sw_chnl_stage, + &rtlphy->sw_chnl_step, + &delay)) { + if (delay > 0) + mdelay(delay); + else + continue; + } else { + rtlphy->sw_chnl_inprogress = false; + } + break; + } while (true); + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "\n"); +} + +u8 rtl8723be_phy_sw_chnl(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + if (rtlphy->sw_chnl_inprogress) + return 0; + if (rtlphy->set_bwmode_inprogress) + return 0; + RT_ASSERT((rtlphy->current_channel <= 14), + "WIRELESS_MODE_G but channel>14"); + rtlphy->sw_chnl_inprogress = true; + rtlphy->sw_chnl_stage = 0; + rtlphy->sw_chnl_step = 0; + if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { + rtl8723be_phy_sw_chnl_callback(hw); + RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, + "sw_chnl_inprogress false schdule " + "workitem current channel %d\n", + rtlphy->current_channel); + rtlphy->sw_chnl_inprogress = false; + } else { + RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, + "sw_chnl_inprogress false driver sleep or" + " unload\n"); + rtlphy->sw_chnl_inprogress = false; + } + return 1; +} + +static bool rtl8723be_phy_sw_chn_step_by_step(struct ieee80211_hw *hw, + u8 channel, u8 *stage, + u8 *step, u32 *delay) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct swchnlcmd precommoncmd[MAX_PRECMD_CNT]; + u32 precommoncmdcnt; + struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT]; + u32 postcommoncmdcnt; + struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT]; + u32 rfdependcmdcnt; + struct swchnlcmd *currentcmd = NULL; + u8 rfpath; + u8 num_total_rfpath = rtlphy->num_total_rfpath; + + precommoncmdcnt = 0; + rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, + MAX_PRECMD_CNT, + CMDID_SET_TXPOWEROWER_LEVEL, + 0, 0, 0); + rtl8723_phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, + MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); + postcommoncmdcnt = 0; + rtl8723_phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, + MAX_POSTCMD_CNT, CMDID_END, + 0, 0, 0); + rfdependcmdcnt = 0; + + RT_ASSERT((channel >= 1 && channel <= 14), + "illegal channel for Zebra: %d\n", channel); + + rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, + MAX_RFDEPENDCMD_CNT, + CMDID_RF_WRITEREG, + RF_CHNLBW, channel, 10); + + rtl8723_phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, + MAX_RFDEPENDCMD_CNT, + CMDID_END, 0, 0, 0); + + do { + switch (*stage) { + case 0: + currentcmd = &precommoncmd[*step]; + break; + case 1: + currentcmd = &rfdependcmd[*step]; + break; + case 2: + currentcmd = &postcommoncmd[*step]; + break; + } + + if (currentcmd->cmdid == CMDID_END) { + if ((*stage) == 2) { + return true; + } else { + (*stage)++; + (*step) = 0; + continue; + } + } + + switch (currentcmd->cmdid) { + case CMDID_SET_TXPOWEROWER_LEVEL: + rtl8723be_phy_set_txpower_level(hw, channel); + break; + case CMDID_WRITEPORT_ULONG: + rtl_write_dword(rtlpriv, currentcmd->para1, + currentcmd->para2); + break; + case CMDID_WRITEPORT_USHORT: + rtl_write_word(rtlpriv, currentcmd->para1, + (u16) currentcmd->para2); + break; + case CMDID_WRITEPORT_UCHAR: + rtl_write_byte(rtlpriv, currentcmd->para1, + (u8) currentcmd->para2); + break; + case CMDID_RF_WRITEREG: + for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) { + rtlphy->rfreg_chnlval[rfpath] = + ((rtlphy->rfreg_chnlval[rfpath] & + 0xfffffc00) | currentcmd->para2); + + rtl_set_rfreg(hw, (enum radio_path)rfpath, + currentcmd->para1, + RFREG_OFFSET_MASK, + rtlphy->rfreg_chnlval[rfpath]); + } + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + + break; + } while (true); + + (*delay) = currentcmd->msdelay; + (*step)++; + return false; +} + +static u8 _rtl8723be_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb) +{ + u32 reg_eac, reg_e94, reg_e9c, reg_ea4; + u8 result = 0x00; + + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1c); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x30008c1c); + rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x8214032a); + rtl_set_bbreg(hw, 0xe3c, MASKDWORD, 0x28160000); + + rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x00462911); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000); + + mdelay(IQK_DELAY_TIME); + + reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); + reg_e94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD); + reg_e9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD); + reg_ea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD); + + if (!(reg_eac & BIT(28)) && + (((reg_e94 & 0x03FF0000) >> 16) != 0x142) && + (((reg_e9c & 0x03FF0000) >> 16) != 0x42)) + result |= 0x01; + return result; +} + +static bool phy_similarity_cmp(struct ieee80211_hw *hw, long result[][8], + u8 c1, u8 c2) +{ + u32 i, j, diff, simularity_bitmap, bound; + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + u8 final_candidate[2] = { 0xFF, 0xFF }; + bool bresult = true, is2t = IS_92C_SERIAL(rtlhal->version); + + if (is2t) + bound = 8; + else + bound = 4; + + simularity_bitmap = 0; + + for (i = 0; i < bound; i++) { + diff = (result[c1][i] > result[c2][i]) ? + (result[c1][i] - result[c2][i]) : + (result[c2][i] - result[c1][i]); + + if (diff > MAX_TOLERANCE) { + if ((i == 2 || i == 6) && !simularity_bitmap) { + if (result[c1][i] + result[c1][i + 1] == 0) + final_candidate[(i / 4)] = c2; + else if (result[c2][i] + result[c2][i + 1] == 0) + final_candidate[(i / 4)] = c1; + else + simularity_bitmap |= (1 << i); + } else { + simularity_bitmap |= (1 << i); + } + } + } + + if (simularity_bitmap == 0) { + for (i = 0; i < (bound / 4); i++) { + if (final_candidate[i] != 0xFF) { + for (j = i * 4; j < (i + 1) * 4 - 2; j++) + result[3][j] = + result[final_candidate[i]][j]; + bresult = false; + } + } + return bresult; + } else if (!(simularity_bitmap & 0x0F)) { + for (i = 0; i < 4; i++) + result[3][i] = result[c1][i]; + return false; + } else if (!(simularity_bitmap & 0xF0) && is2t) { + for (i = 4; i < 8; i++) + result[3][i] = result[c1][i]; + return false; + } else { + return false; + } +} + +static void _rtl8723be_phy_iq_calibrate(struct ieee80211_hw *hw, + long result[][8], u8 t, bool is2t) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 i; + u8 patha_ok; + u32 adda_reg[IQK_ADDA_REG_NUM] = { + 0x85c, 0xe6c, 0xe70, 0xe74, + 0xe78, 0xe7c, 0xe80, 0xe84, + 0xe88, 0xe8c, 0xed0, 0xed4, + 0xed8, 0xedc, 0xee0, 0xeec + }; + + u32 iqk_mac_reg[IQK_MAC_REG_NUM] = { + 0x522, 0x550, 0x551, 0x040 + }; + u32 iqk_bb_reg[IQK_BB_REG_NUM] = { + ROFDM0_TRXPATHENABLE, ROFDM0_TRMUXPAR, + RFPGA0_XCD_RFINTERFACESW, 0xb68, 0xb6c, + 0x870, 0x860, + 0x864, 0x800 + }; + const u32 retrycount = 2; + u32 path_sel_bb, path_sel_rf; + u8 tmp_reg_c50, tmp_reg_c58; + + tmp_reg_c50 = rtl_get_bbreg(hw, 0xc50, MASKBYTE0); + tmp_reg_c58 = rtl_get_bbreg(hw, 0xc58, MASKBYTE0); + + if (t == 0) { + rtl8723_save_adda_registers(hw, adda_reg, + rtlphy->adda_backup, 16); + rtl8723_phy_save_mac_registers(hw, iqk_mac_reg, + rtlphy->iqk_mac_backup); + rtl8723_save_adda_registers(hw, iqk_bb_reg, + rtlphy->iqk_bb_backup, + IQK_BB_REG_NUM); + } + rtl8723_phy_path_adda_on(hw, adda_reg, true, is2t); + if (t == 0) { + rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw, + RFPGA0_XA_HSSIPARAMETER1, + BIT(8)); + } + if (!rtlphy->rfpi_enable) + rtl8723_phy_pi_mode_switch(hw, true); + + path_sel_bb = rtl_get_bbreg(hw, 0x948, MASKDWORD); + path_sel_rf = rtl_get_rfreg(hw, RF90_PATH_A, 0xb0, 0xfffff); + + /*BB Setting*/ + rtl_set_bbreg(hw, 0x800, BIT(24), 0x00); + rtl_set_bbreg(hw, 0xc04, MASKDWORD, 0x03a05600); + rtl_set_bbreg(hw, 0xc08, MASKDWORD, 0x000800e4); + rtl_set_bbreg(hw, 0x874, MASKDWORD, 0x22204000); + + rtl_set_bbreg(hw, 0x870, BIT(10), 0x01); + rtl_set_bbreg(hw, 0x870, BIT(26), 0x01); + rtl_set_bbreg(hw, 0x860, BIT(10), 0x00); + rtl_set_bbreg(hw, 0x864, BIT(10), 0x00); + + if (is2t) + rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASKDWORD, 0x10000); + rtl8723_phy_mac_setting_calibration(hw, iqk_mac_reg, + rtlphy->iqk_mac_backup); + rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x0f600000); + + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); + rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00); + rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x81004800); + for (i = 0; i < retrycount; i++) { + patha_ok = _rtl8723be_phy_path_a_iqk(hw, is2t); + if (patha_ok == 0x01) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Path A Tx IQK Success!!\n"); + result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & + 0x3FF0000) >> 16; + break; + } + } + + if (0 == patha_ok) + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Path A IQK Success!!\n"); + if (is2t) { + rtl8723_phy_path_a_standby(hw); + rtl8723_phy_path_adda_on(hw, adda_reg, false, is2t); + } + + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0); + + if (t != 0) { + if (!rtlphy->rfpi_enable) + rtl8723_phy_pi_mode_switch(hw, false); + rtl8723_phy_reload_adda_registers(hw, adda_reg, + rtlphy->adda_backup, 16); + rtl8723_phy_reload_mac_registers(hw, iqk_mac_reg, + rtlphy->iqk_mac_backup); + rtl8723_phy_reload_adda_registers(hw, iqk_bb_reg, + rtlphy->iqk_bb_backup, + IQK_BB_REG_NUM); + + rtl_set_bbreg(hw, 0x948, MASKDWORD, path_sel_bb); + rtl_set_rfreg(hw, RF90_PATH_B, 0xb0, 0xfffff, path_sel_rf); + + rtl_set_bbreg(hw, 0xc50, MASKBYTE0, 0x50); + rtl_set_bbreg(hw, 0xc50, MASKBYTE0, tmp_reg_c50); + if (is2t) { + rtl_set_bbreg(hw, 0xc58, MASKBYTE0, 0x50); + rtl_set_bbreg(hw, 0xc58, MASKBYTE0, tmp_reg_c58); + } + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x01008c00); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x01008c00); + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "8723be IQK Finish!!\n"); +} + +static void _rtl8723be_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmpreg; + u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal; + + tmpreg = rtl_read_byte(rtlpriv, 0xd03); + + if ((tmpreg & 0x70) != 0) + rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F); + else + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); + + if ((tmpreg & 0x70) != 0) { + rf_a_mode = rtl_get_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS); + + if (is2t) + rf_b_mode = rtl_get_rfreg(hw, RF90_PATH_B, 0x00, + MASK12BITS); + + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, + (rf_a_mode & 0x8FFFF) | 0x10000); + + if (is2t) + rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, + (rf_b_mode & 0x8FFFF) | 0x10000); + } + lc_cal = rtl_get_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS); + + rtl_set_rfreg(hw, RF90_PATH_A, 0xb0, RFREG_OFFSET_MASK, 0xdfbe0); + rtl_set_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS, 0x8c0a); + + mdelay(100); + + rtl_set_rfreg(hw, RF90_PATH_A, 0xb0, RFREG_OFFSET_MASK, 0xdffe0); + + if ((tmpreg & 0x70) != 0) { + rtl_write_byte(rtlpriv, 0xd03, tmpreg); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, rf_a_mode); + + if (is2t) + rtl_set_rfreg(hw, RF90_PATH_B, 0x00, + MASK12BITS, rf_b_mode); + } else { + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "\n"); +} + +static void _rtl8723be_phy_set_rfpath_switch(struct ieee80211_hw *hw, + bool bmain, bool is2t) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "\n"); + + if (is_hal_stop(rtlhal)) { + u8 u1btmp; + u1btmp = rtl_read_byte(rtlpriv, REG_LEDCFG0); + rtl_write_byte(rtlpriv, REG_LEDCFG0, u1btmp | BIT(7)); + rtl_set_bbreg(hw, RFPGA0_XAB_RFPARAMETER, BIT(13), 0x01); + } + if (is2t) { + if (bmain) + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, + BIT(5) | BIT(6), 0x1); + else + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, + BIT(5) | BIT(6), 0x2); + } else { + rtl_set_bbreg(hw, RFPGA0_XAB_RFINTERFACESW, BIT(8) | BIT(9), 0); + rtl_set_bbreg(hw, 0x914, MASKLWORD, 0x0201); + + /* We use the RF definition of MAIN and AUX, + * left antenna and right antenna repectively. + * Default output at AUX. + */ + if (bmain) { + rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, + BIT(14) | BIT(13) | BIT(12), 0); + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, + BIT(5) | BIT(4) | BIT(3), 0); + if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) + rtl_set_bbreg(hw, CONFIG_RAM64X16, BIT(31), 0); + } else { + rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, + BIT(14) | BIT(13) | BIT(12), 1); + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, + BIT(5) | BIT(4) | BIT(3), 1); + if (rtlefuse->antenna_div_type == CGCS_RX_HW_ANTDIV) + rtl_set_bbreg(hw, CONFIG_RAM64X16, BIT(31), 1); + } + } +} + +#undef IQK_ADDA_REG_NUM +#undef IQK_DELAY_TIME + +void rtl8723be_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + long result[4][8]; + u8 i, final_candidate; + bool patha_ok, pathb_ok; + long reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4, + reg_ecc, reg_tmp = 0; + bool is12simular, is13simular, is23simular; + u32 iqk_bb_reg[9] = { + ROFDM0_XARXIQIMBALANCE, + ROFDM0_XBRXIQIMBALANCE, + ROFDM0_ECCATHRESHOLD, + ROFDM0_AGCRSSITABLE, + ROFDM0_XATXIQIMBALANCE, + ROFDM0_XBTXIQIMBALANCE, + ROFDM0_XCTXAFE, + ROFDM0_XDTXAFE, + ROFDM0_RXIQEXTANTA + }; + + if (recovery) { + rtl8723_phy_reload_adda_registers(hw, iqk_bb_reg, + rtlphy->iqk_bb_backup, 9); + return; + } + + for (i = 0; i < 8; i++) { + result[0][i] = 0; + result[1][i] = 0; + result[2][i] = 0; + result[3][i] = 0; + } + final_candidate = 0xff; + patha_ok = false; + pathb_ok = false; + is12simular = false; + is23simular = false; + is13simular = false; + for (i = 0; i < 3; i++) { + if (get_rf_type(rtlphy) == RF_2T2R) + _rtl8723be_phy_iq_calibrate(hw, result, i, true); + else + _rtl8723be_phy_iq_calibrate(hw, result, i, false); + if (i == 1) { + is12simular = phy_similarity_cmp(hw, result, 0, 1); + if (is12simular) { + final_candidate = 0; + break; + } + } + if (i == 2) { + is13simular = phy_similarity_cmp(hw, result, 0, 2); + if (is13simular) { + final_candidate = 0; + break; + } + is23simular = phy_similarity_cmp(hw, result, 1, 2); + if (is23simular) { + final_candidate = 1; + } else { + for (i = 0; i < 8; i++) + reg_tmp += result[3][i]; + + if (reg_tmp != 0) + final_candidate = 3; + else + final_candidate = 0xFF; + } + } + } + for (i = 0; i < 4; i++) { + reg_e94 = result[i][0]; + reg_e9c = result[i][1]; + reg_ea4 = result[i][2]; + reg_eac = result[i][3]; + reg_eb4 = result[i][4]; + reg_ebc = result[i][5]; + reg_ec4 = result[i][6]; + reg_ecc = result[i][7]; + } + if (final_candidate != 0xff) { + reg_e94 = result[final_candidate][0]; + rtlphy->reg_e94 = reg_e94; + reg_e9c = result[final_candidate][1]; + rtlphy->reg_e9c = reg_e9c; + reg_ea4 = result[final_candidate][2]; + reg_eac = result[final_candidate][3]; + reg_eb4 = result[final_candidate][4]; + rtlphy->reg_eb4 = reg_eb4; + reg_ebc = result[final_candidate][5]; + rtlphy->reg_ebc = reg_ebc; + reg_ec4 = result[final_candidate][6]; + reg_ecc = result[final_candidate][7]; + patha_ok = true; + pathb_ok = true; + } else { + rtlphy->reg_e94 = 0x100; + rtlphy->reg_eb4 = 0x100; + rtlphy->reg_e9c = 0x0; + rtlphy->reg_ebc = 0x0; + } + if (reg_e94 != 0) /*&&(reg_ea4 != 0) */ + rtl8723_phy_path_a_fill_iqk_matrix(hw, patha_ok, result, + final_candidate, + (reg_ea4 == 0)); + if (final_candidate != 0xFF) { + for (i = 0; i < IQK_MATRIX_REG_NUM; i++) + rtlphy->iqk_matrix[0].value[0][i] = + result[final_candidate][i]; + rtlphy->iqk_matrix[0].iqk_done = true; + } + rtl8723_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 9); +} + +void rtl8723be_phy_lc_calibrate(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = &(rtlpriv->rtlhal); + u32 timeout = 2000, timecount = 0; + + while (rtlpriv->mac80211.act_scanning && timecount < timeout) { + udelay(50); + timecount += 50; + } + + rtlphy->lck_inprogress = true; + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "LCK:Start!!! currentband %x delay %d ms\n", + rtlhal->current_bandtype, timecount); + + _rtl8723be_phy_lc_calibrate(hw, false); + + rtlphy->lck_inprogress = false; +} + +void rtl23b_phy_ap_calibrate(struct ieee80211_hw *hw, char delta) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + if (rtlphy->apk_done) + return; + + return; +} + +void rtl8723be_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) +{ + _rtl8723be_phy_set_rfpath_switch(hw, bmain, false); +} + +static void rtl8723be_phy_set_io(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "--->Cmd(%#x), set_io_inprogress(%d)\n", + rtlphy->current_io_type, rtlphy->set_io_inprogress); + switch (rtlphy->current_io_type) { + case IO_CMD_RESUME_DM_BY_SCAN: + rtlpriv->dm_digtable.cur_igvalue = + rtlphy->initgain_backup.xaagccore1; + /*rtl92c_dm_write_dig(hw);*/ + rtl8723be_phy_set_txpower_level(hw, rtlphy->current_channel); + rtl_set_bbreg(hw, RCCK0_CCA, 0xff0000, 0x83); + break; + case IO_CMD_PAUSE_DM_BY_SCAN: + rtlphy->initgain_backup.xaagccore1 = + rtlpriv->dm_digtable.cur_igvalue; + rtlpriv->dm_digtable.cur_igvalue = 0x17; + rtl_set_bbreg(hw, RCCK0_CCA, 0xff0000, 0x40); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + rtlphy->set_io_inprogress = false; + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "(%#x)\n", rtlphy->current_io_type); +} + +bool rtl8723be_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + bool postprocessing = false; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "-->IO Cmd(%#x), set_io_inprogress(%d)\n", + iotype, rtlphy->set_io_inprogress); + do { + switch (iotype) { + case IO_CMD_RESUME_DM_BY_SCAN: + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "[IO CMD] Resume DM after scan.\n"); + postprocessing = true; + break; + case IO_CMD_PAUSE_DM_BY_SCAN: + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "[IO CMD] Pause DM before scan.\n"); + postprocessing = true; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + } while (false); + if (postprocessing && !rtlphy->set_io_inprogress) { + rtlphy->set_io_inprogress = true; + rtlphy->current_io_type = iotype; + } else { + return false; + } + rtl8723be_phy_set_io(hw); + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "IO Type(%#x)\n", iotype); + return true; +} + +static void rtl8723be_phy_set_rf_on(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); +} + +static void _rtl8723be_phy_set_rf_sleep(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); + rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); +} + +static bool _rtl8723be_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + bool bresult = true; + u8 i, queue_id; + struct rtl8192_tx_ring *ring = NULL; + + switch (rfpwr_state) { + case ERFON: + if ((ppsc->rfpwr_state == ERFOFF) && + RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { + bool rtstatus; + u32 initialize_count = 0; + do { + initialize_count++; + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "IPS Set eRf nic enable\n"); + rtstatus = rtl_ps_enable_nic(hw); + } while (!rtstatus && (initialize_count < 10)); + RT_CLEAR_PS_LEVEL(ppsc, + RT_RF_OFF_LEVL_HALT_NIC); + } else { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "Set ERFON sleeped:%d ms\n", + jiffies_to_msecs(jiffies - + ppsc->last_sleep_jiffies)); + ppsc->last_awake_jiffies = jiffies; + rtl8723be_phy_set_rf_on(hw); + } + if (mac->link_state == MAC80211_LINKED) + rtlpriv->cfg->ops->led_control(hw, LED_CTL_LINK); + else + rtlpriv->cfg->ops->led_control(hw, LED_CTL_NO_LINK); + break; + case ERFOFF: + for (queue_id = 0, i = 0; + queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { + ring = &pcipriv->dev.tx_ring[queue_id]; + if (skb_queue_len(&ring->queue) == 0) { + queue_id++; + continue; + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "eRf Off/Sleep: %d times " + "TcbBusyQueue[%d] =%d before " + "doze!\n", (i + 1), queue_id, + skb_queue_len(&ring->queue)); + + udelay(10); + i++; + } + if (i >= MAX_DOZE_WAITING_TIMES_9x) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "\n ERFSLEEP: %d times " + "TcbBusyQueue[%d] = %d !\n", + MAX_DOZE_WAITING_TIMES_9x, + queue_id, + skb_queue_len(&ring->queue)); + break; + } + } + + if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "IPS Set eRf nic disable\n"); + rtl_ps_disable_nic(hw); + RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); + } else { + if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) { + rtlpriv->cfg->ops->led_control(hw, + LED_CTL_NO_LINK); + } else { + rtlpriv->cfg->ops->led_control(hw, + LED_CTL_POWER_OFF); + } + } + break; + case ERFSLEEP: + if (ppsc->rfpwr_state == ERFOFF) + break; + for (queue_id = 0, i = 0; + queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { + ring = &pcipriv->dev.tx_ring[queue_id]; + if (skb_queue_len(&ring->queue) == 0) { + queue_id++; + continue; + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "eRf Off/Sleep: %d times " + "TcbBusyQueue[%d] =%d before " + "doze!\n", (i + 1), queue_id, + skb_queue_len(&ring->queue)); + + udelay(10); + i++; + } + if (i >= MAX_DOZE_WAITING_TIMES_9x) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "\n ERFSLEEP: %d times " + "TcbBusyQueue[%d] = %d !\n", + MAX_DOZE_WAITING_TIMES_9x, + queue_id, + skb_queue_len(&ring->queue)); + break; + } + } + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "Set ERFSLEEP awaked:%d ms\n", + jiffies_to_msecs(jiffies - + ppsc->last_awake_jiffies)); + ppsc->last_sleep_jiffies = jiffies; + _rtl8723be_phy_set_rf_sleep(hw); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + bresult = false; + break; + } + if (bresult) + ppsc->rfpwr_state = rfpwr_state; + return bresult; +} + +bool rtl8723be_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state) +{ + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + bool bresult = false; + + if (rfpwr_state == ppsc->rfpwr_state) + return bresult; + bresult = _rtl8723be_phy_set_rf_power_state(hw, rfpwr_state); + return bresult; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h new file mode 100644 index 00000000000..444ef95bb6a --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h @@ -0,0 +1,217 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_PHY_H__ +#define __RTL8723BE_PHY_H__ + +/*It must always set to 4, otherwise read efuse table secquence will be wrong.*/ +#define MAX_TX_COUNT 4 +#define TX_1S 0 +#define TX_2S 1 + +#define MAX_POWER_INDEX 0x3F + +#define MAX_PRECMD_CNT 16 +#define MAX_RFDEPENDCMD_CNT 16 +#define MAX_POSTCMD_CNT 16 + +#define MAX_DOZE_WAITING_TIMES_9x 64 + +#define RT_CANNOT_IO(hw) false +#define HIGHPOWER_RADIOA_ARRAYLEN 22 + +#define IQK_ADDA_REG_NUM 16 +#define IQK_BB_REG_NUM 9 +#define MAX_TOLERANCE 5 +#define IQK_DELAY_TIME 10 +#define index_mapping_NUM 15 + +#define APK_BB_REG_NUM 5 +#define APK_AFE_REG_NUM 16 +#define APK_CURVE_REG_NUM 4 +#define PATH_NUM 1 + +#define LOOP_LIMIT 5 +#define MAX_STALL_TIME 50 +#define ANTENNADIVERSITYVALUE 0x80 +#define MAX_TXPWR_IDX_NMODE_92S 63 +#define RESET_CNT_LIMIT 3 + +#define IQK_ADDA_REG_NUM 16 +#define IQK_MAC_REG_NUM 4 + +#define RF6052_MAX_PATH 2 + +#define CT_OFFSET_MAC_ADDR 0X16 + +#define CT_OFFSET_CCK_TX_PWR_IDX 0x5A +#define CT_OFFSET_HT401S_TX_PWR_IDX 0x60 +#define CT_OFFSET_HT402S_TX_PWR_IDX_DIFF 0x66 +#define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69 +#define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C + +#define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F +#define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72 + +#define CT_OFFSET_CHANNEL_PLAH 0x75 +#define CT_OFFSET_THERMAL_METER 0x78 +#define CT_OFFSET_RF_OPTION 0x79 +#define CT_OFFSET_VERSION 0x7E +#define CT_OFFSET_CUSTOMER_ID 0x7F + +#define RTL92C_MAX_PATH_NUM 2 + +enum hw90_block_e { + HW90_BLOCK_MAC = 0, + HW90_BLOCK_PHY0 = 1, + HW90_BLOCK_PHY1 = 2, + HW90_BLOCK_RF = 3, + HW90_BLOCK_MAXIMUM = 4, +}; + +enum baseband_config_type { + BASEBAND_CONFIG_PHY_REG = 0, + BASEBAND_CONFIG_AGC_TAB = 1, +}; + +enum ra_offset_area { + RA_OFFSET_LEGACY_OFDM1, + RA_OFFSET_LEGACY_OFDM2, + RA_OFFSET_HT_OFDM1, + RA_OFFSET_HT_OFDM2, + RA_OFFSET_HT_OFDM3, + RA_OFFSET_HT_OFDM4, + RA_OFFSET_HT_CCK, +}; + +enum antenna_path { + ANTENNA_NONE, + ANTENNA_D, + ANTENNA_C, + ANTENNA_CD, + ANTENNA_B, + ANTENNA_BD, + ANTENNA_BC, + ANTENNA_BCD, + ANTENNA_A, + ANTENNA_AD, + ANTENNA_AC, + ANTENNA_ACD, + ANTENNA_AB, + ANTENNA_ABD, + ANTENNA_ABC, + ANTENNA_ABCD +}; + +struct r_antenna_select_ofdm { + u32 r_tx_antenna:4; + u32 r_ant_l:4; + u32 r_ant_non_ht:4; + u32 r_ant_ht1:4; + u32 r_ant_ht2:4; + u32 r_ant_ht_s1:4; + u32 r_ant_non_ht_s1:4; + u32 ofdm_txsc:2; + u32 reserved:2; +}; + +struct r_antenna_select_cck { + u8 r_cckrx_enable_2:2; + u8 r_cckrx_enable:2; + u8 r_ccktx_enable:4; +}; + + +struct efuse_contents { + u8 mac_addr[ETH_ALEN]; + u8 cck_tx_power_idx[6]; + u8 ht40_1s_tx_power_idx[6]; + u8 ht40_2s_tx_power_idx_diff[3]; + u8 ht20_tx_power_idx_diff[3]; + u8 ofdm_tx_power_idx_diff[3]; + u8 ht40_max_power_offset[3]; + u8 ht20_max_power_offset[3]; + u8 channel_plan; + u8 thermal_meter; + u8 rf_option[5]; + u8 version; + u8 oem_id; + u8 regulatory; +}; + +struct tx_power_struct { + u8 cck[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 ht40_1s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 ht40_2s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 ht20_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 legacy_ht_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 legacy_ht_txpowerdiff; + u8 groupht20[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 groupht40[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 pwrgroup_cnt; + u32 mcs_original_offset[4][16]; +}; + +enum _ANT_DIV_TYPE { + NO_ANTDIV = 0xFF, + CG_TRX_HW_ANTDIV = 0x01, + CGCS_RX_HW_ANTDIV = 0x02, + FIXED_HW_ANTDIV = 0x03, + CG_TRX_SMART_ANTDIV = 0x04, + CGCS_RX_SW_ANTDIV = 0x05, +}; + +u32 rtl8723be_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 regaddr, u32 bitmask); +void rtl8723be_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 regaddr, u32 bitmask, u32 data); +bool rtl8723be_phy_mac_config(struct ieee80211_hw *hw); +bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw); +bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw); +void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw, + u8 channel); +void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw, + u8 operation); +void rtl8723be_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +void rtl8723be_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +void rtl8723be_phy_sw_chnl_callback(struct ieee80211_hw *hw); +u8 rtl8723be_phy_sw_chnl(struct ieee80211_hw *hw); +void rtl8723be_phy_iq_calibrate(struct ieee80211_hw *hw, + bool b_recovery); +void rtl23b_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); +void rtl8723be_phy_lc_calibrate(struct ieee80211_hw *hw); +void rtl8723be_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); +bool rtl8723be_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +bool rtl8723be_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); +bool rtl8723be_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c new file mode 100644 index 00000000000..b5167e73fec --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.c @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "pwrseqcmd.h" +#include "pwrseq.h" + + +/* drivers should parse below arrays and do the corresponding actions */ +/*3 Power on Array*/ +struct wlan_pwr_cfg rtl8723B_power_on_flow[RTL8723B_TRANS_CARDEMU_TO_ACT_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_CARDEMU_TO_ACT + RTL8723B_TRANS_END +}; + +/*3Radio off GPIO Array */ +struct wlan_pwr_cfg rtl8723B_radio_off_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_ACT_TO_CARDEMU + RTL8723B_TRANS_END +}; + +/*3Card Disable Array*/ +struct wlan_pwr_cfg rtl8723B_card_disable_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_ACT_TO_CARDEMU + RTL8723B_TRANS_CARDEMU_TO_CARDDIS + RTL8723B_TRANS_END +}; + +/*3 Card Enable Array*/ +struct wlan_pwr_cfg rtl8723B_card_enable_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_CARDDIS_TO_CARDEMU + RTL8723B_TRANS_CARDEMU_TO_ACT + RTL8723B_TRANS_END +}; + +/*3Suspend Array*/ +struct wlan_pwr_cfg rtl8723B_suspend_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_ACT_TO_CARDEMU + RTL8723B_TRANS_CARDEMU_TO_SUS + RTL8723B_TRANS_END +}; + +/*3 Resume Array*/ +struct wlan_pwr_cfg rtl8723B_resume_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_SUS_TO_CARDEMU + RTL8723B_TRANS_CARDEMU_TO_ACT + RTL8723B_TRANS_END +}; + +/*3HWPDN Array*/ +struct wlan_pwr_cfg rtl8723B_hwpdn_flow[RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS + + RTL8723B_TRANS_END_STEPS] = { + RTL8723B_TRANS_ACT_TO_CARDEMU + RTL8723B_TRANS_CARDEMU_TO_PDN + RTL8723B_TRANS_END +}; + +/*3 Enter LPS */ +struct wlan_pwr_cfg rtl8723B_enter_lps_flow[RTL8723B_TRANS_ACT_TO_LPS_STEPS + + RTL8723B_TRANS_END_STEPS] = { + /*FW behavior*/ + RTL8723B_TRANS_ACT_TO_LPS + RTL8723B_TRANS_END +}; + +/*3 Leave LPS */ +struct wlan_pwr_cfg rtl8723B_leave_lps_flow[RTL8723B_TRANS_LPS_TO_ACT_STEPS + + RTL8723B_TRANS_END_STEPS] = { + /*FW behavior*/ + RTL8723B_TRANS_LPS_TO_ACT + RTL8723B_TRANS_END +}; diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h new file mode 100644 index 00000000000..a62f43ed8d3 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseq.h @@ -0,0 +1,304 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_PWRSEQ_H__ +#define __RTL8723BE_PWRSEQ_H__ + +/* Check document WM-20130425-JackieLau-RTL8723B_Power_Architecture v05.vsd + * There are 6 HW Power States: + * 0: POFF--Power Off + * 1: PDN--Power Down + * 2: CARDEMU--Card Emulation + * 3: ACT--Active Mode + * 4: LPS--Low Power State + * 5: SUS--Suspend + * + * The transition from different states are defined below + * TRANS_CARDEMU_TO_ACT + * TRANS_ACT_TO_CARDEMU + * TRANS_CARDEMU_TO_SUS + * TRANS_SUS_TO_CARDEMU + * TRANS_CARDEMU_TO_PDN + * TRANS_ACT_TO_LPS + * TRANS_LPS_TO_ACT + * + * TRANS_END + */ +#define RTL8723B_TRANS_CARDEMU_TO_ACT_STEPS 23 +#define RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS 15 +#define RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS 15 +#define RTL8723B_TRANS_SUS_TO_CARDEMU_STEPS 15 +#define RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS 15 +#define RTL8723B_TRANS_PDN_TO_CARDEMU_STEPS 15 +#define RTL8723B_TRANS_ACT_TO_LPS_STEPS 15 +#define RTL8723B_TRANS_LPS_TO_ACT_STEPS 15 +#define RTL8723B_TRANS_END_STEPS 1 + +#define RTL8723B_TRANS_CARDEMU_TO_ACT \ + {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + {0x0067, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \ + {0x0001, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_DELAY, 1, PWRSEQ_DELAY_MS}, \ + {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)|BIT(2)), 0}, \ + {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0) , 0}, \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)}, \ + {0x0075, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0) , BIT(0)}, \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0}, \ + {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), BIT(6)}, \ + {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + {0x0063, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + {0x0062, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \ + {0x0058, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + {0x005A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + {0x0068, PWR_CUT_TESTCHIP_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3), BIT(3)}, \ + {0x0069, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), BIT(6)}, + +#define RTL8723B_TRANS_ACT_TO_CARDEMU \ + {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, \ + {0x004F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \ + {0x0049, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), 0}, \ + {0x0010, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6), 0}, \ + {0x0000, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \ + PWR_CMD_WRITE, BIT(5), BIT(5)}, \ + {0x0020, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \ + PWR_CMD_WRITE, BIT(0), 0}, + +#define RTL8723B_TRANS_CARDEMU_TO_SUS \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4) | BIT(3), (BIT(4) | BIT(3))}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, PWR_BASEADDR_MAC, \ + PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3)}, \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, \ + {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) | BIT(4)},\ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0}, + +#define RTL8723B_TRANS_SUS_TO_CARDEMU \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, + +#define RTL8723B_TRANS_CARDEMU_TO_CARDDIS \ + {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x20}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK | PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), BIT(2)}, \ + {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 1}, \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0}, + +#define RTL8723B_TRANS_CARDDIS_TO_CARDEMU \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3) | BIT(7), 0}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, \ + {0x004A, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \ + {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, + +#define RTL8723B_TRANS_CARDEMU_TO_PDN \ + {0x0023, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), BIT(4)}, \ + {0x0007, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_SDIO_MSK | PWR_INTF_USB_MSK, PWR_BASEADDR_MAC, \ + PWR_CMD_WRITE, 0xFF, 0x20}, \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)}, + +#define RTL8723B_TRANS_PDN_TO_CARDEMU \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0}, + +#define RTL8723B_TRANS_ACT_TO_LPS \ + {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \ + {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \ + {0x05F8, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US}, \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \ + {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x03}, \ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \ + {0x0093, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x00}, \ + {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)}, + +#define RTL8723B_TRANS_LPS_TO_ACT \ + {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, 0xFF, 0x84}, \ + {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, \ + {0x0361, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, \ + {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \ + {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), 0}, \ + {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6)|BIT(7), 0}, \ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1) | BIT(0), BIT(1) | BIT(0)}, \ + {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, + +#define RTL8723B_TRANS_END \ + {0xFFFF, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, 0, \ + PWR_CMD_END, 0, 0}, + +extern struct wlan_pwr_cfg rtl8723B_power_on_flow + [RTL8723B_TRANS_CARDEMU_TO_ACT_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_radio_off_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_card_disable_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_card_enable_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_suspend_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_resume_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_SUS_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_hwpdn_flow + [RTL8723B_TRANS_ACT_TO_CARDEMU_STEPS + + RTL8723B_TRANS_CARDEMU_TO_PDN_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_enter_lps_flow + [RTL8723B_TRANS_ACT_TO_LPS_STEPS + + RTL8723B_TRANS_END_STEPS]; +extern struct wlan_pwr_cfg rtl8723B_leave_lps_flow + [RTL8723B_TRANS_LPS_TO_ACT_STEPS + + RTL8723B_TRANS_END_STEPS]; + +/* RTL8723 Power Configuration CMDs for PCIe interface */ +#define RTL8723_NIC_PWR_ON_FLOW rtl8723B_power_on_flow +#define RTL8723_NIC_RF_OFF_FLOW rtl8723B_radio_off_flow +#define RTL8723_NIC_DISABLE_FLOW rtl8723B_card_disable_flow +#define RTL8723_NIC_ENABLE_FLOW rtl8723B_card_enable_flow +#define RTL8723_NIC_SUSPEND_FLOW rtl8723B_suspend_flow +#define RTL8723_NIC_RESUME_FLOW rtl8723B_resume_flow +#define RTL8723_NIC_PDN_FLOW rtl8723B_hwpdn_flow +#define RTL8723_NIC_LPS_ENTER_FLOW rtl8723B_enter_lps_flow +#define RTL8723_NIC_LPS_LEAVE_FLOW rtl8723B_leave_lps_flow + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c new file mode 100644 index 00000000000..e4a507a756f --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.c @@ -0,0 +1,140 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "pwrseq.h" + +/* Description: + * This routine deal with the Power Configuration CMDs + * parsing for RTL8723/RTL8188E Series IC. + * Assumption: + * We should follow specific format which was released from HW SD. + * + * 2011.07.07, added by Roger. + */ +bool rtlbe_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version, + u8 fab_version, u8 interface_type, + struct wlan_pwr_cfg pwrcfgcmd[]) + +{ + struct wlan_pwr_cfg pwr_cfg_cmd = {0}; + bool b_polling_bit = false; + u32 ary_idx = 0; + u8 value = 0; + u32 offset = 0; + u32 polling_count = 0; + u32 max_polling_cnt = 5000; + + do { + pwr_cfg_cmd = pwrcfgcmd[ary_idx]; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtlbe_hal_pwrseqcmdparsing(): " + "offset(%#x),cut_msk(%#x), fab_msk(%#x)," + "interface_msk(%#x), base(%#x), " + "cmd(%#x), msk(%#x), value(%#x)\n", + GET_PWR_CFG_OFFSET(pwr_cfg_cmd), + GET_PWR_CFG_CUT_MASK(pwr_cfg_cmd), + GET_PWR_CFG_FAB_MASK(pwr_cfg_cmd), + GET_PWR_CFG_INTF_MASK(pwr_cfg_cmd), + GET_PWR_CFG_BASE(pwr_cfg_cmd), + GET_PWR_CFG_CMD(pwr_cfg_cmd), + GET_PWR_CFG_MASK(pwr_cfg_cmd), + GET_PWR_CFG_VALUE(pwr_cfg_cmd)); + + if ((GET_PWR_CFG_FAB_MASK(pwr_cfg_cmd)&fab_version) && + (GET_PWR_CFG_CUT_MASK(pwr_cfg_cmd)&cut_version) && + (GET_PWR_CFG_INTF_MASK(pwr_cfg_cmd)&interface_type)) { + switch (GET_PWR_CFG_CMD(pwr_cfg_cmd)) { + case PWR_CMD_READ: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtlbe_hal_pwrseqcmdparsing(): " + "PWR_CMD_READ\n"); + break; + case PWR_CMD_WRITE: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtlbe_hal_pwrseqcmdparsing(): " + "PWR_CMD_WRITE\n"); + offset = GET_PWR_CFG_OFFSET(pwr_cfg_cmd); + + /*Read the value from system register*/ + value = rtl_read_byte(rtlpriv, offset); + value &= (~(GET_PWR_CFG_MASK(pwr_cfg_cmd))); + value = value | (GET_PWR_CFG_VALUE(pwr_cfg_cmd) + & GET_PWR_CFG_MASK(pwr_cfg_cmd)); + + /*Write the value back to sytem register*/ + rtl_write_byte(rtlpriv, offset, value); + break; + case PWR_CMD_POLLING: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtlbe_hal_pwrseqcmdparsing(): " + "PWR_CMD_POLLING\n"); + b_polling_bit = false; + offset = GET_PWR_CFG_OFFSET(pwr_cfg_cmd); + + do { + value = rtl_read_byte(rtlpriv, offset); + + value &= GET_PWR_CFG_MASK(pwr_cfg_cmd); + if (value == + (GET_PWR_CFG_VALUE(pwr_cfg_cmd) & + GET_PWR_CFG_MASK(pwr_cfg_cmd))) + b_polling_bit = true; + else + udelay(10); + + if (polling_count++ > max_polling_cnt) + return false; + + } while (!b_polling_bit); + break; + case PWR_CMD_DELAY: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtlbe_hal_pwrseqcmdparsing(): " + "PWR_CMD_DELAY\n"); + if (GET_PWR_CFG_VALUE(pwr_cfg_cmd) == + PWRSEQ_DELAY_US) + udelay(GET_PWR_CFG_OFFSET(pwr_cfg_cmd)); + else + mdelay(GET_PWR_CFG_OFFSET(pwr_cfg_cmd)); + break; + case PWR_CMD_END: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtlbe_hal_pwrseqcmdparsing(): " + "PWR_CMD_END\n"); + return true; + break; + default: + RT_ASSERT(false, + "rtlbe_hal_pwrseqcmdparsing(): " + "Unknown CMD!!\n"); + break; + } + } + + ary_idx++; + } while (1); + + return true; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h new file mode 100644 index 00000000000..ce14a3b5cb7 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/pwrseqcmd.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_PWRSEQCMD_H__ +#define __RTL8723BE_PWRSEQCMD_H__ + +#include "../wifi.h" +/*---------------------------------------------*/ +/*The value of cmd: 4 bits */ +/*---------------------------------------------*/ +#define PWR_CMD_READ 0x00 +#define PWR_CMD_WRITE 0x01 +#define PWR_CMD_POLLING 0x02 +#define PWR_CMD_DELAY 0x03 +#define PWR_CMD_END 0x04 + +/* define the base address of each block */ +#define PWR_BASEADDR_MAC 0x00 +#define PWR_BASEADDR_USB 0x01 +#define PWR_BASEADDR_PCIE 0x02 +#define PWR_BASEADDR_SDIO 0x03 + +#define PWR_INTF_SDIO_MSK BIT(0) +#define PWR_INTF_USB_MSK BIT(1) +#define PWR_INTF_PCI_MSK BIT(2) +#define PWR_INTF_ALL_MSK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) + +#define PWR_FAB_TSMC_MSK BIT(0) +#define PWR_FAB_UMC_MSK BIT(1) +#define PWR_FAB_ALL_MSK (BIT(0) | BIT(1) | BIT(2) | BIT(3)) + +#define PWR_CUT_TESTCHIP_MSK BIT(0) +#define PWR_CUT_A_MSK BIT(1) +#define PWR_CUT_B_MSK BIT(2) +#define PWR_CUT_C_MSK BIT(3) +#define PWR_CUT_D_MSK BIT(4) +#define PWR_CUT_E_MSK BIT(5) +#define PWR_CUT_F_MSK BIT(6) +#define PWR_CUT_G_MSK BIT(7) +#define PWR_CUT_ALL_MSK 0xFF + + +enum pwrseq_delay_unit { + PWRSEQ_DELAY_US, + PWRSEQ_DELAY_MS, +}; + +struct wlan_pwr_cfg { + u16 offset; + u8 cut_msk; + u8 fab_msk:4; + u8 interface_msk:4; + u8 base:4; + u8 cmd:4; + u8 msk; + u8 value; + +}; + +#define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset +#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) __PWR_CMD.cut_msk +#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) __PWR_CMD.fab_msk +#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) __PWR_CMD.interface_msk +#define GET_PWR_CFG_BASE(__PWR_CMD) __PWR_CMD.base +#define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd +#define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk +#define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value + +bool rtlbe_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version, + u8 fab_version, u8 interface_type, + struct wlan_pwr_cfg pwrcfgcmd[]); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/reg.h b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h new file mode 100644 index 00000000000..4c653fab879 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h @@ -0,0 +1,2277 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_REG_H__ +#define __RTL8723BE_REG_H__ + +#define TXPKT_BUF_SELECT 0x69 +#define RXPKT_BUF_SELECT 0xA5 +#define DISABLE_TRXPKT_BUF_ACCESS 0x0 + +#define REG_SYS_ISO_CTRL 0x0000 +#define REG_SYS_FUNC_EN 0x0002 +#define REG_APS_FSMCO 0x0004 +#define REG_SYS_CLKR 0x0008 +#define REG_9346CR 0x000A +#define REG_EE_VPD 0x000C +#define REG_AFE_MISC 0x0010 +#define REG_SPS0_CTRL 0x0011 +#define REG_SPS_OCP_CFG 0x0018 +#define REG_RSV_CTRL 0x001C +#define REG_RF_CTRL 0x001F +#define REG_LDOA15_CTRL 0x0020 +#define REG_LDOV12D_CTRL 0x0021 +#define REG_LDOHCI12_CTRL 0x0022 +#define REG_LPLDO_CTRL 0x0023 +#define REG_AFE_XTAL_CTRL 0x0024 +/* 1.5v for 8188EE test chip, 1.4v for MP chip */ +#define REG_AFE_LDO_CTRL 0x0027 +#define REG_AFE_PLL_CTRL 0x0028 +#define REG_MAC_PHY_CTRL 0x002c +#define REG_EFUSE_CTRL 0x0030 +#define REG_EFUSE_TEST 0x0034 +#define REG_PWR_DATA 0x0038 +#define REG_CAL_TIMER 0x003C +#define REG_ACLK_MON 0x003E +#define REG_GPIO_MUXCFG 0x0040 +#define REG_GPIO_IO_SEL 0x0042 +#define REG_MAC_PINMUX_CFG 0x0043 +#define REG_GPIO_PIN_CTRL 0x0044 +#define REG_GPIO_INTM 0x0048 +#define REG_LEDCFG0 0x004C +#define REG_LEDCFG1 0x004D +#define REG_LEDCFG2 0x004E +#define REG_LEDCFG3 0x004F +#define REG_FSIMR 0x0050 +#define REG_FSISR 0x0054 +#define REG_HSIMR 0x0058 +#define REG_HSISR 0x005c +#define REG_GPIO_PIN_CTRL_2 0x0060 +#define REG_GPIO_IO_SEL_2 0x0062 +#define REG_MULTI_FUNC_CTRL 0x0068 +#define REG_GPIO_OUTPUT 0x006c +#define REG_AFE_XTAL_CTRL_EXT 0x0078 +#define REG_XCK_OUT_CTRL 0x007c +#define REG_MCUFWDL 0x0080 +#define REG_WOL_EVENT 0x0081 +#define REG_MCUTSTCFG 0x0084 + + +#define REG_HIMR 0x00B0 +#define REG_HISR 0x00B4 +#define REG_HIMRE 0x00B8 +#define REG_HISRE 0x00BC + +#define REG_EFUSE_ACCESS 0x00CF + +#define REG_BIST_SCAN 0x00D0 +#define REG_BIST_RPT 0x00D4 +#define REG_BIST_ROM_RPT 0x00D8 +#define REG_USB_SIE_INTF 0x00E0 +#define REG_PCIE_MIO_INTF 0x00E4 +#define REG_PCIE_MIO_INTD 0x00E8 +#define REG_HPON_FSM 0x00EC +#define REG_SYS_CFG 0x00F0 +#define REG_GPIO_OUTSTS 0x00F4 +#define REG_SYS_CFG1 0x00F0 +#define REG_ROM_VERSION 0x00FD + +#define REG_CR 0x0100 +#define REG_PBP 0x0104 +#define REG_PKT_BUFF_ACCESS_CTRL 0x0106 +#define REG_TRXDMA_CTRL 0x010C +#define REG_TRXFF_BNDY 0x0114 +#define REG_TRXFF_STATUS 0x0118 +#define REG_RXFF_PTR 0x011C + +#define REG_CPWM 0x012F +#define REG_FWIMR 0x0130 +#define REG_FWISR 0x0134 +#define REG_PKTBUF_DBG_CTRL 0x0140 +#define REG_PKTBUF_DBG_DATA_L 0x0144 +#define REG_PKTBUF_DBG_DATA_H 0x0148 +#define REG_RXPKTBUF_CTRL (REG_PKTBUF_DBG_CTRL + 2) + +#define REG_TC0_CTRL 0x0150 +#define REG_TC1_CTRL 0x0154 +#define REG_TC2_CTRL 0x0158 +#define REG_TC3_CTRL 0x015C +#define REG_TC4_CTRL 0x0160 +#define REG_TCUNIT_BASE 0x0164 +#define REG_MBIST_START 0x0174 +#define REG_MBIST_DONE 0x0178 +#define REG_MBIST_FAIL 0x017C +#define REG_32K_CTRL 0x0194 +#define REG_C2HEVT_MSG_NORMAL 0x01A0 +#define REG_C2HEVT_CLEAR 0x01AF +#define REG_C2HEVT_MSG_TEST 0x01B8 +#define REG_MCUTST_1 0x01c0 +#define REG_FMETHR 0x01C8 +#define REG_HMETFR 0x01CC +#define REG_HMEBOX_0 0x01D0 +#define REG_HMEBOX_1 0x01D4 +#define REG_HMEBOX_2 0x01D8 +#define REG_HMEBOX_3 0x01DC + +#define REG_LLT_INIT 0x01E0 +#define REG_BB_ACCEESS_CTRL 0x01E8 +#define REG_BB_ACCESS_DATA 0x01EC + +#define REG_HMEBOX_EXT_0 0x01F0 +#define REG_HMEBOX_EXT_1 0x01F4 +#define REG_HMEBOX_EXT_2 0x01F8 +#define REG_HMEBOX_EXT_3 0x01FC + +#define REG_RQPN 0x0200 +#define REG_FIFOPAGE 0x0204 +#define REG_TDECTRL 0x0208 +#define REG_TXDMA_OFFSET_CHK 0x020C +#define REG_TXDMA_STATUS 0x0210 +#define REG_RQPN_NPQ 0x0214 + +#define REG_RXDMA_AGG_PG_TH 0x0280 +/* FW shall update this register before FW write RXPKT_RELEASE_POLL to 1 */ +#define REG_FW_UPD_RDPTR 0x0284 +/* Control the RX DMA.*/ +#define REG_RXDMA_CONTROL 0x0286 +/* The number of packets in RXPKTBUF. */ +#define REG_RXPKT_NUM 0x0287 + +#define REG_PCIE_CTRL_REG 0x0300 +#define REG_INT_MIG 0x0304 +#define REG_BCNQ_DESA 0x0308 +#define REG_HQ_DESA 0x0310 +#define REG_MGQ_DESA 0x0318 +#define REG_VOQ_DESA 0x0320 +#define REG_VIQ_DESA 0x0328 +#define REG_BEQ_DESA 0x0330 +#define REG_BKQ_DESA 0x0338 +#define REG_RX_DESA 0x0340 + +#define REG_DBI 0x0348 +#define REG_MDIO 0x0354 +#define REG_DBG_SEL 0x0360 +#define REG_PCIE_HRPWM 0x0361 +#define REG_PCIE_HCPWM 0x0363 +#define REG_UART_CTRL 0x0364 +#define REG_WATCH_DOG 0x0368 +#define REG_UART_TX_DESA 0x0370 +#define REG_UART_RX_DESA 0x0378 + + +#define REG_HDAQ_DESA_NODEF 0x0000 +#define REG_CMDQ_DESA_NODEF 0x0000 + +#define REG_VOQ_INFORMATION 0x0400 +#define REG_VIQ_INFORMATION 0x0404 +#define REG_BEQ_INFORMATION 0x0408 +#define REG_BKQ_INFORMATION 0x040C +#define REG_MGQ_INFORMATION 0x0410 +#define REG_HGQ_INFORMATION 0x0414 +#define REG_BCNQ_INFORMATION 0x0418 +#define REG_TXPKT_EMPTY 0x041A + + +#define REG_CPU_MGQ_INFORMATION 0x041C +#define REG_FWHW_TXQ_CTRL 0x0420 +#define REG_HWSEQ_CTRL 0x0423 +#define REG_TXPKTBUF_BCNQ_BDNY 0x0424 +#define REG_TXPKTBUF_MGQ_BDNY 0x0425 +#define REG_MULTI_BCNQ_EN 0x0426 +#define REG_MULTI_BCNQ_OFFSET 0x0427 +#define REG_SPEC_SIFS 0x0428 +#define REG_RL 0x042A +#define REG_DARFRC 0x0430 +#define REG_RARFRC 0x0438 +#define REG_RRSR 0x0440 +#define REG_ARFR0 0x0444 +#define REG_ARFR1 0x0448 +#define REG_ARFR2 0x044C +#define REG_ARFR3 0x0450 +#define REG_AMPDU_MAX_TIME 0x0456 +#define REG_AGGLEN_LMT 0x0458 +#define REG_AMPDU_MIN_SPACE 0x045C +#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D +#define REG_FAST_EDCA_CTRL 0x0460 +#define REG_RD_RESP_PKT_TH 0x0463 +#define REG_INIRTS_RATE_SEL 0x0480 +#define REG_INIDATA_RATE_SEL 0x0484 +#define REG_POWER_STATUS 0x04A4 +#define REG_POWER_STAGE1 0x04B4 +#define REG_POWER_STAGE2 0x04B8 +#define REG_PKT_LIFE_TIME 0x04C0 +#define REG_STBC_SETTING 0x04C4 +#define REG_PROT_MODE_CTRL 0x04C8 +#define REG_BAR_MODE_CTRL 0x04CC +#define REG_RA_TRY_RATE_AGG_LMT 0x04CF +#define REG_EARLY_MODE_CONTROL 0x04D0 +#define REG_NQOS_SEQ 0x04DC +#define REG_QOS_SEQ 0x04DE +#define REG_NEED_CPU_HANDLE 0x04E0 +#define REG_PKT_LOSE_RPT 0x04E1 +#define REG_PTCL_ERR_STATUS 0x04E2 +#define REG_TX_RPT_CTRL 0x04EC +#define REG_TX_RPT_TIME 0x04F0 +#define REG_DUMMY 0x04FC + +#define REG_EDCA_VO_PARAM 0x0500 +#define REG_EDCA_VI_PARAM 0x0504 +#define REG_EDCA_BE_PARAM 0x0508 +#define REG_EDCA_BK_PARAM 0x050C +#define REG_BCNTCFG 0x0510 +#define REG_PIFS 0x0512 +#define REG_RDG_PIFS 0x0513 +#define REG_SIFS_CTX 0x0514 +#define REG_SIFS_TRX 0x0516 +#define REG_AGGR_BREAK_TIME 0x051A +#define REG_SLOT 0x051B +#define REG_TX_PTCL_CTRL 0x0520 +#define REG_TXPAUSE 0x0522 +#define REG_DIS_TXREQ_CLR 0x0523 +#define REG_RD_CTRL 0x0524 +#define REG_TBTT_PROHIBIT 0x0540 +#define REG_RD_NAV_NXT 0x0544 +#define REG_NAV_PROT_LEN 0x0546 +#define REG_BCN_CTRL 0x0550 +#define REG_USTIME_TSF 0x0551 +#define REG_MBID_NUM 0x0552 +#define REG_DUAL_TSF_RST 0x0553 +#define REG_BCN_INTERVAL 0x0554 +#define REG_MBSSID_BCN_SPACE 0x0554 +#define REG_DRVERLYINT 0x0558 +#define REG_BCNDMATIM 0x0559 +#define REG_ATIMWND 0x055A +#define REG_BCN_MAX_ERR 0x055D +#define REG_RXTSF_OFFSET_CCK 0x055E +#define REG_RXTSF_OFFSET_OFDM 0x055F +#define REG_TSFTR 0x0560 +#define REG_INIT_TSFTR 0x0564 +#define REG_SECONDARY_CCA_CTRL 0x0577 +#define REG_PSTIMER 0x0580 +#define REG_TIMER0 0x0584 +#define REG_TIMER1 0x0588 +#define REG_ACMHWCTRL 0x05C0 +#define REG_ACMRSTCTRL 0x05C1 +#define REG_ACMAVG 0x05C2 +#define REG_VO_ADMTIME 0x05C4 +#define REG_VI_ADMTIME 0x05C6 +#define REG_BE_ADMTIME 0x05C8 +#define REG_EDCA_RANDOM_GEN 0x05CC +#define REG_SCH_TXCMD 0x05D0 + +#define REG_APSD_CTRL 0x0600 +#define REG_BWOPMODE 0x0603 +#define REG_TCR 0x0604 +#define REG_RCR 0x0608 +#define REG_RX_PKT_LIMIT 0x060C +#define REG_RX_DLK_TIME 0x060D +#define REG_RX_DRVINFO_SZ 0x060F + +#define REG_MACID 0x0610 +#define REG_BSSID 0x0618 +#define REG_MAR 0x0620 +#define REG_MBIDCAMCFG 0x0628 + +#define REG_USTIME_EDCA 0x0638 +#define REG_MAC_SPEC_SIFS 0x063A +#define REG_RESP_SIFS_CCK 0x063C +#define REG_RESP_SIFS_OFDM 0x063E +#define REG_ACKTO 0x0640 +#define REG_CTS2TO 0x0641 +#define REG_EIFS 0x0642 + +#define REG_NAV_CTRL 0x0650 +#define REG_BACAMCMD 0x0654 +#define REG_BACAMCONTENT 0x0658 +#define REG_LBDLY 0x0660 +#define REG_FWDLY 0x0661 +#define REG_RXERR_RPT 0x0664 +#define REG_TRXPTCL_CTL 0x0668 + +#define REG_CAMCMD 0x0670 +#define REG_CAMWRITE 0x0674 +#define REG_CAMREAD 0x0678 +#define REG_CAMDBG 0x067C +#define REG_SECCFG 0x0680 + +#define REG_WOW_CTRL 0x0690 +#define REG_PSSTATUS 0x0691 +#define REG_PS_RX_INFO 0x0692 +#define REG_UAPSD_TID 0x0693 +#define REG_LPNAV_CTRL 0x0694 +#define REG_WKFMCAM_NUM 0x0698 +#define REG_WKFMCAM_RWD 0x069C +#define REG_RXFLTMAP0 0x06A0 +#define REG_RXFLTMAP1 0x06A2 +#define REG_RXFLTMAP2 0x06A4 +#define REG_BCN_PSR_RPT 0x06A8 +#define REG_CALB32K_CTRL 0x06AC +#define REG_PKT_MON_CTRL 0x06B4 +#define REG_BT_COEX_TABLE 0x06C0 +#define REG_WMAC_RESP_TXINFO 0x06D8 + +#define REG_USB_INFO 0xFE17 +#define REG_USB_SPECIAL_OPTION 0xFE55 +#define REG_USB_DMA_AGG_TO 0xFE5B +#define REG_USB_AGG_TO 0xFE5C +#define REG_USB_AGG_TH 0xFE5D + +#define REG_TEST_USB_TXQS 0xFE48 +#define REG_TEST_SIE_VID 0xFE60 +#define REG_TEST_SIE_PID 0xFE62 +#define REG_TEST_SIE_OPTIONAL 0xFE64 +#define REG_TEST_SIE_CHIRP_K 0xFE65 +#define REG_TEST_SIE_PHY 0xFE66 +#define REG_TEST_SIE_MAC_ADDR 0xFE70 +#define REG_TEST_SIE_STRING 0xFE80 + +#define REG_NORMAL_SIE_VID 0xFE60 +#define REG_NORMAL_SIE_PID 0xFE62 +#define REG_NORMAL_SIE_OPTIONAL 0xFE64 +#define REG_NORMAL_SIE_EP 0xFE65 +#define REG_NORMAL_SIE_PHY 0xFE68 +#define REG_NORMAL_SIE_MAC_ADDR 0xFE70 +#define REG_NORMAL_SIE_STRING 0xFE80 + +#define CR9346 REG_9346CR +#define MSR (REG_CR + 2) +#define ISR REG_HISR +#define TSFR REG_TSFTR + +#define MACIDR0 REG_MACID +#define MACIDR4 (REG_MACID + 4) + +#define PBP REG_PBP + +#define IDR0 MACIDR0 +#define IDR4 MACIDR4 + +#define UNUSED_REGISTER 0x1BF +#define DCAM UNUSED_REGISTER +#define PSR UNUSED_REGISTER +#define BBADDR UNUSED_REGISTER +#define PHYDATAR UNUSED_REGISTER + +#define INVALID_BBRF_VALUE 0x12345678 + +#define MAX_MSS_DENSITY_2T 0x13 +#define MAX_MSS_DENSITY_1T 0x0A + +#define CMDEEPROM_EN BIT(5) +#define CMDEEPROM_SEL BIT(4) +#define CMD9346CR_9356SEL BIT(4) +#define AUTOLOAD_EEPROM (CMDEEPROM_EN | CMDEEPROM_SEL) +#define AUTOLOAD_EFUSE CMDEEPROM_EN + +#define GPIOSEL_GPIO 0 +#define GPIOSEL_ENBT BIT(5) + +#define GPIO_IN REG_GPIO_PIN_CTRL +#define GPIO_OUT (REG_GPIO_PIN_CTRL + 1) +#define GPIO_IO_SEL (REG_GPIO_PIN_CTRL + 2) +#define GPIO_MOD (REG_GPIO_PIN_CTRL + 3) + +/* 8723/8188E Host System Interrupt Mask Register (offset 0x58, 32 byte) */ +#define HSIMR_GPIO12_0_INT_EN BIT(0) +#define HSIMR_SPS_OCP_INT_EN BIT(5) +#define HSIMR_RON_INT_EN BIT(6) +#define HSIMR_PDN_INT_EN BIT(7) +#define HSIMR_GPIO9_INT_EN BIT(25) + +/* 8723/8188E Host System Interrupt Status Register (offset 0x5C, 32 byte) */ + +#define HSISR_GPIO12_0_INT BIT(0) +#define HSISR_SPS_OCP_INT BIT(5) +#define HSISR_RON_INT_EN BIT(6) +#define HSISR_PDNINT BIT(7) +#define HSISR_GPIO9_INT BIT(25) + +#define MSR_NOLINK 0x00 +#define MSR_ADHOC 0x01 +#define MSR_INFRA 0x02 +#define MSR_AP 0x03 + +#define RRSR_RSC_OFFSET 21 +#define RRSR_SHORT_OFFSET 23 +#define RRSR_RSC_BW_40M 0x600000 +#define RRSR_RSC_UPSUBCHNL 0x400000 +#define RRSR_RSC_LOWSUBCHNL 0x200000 +#define RRSR_SHORT 0x800000 +#define RRSR_1M BIT(0) +#define RRSR_2M BIT(1) +#define RRSR_5_5M BIT(2) +#define RRSR_11M BIT(3) +#define RRSR_6M BIT(4) +#define RRSR_9M BIT(5) +#define RRSR_12M BIT(6) +#define RRSR_18M BIT(7) +#define RRSR_24M BIT(8) +#define RRSR_36M BIT(9) +#define RRSR_48M BIT(10) +#define RRSR_54M BIT(11) +#define RRSR_MCS0 BIT(12) +#define RRSR_MCS1 BIT(13) +#define RRSR_MCS2 BIT(14) +#define RRSR_MCS3 BIT(15) +#define RRSR_MCS4 BIT(16) +#define RRSR_MCS5 BIT(17) +#define RRSR_MCS6 BIT(18) +#define RRSR_MCS7 BIT(19) +#define BRSR_ACKSHORTPMB BIT(23) + +#define RATR_1M 0x00000001 +#define RATR_2M 0x00000002 +#define RATR_55M 0x00000004 +#define RATR_11M 0x00000008 +#define RATR_6M 0x00000010 +#define RATR_9M 0x00000020 +#define RATR_12M 0x00000040 +#define RATR_18M 0x00000080 +#define RATR_24M 0x00000100 +#define RATR_36M 0x00000200 +#define RATR_48M 0x00000400 +#define RATR_54M 0x00000800 +#define RATR_MCS0 0x00001000 +#define RATR_MCS1 0x00002000 +#define RATR_MCS2 0x00004000 +#define RATR_MCS3 0x00008000 +#define RATR_MCS4 0x00010000 +#define RATR_MCS5 0x00020000 +#define RATR_MCS6 0x00040000 +#define RATR_MCS7 0x00080000 +#define RATR_MCS8 0x00100000 +#define RATR_MCS9 0x00200000 +#define RATR_MCS10 0x00400000 +#define RATR_MCS11 0x00800000 +#define RATR_MCS12 0x01000000 +#define RATR_MCS13 0x02000000 +#define RATR_MCS14 0x04000000 +#define RATR_MCS15 0x08000000 + +#define RATE_1M BIT(0) +#define RATE_2M BIT(1) +#define RATE_5_5M BIT(2) +#define RATE_11M BIT(3) +#define RATE_6M BIT(4) +#define RATE_9M BIT(5) +#define RATE_12M BIT(6) +#define RATE_18M BIT(7) +#define RATE_24M BIT(8) +#define RATE_36M BIT(9) +#define RATE_48M BIT(10) +#define RATE_54M BIT(11) +#define RATE_MCS0 BIT(12) +#define RATE_MCS1 BIT(13) +#define RATE_MCS2 BIT(14) +#define RATE_MCS3 BIT(15) +#define RATE_MCS4 BIT(16) +#define RATE_MCS5 BIT(17) +#define RATE_MCS6 BIT(18) +#define RATE_MCS7 BIT(19) +#define RATE_MCS8 BIT(20) +#define RATE_MCS9 BIT(21) +#define RATE_MCS10 BIT(22) +#define RATE_MCS11 BIT(23) +#define RATE_MCS12 BIT(24) +#define RATE_MCS13 BIT(25) +#define RATE_MCS14 BIT(26) +#define RATE_MCS15 BIT(27) + +#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M) +#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M |\ + RATR_24M | RATR_36M | RATR_48M | RATR_54M) +#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 |\ + RATR_MCS3 | RATR_MCS4 | RATR_MCS5 |\ + RATR_MCS6 | RATR_MCS7) +#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 |\ + RATR_MCS11 | RATR_MCS12 | RATR_MCS13 |\ + RATR_MCS14 | RATR_MCS15) + +#define BW_OPMODE_20MHZ BIT(2) +#define BW_OPMODE_5G BIT(1) +#define BW_OPMODE_11J BIT(0) + +#define CAM_VALID BIT(15) +#define CAM_NOTVALID 0x0000 +#define CAM_USEDK BIT(5) + +#define CAM_NONE 0x0 +#define CAM_WEP40 0x01 +#define CAM_TKIP 0x02 +#define CAM_AES 0x04 +#define CAM_WEP104 0x05 + +#define TOTAL_CAM_ENTRY 32 +#define HALF_CAM_ENTRY 16 + +#define CAM_WRITE BIT(16) +#define CAM_READ 0x00000000 +#define CAM_POLLINIG BIT(31) + +#define SCR_USEDK 0x01 +#define SCR_TXSEC_ENABLE 0x02 +#define SCR_RXSEC_ENABLE 0x04 + +#define WOW_PMEN BIT(0) +#define WOW_WOMEN BIT(1) +#define WOW_MAGIC BIT(2) +#define WOW_UWF BIT(3) + +/********************************************* +* 8723BE IMR/ISR bits +**********************************************/ +#define IMR_DISABLED 0x0 +/* IMR DW0(0x0060-0063) Bit 0-31 */ +#define IMR_TXCCK BIT(30) /* TXRPT interrupt when + * CCX bit of the packet is set + */ +#define IMR_PSTIMEOUT BIT(29) /* Power Save Time Out Interrupt */ +#define IMR_GTINT4 BIT(28) /* When GTIMER4 expires, + * this bit is set to 1 + */ +#define IMR_GTINT3 BIT(27) /* When GTIMER3 expires, + * this bit is set to 1 + */ +#define IMR_TBDER BIT(26) /* Transmit Beacon0 Error */ +#define IMR_TBDOK BIT(25) /* Transmit Beacon0 OK */ +#define IMR_TSF_BIT32_TOGGLE BIT(24) /* TSF Timer BIT32 toggle + * indication interrupt + */ +#define IMR_BCNDMAINT0 BIT(20) /* Beacon DMA Interrupt 0 */ +#define IMR_BCNDOK0 BIT(16) /* Beacon Queue DMA OK0 */ +#define IMR_HSISR_IND_ON_INT BIT(15) /* HSISR Indicator (HSIMR & HSISR is + * true, this bit is set to 1) + */ +#define IMR_BCNDMAINT_E BIT(14) /* Beacon DMA Interrupt + * Extension for Win7 + */ +#define IMR_ATIMEND BIT(12) /* CTWidnow End or ATIM Window End */ +#define IMR_HISR1_IND_INT BIT(11) /* HISR1 Indicator (HISR1 & HIMR1 is + * true, this bit is set to 1) + */ +#define IMR_C2HCMD BIT(10) /* CPU to Host Command INT Status, + * Write 1 clear + */ +#define IMR_CPWM2 BIT(9) /* CPU power Mode exchange INT Status, + * Write 1 clear + */ +#define IMR_CPWM BIT(8) /* CPU power Mode exchange INT Status, + * Write 1 clear + */ +#define IMR_HIGHDOK BIT(7) /* High Queue DMA OK */ +#define IMR_MGNTDOK BIT(6) /* Management Queue DMA OK */ +#define IMR_BKDOK BIT(5) /* AC_BK DMA OK */ +#define IMR_BEDOK BIT(4) /* AC_BE DMA OK */ +#define IMR_VIDOK BIT(3) /* AC_VI DMA OK */ +#define IMR_VODOK BIT(2) /* AC_VO DMA OK */ +#define IMR_RDU BIT(1) /* Rx Descriptor Unavailable */ +#define IMR_ROK BIT(0) /* Receive DMA OK */ + +/* IMR DW1(0x00B4-00B7) Bit 0-31 */ +#define IMR_BCNDMAINT7 BIT(27) /* Beacon DMA Interrupt 7 */ +#define IMR_BCNDMAINT6 BIT(26) /* Beacon DMA Interrupt 6 */ +#define IMR_BCNDMAINT5 BIT(25) /* Beacon DMA Interrupt 5 */ +#define IMR_BCNDMAINT4 BIT(24) /* Beacon DMA Interrupt 4 */ +#define IMR_BCNDMAINT3 BIT(23) /* Beacon DMA Interrupt 3 */ +#define IMR_BCNDMAINT2 BIT(22) /* Beacon DMA Interrupt 2 */ +#define IMR_BCNDMAINT1 BIT(21) /* Beacon DMA Interrupt 1 */ +#define IMR_BCNDOK7 BIT(20) /* Beacon Queue DMA OK Interrup 7 */ +#define IMR_BCNDOK6 BIT(19) /* Beacon Queue DMA OK Interrup 6 */ +#define IMR_BCNDOK5 BIT(18) /* Beacon Queue DMA OK Interrup 5 */ +#define IMR_BCNDOK4 BIT(17) /* Beacon Queue DMA OK Interrup 4 */ +#define IMR_BCNDOK3 BIT(16) /* Beacon Queue DMA OK Interrup 3 */ +#define IMR_BCNDOK2 BIT(15) /* Beacon Queue DMA OK Interrup 2 */ +#define IMR_BCNDOK1 BIT(14) /* Beacon Queue DMA OK Interrup 1 */ +#define IMR_ATIMEND_E BIT(13) /* ATIM Window End Extension for Win7 */ +#define IMR_TXERR BIT(11) /* Tx Error Flag Interrupt Status, + * write 1 clear. + */ +#define IMR_RXERR BIT(10) /* Rx Error Flag INT Status, + * Write 1 clear + */ +#define IMR_TXFOVW BIT(9) /* Transmit FIFO Overflow */ +#define IMR_RXFOVW BIT(8) /* Receive FIFO Overflow */ + +#define HWSET_MAX_SIZE 512 +#define EFUSE_MAX_SECTION 64 +#define EFUSE_REAL_CONTENT_LEN 256 +#define EFUSE_OOB_PROTECT_BYTES 18 /* PG data exclude header, + * dummy 7 bytes frome CP test + * and reserved 1byte. + */ + +#define EEPROM_DEFAULT_TSSI 0x0 +#define EEPROM_DEFAULT_TXPOWERDIFF 0x0 +#define EEPROM_DEFAULT_CRYSTALCAP 0x5 +#define EEPROM_DEFAULT_BOARDTYPE 0x02 +#define EEPROM_DEFAULT_TXPOWER 0x1010 +#define EEPROM_DEFAULT_HT2T_TXPWR 0x10 + +#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 +#define EEPROM_DEFAULT_THERMALMETER 0x18 +#define EEPROM_DEFAULT_ANTTXPOWERDIFF 0x0 +#define EEPROM_DEFAULT_TXPWDIFF_CRYSTALCAP 0x5 +#define EEPROM_DEFAULT_TXPOWERLEVEL 0x22 +#define EEPROM_DEFAULT_HT40_2SDIFF 0x0 +#define EEPROM_DEFAULT_HT20_DIFF 2 +#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 +#define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0 +#define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0 + +#define RF_OPTION1 0x79 +#define RF_OPTION2 0x7A +#define RF_OPTION3 0x7B +#define RF_OPTION4 0xC3 + +#define EEPROM_DEFAULT_PID 0x1234 +#define EEPROM_DEFAULT_VID 0x5678 +#define EEPROM_DEFAULT_CUSTOMERID 0xAB +#define EEPROM_DEFAULT_SUBCUSTOMERID 0xCD +#define EEPROM_DEFAULT_VERSION 0 + +#define EEPROM_CHANNEL_PLAN_FCC 0x0 +#define EEPROM_CHANNEL_PLAN_IC 0x1 +#define EEPROM_CHANNEL_PLAN_ETSI 0x2 +#define EEPROM_CHANNEL_PLAN_SPAIN 0x3 +#define EEPROM_CHANNEL_PLAN_FRANCE 0x4 +#define EEPROM_CHANNEL_PLAN_MKK 0x5 +#define EEPROM_CHANNEL_PLAN_MKK1 0x6 +#define EEPROM_CHANNEL_PLAN_ISRAEL 0x7 +#define EEPROM_CHANNEL_PLAN_TELEC 0x8 +#define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9 +#define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA +#define EEPROM_CHANNEL_PLAN_NCC 0xB +#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80 + +#define EEPROM_CID_DEFAULT 0x0 +#define EEPROM_CID_TOSHIBA 0x4 +#define EEPROM_CID_CCX 0x10 +#define EEPROM_CID_QMI 0x0D +#define EEPROM_CID_WHQL 0xFE + +#define RTL8723BE_EEPROM_ID 0x8129 + +#define EEPROM_HPON 0x02 +#define EEPROM_CLK 0x06 +#define EEPROM_TESTR 0x08 + + +#define EEPROM_TXPOWERCCK 0x10 +#define EEPROM_TXPOWERHT40_1S 0x16 +#define EEPROM_TXPOWERHT20DIFF 0x1B +#define EEPROM_TXPOWER_OFDMDIFF 0x1B + + + +#define EEPROM_TX_PWR_INX 0x10 + +#define EEPROM_CHANNELPLAN 0xB8 +#define EEPROM_XTAL_8723BE 0xB9 +#define EEPROM_THERMAL_METER_88E 0xBA +#define EEPROM_IQK_LCK_88E 0xBB + +#define EEPROM_RF_BOARD_OPTION_88E 0xC1 +#define EEPROM_RF_FEATURE_OPTION_88E 0xC2 +#define EEPROM_RF_BT_SETTING_88E 0xC3 +#define EEPROM_VERSION 0xC4 +#define EEPROM_CUSTOMER_ID 0xC5 +#define EEPROM_RF_ANTENNA_OPT_88E 0xC9 + +#define EEPROM_MAC_ADDR 0xD0 +#define EEPROM_VID 0xD6 +#define EEPROM_DID 0xD8 +#define EEPROM_SVID 0xDA +#define EEPROM_SMID 0xDC + +#define STOPBECON BIT(6) +#define STOPHIGHT BIT(5) +#define STOPMGT BIT(4) +#define STOPVO BIT(3) +#define STOPVI BIT(2) +#define STOPBE BIT(1) +#define STOPBK BIT(0) + +#define RCR_APPFCS BIT(31) +#define RCR_APP_MIC BIT(30) +#define RCR_APP_ICV BIT(29) +#define RCR_APP_PHYST_RXFF BIT(28) +#define RCR_APP_BA_SSN BIT(27) +#define RCR_ENMBID BIT(24) +#define RCR_LSIGEN BIT(23) +#define RCR_MFBEN BIT(22) +#define RCR_HTC_LOC_CTRL BIT(14) +#define RCR_AMF BIT(13) +#define RCR_ACF BIT(12) +#define RCR_ADF BIT(11) +#define RCR_AICV BIT(9) +#define RCR_ACRC32 BIT(8) +#define RCR_CBSSID_BCN BIT(7) +#define RCR_CBSSID_DATA BIT(6) +#define RCR_CBSSID RCR_CBSSID_DATA +#define RCR_APWRMGT BIT(5) +#define RCR_ADD3 BIT(4) +#define RCR_AB BIT(3) +#define RCR_AM BIT(2) +#define RCR_APM BIT(1) +#define RCR_AAP BIT(0) +#define RCR_MXDMA_OFFSET 8 +#define RCR_FIFO_OFFSET 13 + +#define RSV_CTRL 0x001C +#define RD_CTRL 0x0524 + +#define REG_USB_INFO 0xFE17 +#define REG_USB_SPECIAL_OPTION 0xFE55 +#define REG_USB_DMA_AGG_TO 0xFE5B +#define REG_USB_AGG_TO 0xFE5C +#define REG_USB_AGG_TH 0xFE5D + +#define REG_USB_VID 0xFE60 +#define REG_USB_PID 0xFE62 +#define REG_USB_OPTIONAL 0xFE64 +#define REG_USB_CHIRP_K 0xFE65 +#define REG_USB_PHY 0xFE66 +#define REG_USB_MAC_ADDR 0xFE70 +#define REG_USB_HRPWM 0xFE58 +#define REG_USB_HCPWM 0xFE57 + +#define SW18_FPWM BIT(3) + +#define ISO_MD2PP BIT(0) +#define ISO_UA2USB BIT(1) +#define ISO_UD2CORE BIT(2) +#define ISO_PA2PCIE BIT(3) +#define ISO_PD2CORE BIT(4) +#define ISO_IP2MAC BIT(5) +#define ISO_DIOP BIT(6) +#define ISO_DIOE BIT(7) +#define ISO_EB2CORE BIT(8) +#define ISO_DIOR BIT(9) + +#define PWC_EV25V BIT(14) +#define PWC_EV12V BIT(15) + +#define FEN_BBRSTB BIT(0) +#define FEN_BB_GLB_RSTN BIT(1) +#define FEN_USBA BIT(2) +#define FEN_UPLL BIT(3) +#define FEN_USBD BIT(4) +#define FEN_DIO_PCIE BIT(5) +#define FEN_PCIEA BIT(6) +#define FEN_PPLL BIT(7) +#define FEN_PCIED BIT(8) +#define FEN_DIOE BIT(9) +#define FEN_CPUEN BIT(10) +#define FEN_DCORE BIT(11) +#define FEN_ELDR BIT(12) +#define FEN_DIO_RF BIT(13) +#define FEN_HWPDN BIT(14) +#define FEN_MREGEN BIT(15) + +#define PFM_LDALL BIT(0) +#define PFM_ALDN BIT(1) +#define PFM_LDKP BIT(2) +#define PFM_WOWL BIT(3) +#define ENPDN BIT(4) +#define PDN_PL BIT(5) +#define APFM_ONMAC BIT(8) +#define APFM_OFF BIT(9) +#define APFM_RSM BIT(10) +#define AFSM_HSUS BIT(11) +#define AFSM_PCIE BIT(12) +#define APDM_MAC BIT(13) +#define APDM_HOST BIT(14) +#define APDM_HPDN BIT(15) +#define RDY_MACON BIT(16) +#define SUS_HOST BIT(17) +#define ROP_ALD BIT(20) +#define ROP_PWR BIT(21) +#define ROP_SPS BIT(22) +#define SOP_MRST BIT(25) +#define SOP_FUSE BIT(26) +#define SOP_ABG BIT(27) +#define SOP_AMB BIT(28) +#define SOP_RCK BIT(29) +#define SOP_A8M BIT(30) +#define XOP_BTCK BIT(31) + +#define ANAD16V_EN BIT(0) +#define ANA8M BIT(1) +#define MACSLP BIT(4) +#define LOADER_CLK_EN BIT(5) +#define _80M_SSC_DIS BIT(7) +#define _80M_SSC_EN_HO BIT(8) +#define PHY_SSC_RSTB BIT(9) +#define SEC_CLK_EN BIT(10) +#define MAC_CLK_EN BIT(11) +#define SYS_CLK_EN BIT(12) +#define RING_CLK_EN BIT(13) + +#define BOOT_FROM_EEPROM BIT(4) +#define EEPROM_EN BIT(5) + +#define AFE_BGEN BIT(0) +#define AFE_MBEN BIT(1) +#define MAC_ID_EN BIT(7) + +#define WLOCK_ALL BIT(0) +#define WLOCK_00 BIT(1) +#define WLOCK_04 BIT(2) +#define WLOCK_08 BIT(3) +#define WLOCK_40 BIT(4) +#define R_DIS_PRST_0 BIT(5) +#define R_DIS_PRST_1 BIT(6) +#define LOCK_ALL_EN BIT(7) + +#define RF_EN BIT(0) +#define RF_RSTB BIT(1) +#define RF_SDMRSTB BIT(2) + +#define LDA15_EN BIT(0) +#define LDA15_STBY BIT(1) +#define LDA15_OBUF BIT(2) +#define LDA15_REG_VOS BIT(3) +#define _LDA15_VOADJ(x) (((x) & 0x7) << 4) + +#define LDV12_EN BIT(0) +#define LDV12_SDBY BIT(1) +#define LPLDO_HSM BIT(2) +#define LPLDO_LSM_DIS BIT(3) +#define _LDV12_VADJ(x) (((x) & 0xF) << 4) + +#define XTAL_EN BIT(0) +#define XTAL_BSEL BIT(1) +#define _XTAL_BOSC(x) (((x) & 0x3) << 2) +#define _XTAL_CADJ(x) (((x) & 0xF) << 4) +#define XTAL_GATE_USB BIT(8) +#define _XTAL_USB_DRV(x) (((x) & 0x3) << 9) +#define XTAL_GATE_AFE BIT(11) +#define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12) +#define XTAL_RF_GATE BIT(14) +#define _XTAL_RF_DRV(x) (((x) & 0x3) << 15) +#define XTAL_GATE_DIG BIT(17) +#define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18) +#define XTAL_BT_GATE BIT(20) +#define _XTAL_BT_DRV(x) (((x) & 0x3) << 21) +#define _XTAL_GPIO(x) (((x) & 0x7) << 23) + +#define CKDLY_AFE BIT(26) +#define CKDLY_USB BIT(27) +#define CKDLY_DIG BIT(28) +#define CKDLY_BT BIT(29) + +#define APLL_EN BIT(0) +#define APLL_320_EN BIT(1) +#define APLL_FREF_SEL BIT(2) +#define APLL_EDGE_SEL BIT(3) +#define APLL_WDOGB BIT(4) +#define APLL_LPFEN BIT(5) + +#define APLL_REF_CLK_13MHZ 0x1 +#define APLL_REF_CLK_19_2MHZ 0x2 +#define APLL_REF_CLK_20MHZ 0x3 +#define APLL_REF_CLK_25MHZ 0x4 +#define APLL_REF_CLK_26MHZ 0x5 +#define APLL_REF_CLK_38_4MHZ 0x6 +#define APLL_REF_CLK_40MHZ 0x7 + +#define APLL_320EN BIT(14) +#define APLL_80EN BIT(15) +#define APLL_1MEN BIT(24) + +#define ALD_EN BIT(18) +#define EF_PD BIT(19) +#define EF_FLAG BIT(31) + +#define EF_TRPT BIT(7) +#define LDOE25_EN BIT(31) + +#define RSM_EN BIT(0) +#define TIMER_EN BIT(4) + +#define TRSW0EN BIT(2) +#define TRSW1EN BIT(3) +#define EROM_EN BIT(4) +#define ENBT BIT(5) +#define ENUART BIT(8) +#define UART_910 BIT(9) +#define ENPMAC BIT(10) +#define SIC_SWRST BIT(11) +#define ENSIC BIT(12) +#define SIC_23 BIT(13) +#define ENHDP BIT(14) +#define SIC_LBK BIT(15) + +#define LED0PL BIT(4) +#define LED1PL BIT(12) +#define LED0DIS BIT(7) + +#define MCUFWDL_EN BIT(0) +#define MCUFWDL_RDY BIT(1) +#define FWDL_CHKSUM_RPT BIT(2) +#define MACINI_RDY BIT(3) +#define BBINI_RDY BIT(4) +#define RFINI_RDY BIT(5) +#define WINTINI_RDY BIT(6) +#define CPRST BIT(23) + +#define XCLK_VLD BIT(0) +#define ACLK_VLD BIT(1) +#define UCLK_VLD BIT(2) +#define PCLK_VLD BIT(3) +#define PCIRSTB BIT(4) +#define V15_VLD BIT(5) +#define TRP_B15V_EN BIT(7) +#define SIC_IDLE BIT(8) +#define BD_MAC2 BIT(9) +#define BD_MAC1 BIT(10) +#define IC_MACPHY_MODE BIT(11) +#define VENDOR_ID BIT(19) +#define PAD_HWPD_IDN BIT(22) +#define TRP_VAUX_EN BIT(23) +#define TRP_BT_EN BIT(24) +#define BD_PKG_SEL BIT(25) +#define BD_HCI_SEL BIT(26) +#define TYPE_ID BIT(27) + +#define CHIP_VER_RTL_MASK 0xF000 +#define CHIP_VER_RTL_SHIFT 12 + +#define REG_LBMODE (REG_CR + 3) + +#define HCI_TXDMA_EN BIT(0) +#define HCI_RXDMA_EN BIT(1) +#define TXDMA_EN BIT(2) +#define RXDMA_EN BIT(3) +#define PROTOCOL_EN BIT(4) +#define SCHEDULE_EN BIT(5) +#define MACTXEN BIT(6) +#define MACRXEN BIT(7) +#define ENSWBCN BIT(8) +#define ENSEC BIT(9) + +#define _NETTYPE(x) (((x) & 0x3) << 16) +#define MASK_NETTYPE 0x30000 +#define NT_NO_LINK 0x0 +#define NT_LINK_AD_HOC 0x1 +#define NT_LINK_AP 0x2 +#define NT_AS_AP 0x3 + +#define _LBMODE(x) (((x) & 0xF) << 24) +#define MASK_LBMODE 0xF000000 +#define LOOPBACK_NORMAL 0x0 +#define LOOPBACK_IMMEDIATELY 0xB +#define LOOPBACK_MAC_DELAY 0x3 +#define LOOPBACK_PHY 0x1 +#define LOOPBACK_DMA 0x7 + +#define GET_RX_PAGE_SIZE(value) ((value) & 0xF) +#define GET_TX_PAGE_SIZE(value) (((value) & 0xF0) >> 4) +#define _PSRX_MASK 0xF +#define _PSTX_MASK 0xF0 +#define _PSRX(x) (x) +#define _PSTX(x) ((x) << 4) + +#define PBP_64 0x0 +#define PBP_128 0x1 +#define PBP_256 0x2 +#define PBP_512 0x3 +#define PBP_1024 0x4 + +#define RXDMA_ARBBW_EN BIT(0) +#define RXSHFT_EN BIT(1) +#define RXDMA_AGG_EN BIT(2) +#define QS_VO_QUEUE BIT(8) +#define QS_VI_QUEUE BIT(9) +#define QS_BE_QUEUE BIT(10) +#define QS_BK_QUEUE BIT(11) +#define QS_MANAGER_QUEUE BIT(12) +#define QS_HIGH_QUEUE BIT(13) + +#define HQSEL_VOQ BIT(0) +#define HQSEL_VIQ BIT(1) +#define HQSEL_BEQ BIT(2) +#define HQSEL_BKQ BIT(3) +#define HQSEL_MGTQ BIT(4) +#define HQSEL_HIQ BIT(5) + +#define _TXDMA_HIQ_MAP(x) (((x)&0x3) << 14) +#define _TXDMA_MGQ_MAP(x) (((x)&0x3) << 12) +#define _TXDMA_BKQ_MAP(x) (((x)&0x3) << 10) +#define _TXDMA_BEQ_MAP(x) (((x)&0x3) << 8) +#define _TXDMA_VIQ_MAP(x) (((x)&0x3) << 6) +#define _TXDMA_VOQ_MAP(x) (((x)&0x3) << 4) + +#define QUEUE_LOW 1 +#define QUEUE_NORMAL 2 +#define QUEUE_HIGH 3 + +#define _LLT_NO_ACTIVE 0x0 +#define _LLT_WRITE_ACCESS 0x1 +#define _LLT_READ_ACCESS 0x2 + +#define _LLT_INIT_DATA(x) ((x) & 0xFF) +#define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8) +#define _LLT_OP(x) (((x) & 0x3) << 30) +#define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3) + +#define BB_WRITE_READ_MASK (BIT(31) | BIT(30)) +#define BB_WRITE_EN BIT(30) +#define BB_READ_EN BIT(31) + +#define _HPQ(x) ((x) & 0xFF) +#define _LPQ(x) (((x) & 0xFF) << 8) +#define _PUBQ(x) (((x) & 0xFF) << 16) +#define _NPQ(x) ((x) & 0xFF) + +#define HPQ_PUBLIC_DIS BIT(24) +#define LPQ_PUBLIC_DIS BIT(25) +#define LD_RQPN BIT(31) + +#define BCN_VALID BIT(16) +#define BCN_HEAD(x) (((x) & 0xFF) << 8) +#define BCN_HEAD_MASK 0xFF00 + +#define BLK_DESC_NUM_SHIFT 4 +#define BLK_DESC_NUM_MASK 0xF + +#define DROP_DATA_EN BIT(9) + +#define EN_AMPDU_RTY_NEW BIT(7) + +#define _INIRTSMCS_SEL(x) ((x) & 0x3F) + +#define _SPEC_SIFS_CCK(x) ((x) & 0xFF) +#define _SPEC_SIFS_OFDM(x) (((x) & 0xFF) << 8) + +#define RATE_REG_BITMAP_ALL 0xFFFFF + +#define _RRSC_BITMAP(x) ((x) & 0xFFFFF) + +#define _RRSR_RSC(x) (((x) & 0x3) << 21) +#define RRSR_RSC_RESERVED 0x0 +#define RRSR_RSC_UPPER_SUBCHANNEL 0x1 +#define RRSR_RSC_LOWER_SUBCHANNEL 0x2 +#define RRSR_RSC_DUPLICATE_MODE 0x3 + +#define USE_SHORT_G1 BIT(20) + +#define _AGGLMT_MCS0(x) ((x) & 0xF) +#define _AGGLMT_MCS1(x) (((x) & 0xF) << 4) +#define _AGGLMT_MCS2(x) (((x) & 0xF) << 8) +#define _AGGLMT_MCS3(x) (((x) & 0xF) << 12) +#define _AGGLMT_MCS4(x) (((x) & 0xF) << 16) +#define _AGGLMT_MCS5(x) (((x) & 0xF) << 20) +#define _AGGLMT_MCS6(x) (((x) & 0xF) << 24) +#define _AGGLMT_MCS7(x) (((x) & 0xF) << 28) + +#define RETRY_LIMIT_SHORT_SHIFT 8 +#define RETRY_LIMIT_LONG_SHIFT 0 + +#define _DARF_RC1(x) ((x) & 0x1F) +#define _DARF_RC2(x) (((x) & 0x1F) << 8) +#define _DARF_RC3(x) (((x) & 0x1F) << 16) +#define _DARF_RC4(x) (((x) & 0x1F) << 24) +#define _DARF_RC5(x) ((x) & 0x1F) +#define _DARF_RC6(x) (((x) & 0x1F) << 8) +#define _DARF_RC7(x) (((x) & 0x1F) << 16) +#define _DARF_RC8(x) (((x) & 0x1F) << 24) + +#define _RARF_RC1(x) ((x) & 0x1F) +#define _RARF_RC2(x) (((x) & 0x1F) << 8) +#define _RARF_RC3(x) (((x) & 0x1F) << 16) +#define _RARF_RC4(x) (((x) & 0x1F) << 24) +#define _RARF_RC5(x) ((x) & 0x1F) +#define _RARF_RC6(x) (((x) & 0x1F) << 8) +#define _RARF_RC7(x) (((x) & 0x1F) << 16) +#define _RARF_RC8(x) (((x) & 0x1F) << 24) + +#define AC_PARAM_TXOP_LIMIT_OFFSET 16 +#define AC_PARAM_ECW_MAX_OFFSET 12 +#define AC_PARAM_ECW_MIN_OFFSET 8 +#define AC_PARAM_AIFS_OFFSET 0 + +#define _AIFS(x) (x) +#define _ECW_MAX_MIN(x) ((x) << 8) +#define _TXOP_LIMIT(x) ((x) << 16) + +#define _BCNIFS(x) ((x) & 0xFF) +#define _BCNECW(x) ((((x) & 0xF)) << 8) + +#define _LRL(x) ((x) & 0x3F) +#define _SRL(x) (((x) & 0x3F) << 8) + +#define _SIFS_CCK_CTX(x) ((x) & 0xFF) +#define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8) + +#define _SIFS_OFDM_CTX(x) ((x) & 0xFF) +#define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8) + +#define _TBTT_PROHIBIT_HOLD(x) (((x) & 0xFF) << 8) + +#define DIS_EDCA_CNT_DWN BIT(11) + +#define EN_MBSSID BIT(1) +#define EN_TXBCN_RPT BIT(2) +#define EN_BCN_FUNCTION BIT(3) + +#define TSFTR_RST BIT(0) +#define TSFTR1_RST BIT(1) + +#define STOP_BCNQ BIT(6) + +#define DIS_TSF_UDT0_NORMAL_CHIP BIT(4) +#define DIS_TSF_UDT0_TEST_CHIP BIT(5) + +#define ACMHW_HWEN BIT(0) +#define ACMHW_BEQEN BIT(1) +#define ACMHW_VIQEN BIT(2) +#define ACMHW_VOQEN BIT(3) +#define ACMHW_BEQSTATUS BIT(4) +#define ACMHW_VIQSTATUS BIT(5) +#define ACMHW_VOQSTATUS BIT(6) + +#define APSDOFF BIT(6) +#define APSDOFF_STATUS BIT(7) + +#define BW_20MHZ BIT(2) + +#define RATE_BITMAP_ALL 0xFFFFF + +#define RATE_RRSR_CCK_ONLY_1M 0xFFFF1 + +#define TSFRST BIT(0) +#define DIS_GCLK BIT(1) +#define PAD_SEL BIT(2) +#define PWR_ST BIT(6) +#define PWRBIT_OW_EN BIT(7) +#define ACRC BIT(8) +#define CFENDFORM BIT(9) +#define ICV BIT(10) + +#define AAP BIT(0) +#define APM BIT(1) +#define AM BIT(2) +#define AB BIT(3) +#define ADD3 BIT(4) +#define APWRMGT BIT(5) +#define CBSSID BIT(6) +#define CBSSID_DATA BIT(6) +#define CBSSID_BCN BIT(7) +#define ACRC32 BIT(8) +#define AICV BIT(9) +#define ADF BIT(11) +#define ACF BIT(12) +#define AMF BIT(13) +#define HTC_LOC_CTRL BIT(14) +#define UC_DATA_EN BIT(16) +#define BM_DATA_EN BIT(17) +#define MFBEN BIT(22) +#define LSIGEN BIT(23) +#define ENMBID BIT(24) +#define APP_BASSN BIT(27) +#define APP_PHYSTS BIT(28) +#define APP_ICV BIT(29) +#define APP_MIC BIT(30) +#define APP_FCS BIT(31) + +#define _MIN_SPACE(x) ((x) & 0x7) +#define _SHORT_GI_PADDING(x) (((x) & 0x1F) << 3) + +#define RXERR_TYPE_OFDM_PPDU 0 +#define RXERR_TYPE_OFDM_FALSE_ALARM 1 +#define RXERR_TYPE_OFDM_MPDU_OK 2 +#define RXERR_TYPE_OFDM_MPDU_FAIL 3 +#define RXERR_TYPE_CCK_PPDU 4 +#define RXERR_TYPE_CCK_FALSE_ALARM 5 +#define RXERR_TYPE_CCK_MPDU_OK 6 +#define RXERR_TYPE_CCK_MPDU_FAIL 7 +#define RXERR_TYPE_HT_PPDU 8 +#define RXERR_TYPE_HT_FALSE_ALARM 9 +#define RXERR_TYPE_HT_MPDU_TOTAL 10 +#define RXERR_TYPE_HT_MPDU_OK 11 +#define RXERR_TYPE_HT_MPDU_FAIL 12 +#define RXERR_TYPE_RX_FULL_DROP 15 + +#define RXERR_COUNTER_MASK 0xFFFFF +#define RXERR_RPT_RST BIT(27) +#define _RXERR_RPT_SEL(type) ((type) << 28) + +#define SCR_TXUSEDK BIT(0) +#define SCR_RXUSEDK BIT(1) +#define SCR_TXENCENABLE BIT(2) +#define SCR_RXDECENABLE BIT(3) +#define SCR_SKBYA2 BIT(4) +#define SCR_NOSKMC BIT(5) +#define SCR_TXBCUSEDK BIT(6) +#define SCR_RXBCUSEDK BIT(7) + +#define XCLK_VLD BIT(0) +#define ACLK_VLD BIT(1) +#define UCLK_VLD BIT(2) +#define PCLK_VLD BIT(3) +#define PCIRSTB BIT(4) +#define V15_VLD BIT(5) +#define TRP_B15V_EN BIT(7) +#define SIC_IDLE BIT(8) +#define BD_MAC2 BIT(9) +#define BD_MAC1 BIT(10) +#define IC_MACPHY_MODE BIT(11) +#define BT_FUNC BIT(16) +#define VENDOR_ID BIT(19) +#define PAD_HWPD_IDN BIT(22) +#define TRP_VAUX_EN BIT(23) +#define TRP_BT_EN BIT(24) +#define BD_PKG_SEL BIT(25) +#define BD_HCI_SEL BIT(26) +#define TYPE_ID BIT(27) + +#define USB_IS_HIGH_SPEED 0 +#define USB_IS_FULL_SPEED 1 +#define USB_SPEED_MASK BIT(5) + +#define USB_NORMAL_SIE_EP_MASK 0xF +#define USB_NORMAL_SIE_EP_SHIFT 4 + +#define USB_TEST_EP_MASK 0x30 +#define USB_TEST_EP_SHIFT 4 + +#define USB_AGG_EN BIT(3) + +#define MAC_ADDR_LEN 6 +#define LAST_ENTRY_OF_TX_PKT_BUFFER 175/*255 88e*/ + +#define POLLING_LLT_THRESHOLD 20 +#define POLLING_READY_TIMEOUT_COUNT 3000 + +#define MAX_MSS_DENSITY_2T 0x13 +#define MAX_MSS_DENSITY_1T 0x0A + +#define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6)) +#define EPROM_CMD_CONFIG 0x3 +#define EPROM_CMD_LOAD 1 + +#define HWSET_MAX_SIZE_92S HWSET_MAX_SIZE + +#define HAL_8192C_HW_GPIO_WPS_BIT BIT(2) + +#define RPMAC_RESET 0x100 +#define RPMAC_TXSTART 0x104 +#define RPMAC_TXLEGACYSIG 0x108 +#define RPMAC_TXHTSIG1 0x10c +#define RPMAC_TXHTSIG2 0x110 +#define RPMAC_PHYDEBUG 0x114 +#define RPMAC_TXPACKETNUM 0x118 +#define RPMAC_TXIDLE 0x11c +#define RPMAC_TXMACHEADER0 0x120 +#define RPMAC_TXMACHEADER1 0x124 +#define RPMAC_TXMACHEADER2 0x128 +#define RPMAC_TXMACHEADER3 0x12c +#define RPMAC_TXMACHEADER4 0x130 +#define RPMAC_TXMACHEADER5 0x134 +#define RPMAC_TXDADATYPE 0x138 +#define RPMAC_TXRANDOMSEED 0x13c +#define RPMAC_CCKPLCPPREAMBLE 0x140 +#define RPMAC_CCKPLCPHEADER 0x144 +#define RPMAC_CCKCRC16 0x148 +#define RPMAC_OFDMRXCRC32OK 0x170 +#define RPMAC_OFDMRXCRC32ER 0x174 +#define RPMAC_OFDMRXPARITYER 0x178 +#define RPMAC_OFDMRXCRC8ER 0x17c +#define RPMAC_CCKCRXRC16ER 0x180 +#define RPMAC_CCKCRXRC32ER 0x184 +#define RPMAC_CCKCRXRC32OK 0x188 +#define RPMAC_TXSTATUS 0x18c + +#define RFPGA0_RFMOD 0x800 + +#define RFPGA0_TXINFO 0x804 +#define RFPGA0_PSDFUNCTION 0x808 + +#define RFPGA0_TXGAINSTAGE 0x80c + +#define RFPGA0_RFTIMING1 0x810 +#define RFPGA0_RFTIMING2 0x814 + +#define RFPGA0_XA_HSSIPARAMETER1 0x820 +#define RFPGA0_XA_HSSIPARAMETER2 0x824 +#define RFPGA0_XB_HSSIPARAMETER1 0x828 +#define RFPGA0_XB_HSSIPARAMETER2 0x82c + +#define RFPGA0_XA_LSSIPARAMETER 0x840 +#define RFPGA0_XB_LSSIPARAMETER 0x844 + +#define RFPGA0_RFWAKEUPPARAMETER 0x850 +#define RFPGA0_RFSLEEPUPPARAMETER 0x854 + +#define RFPGA0_XAB_SWITCHCONTROL 0x858 +#define RFPGA0_XCD_SWITCHCONTROL 0x85c + +#define RFPGA0_XA_RFINTERFACEOE 0x860 +#define RFPGA0_XB_RFINTERFACEOE 0x864 + +#define RFPGA0_XAB_RFINTERFACESW 0x870 +#define RFPGA0_XCD_RFINTERFACESW 0x874 + +#define RFPGA0_XAB_RFPARAMETER 0x878 +#define RFPGA0_XCD_RFPARAMETER 0x87c + +#define RFPGA0_ANALOGPARAMETER1 0x880 +#define RFPGA0_ANALOGPARAMETER2 0x884 +#define RFPGA0_ANALOGPARAMETER3 0x888 +#define RFPGA0_ANALOGPARAMETER4 0x88c + +#define RFPGA0_XA_LSSIREADBACK 0x8a0 +#define RFPGA0_XB_LSSIREADBACK 0x8a4 +#define RFPGA0_XC_LSSIREADBACK 0x8a8 +#define RFPGA0_XD_LSSIREADBACK 0x8ac + +#define RFPGA0_PSDREPORT 0x8b4 +#define TRANSCEIVEA_HSPI_READBACK 0x8b8 +#define TRANSCEIVEB_HSPI_READBACK 0x8bc +#define REG_SC_CNT 0x8c4 +#define RFPGA0_XAB_RFINTERFACERB 0x8e0 +#define RFPGA0_XCD_RFINTERFACERB 0x8e4 + +#define RFPGA1_RFMOD 0x900 + +#define RFPGA1_TXBLOCK 0x904 +#define RFPGA1_DEBUGSELECT 0x908 +#define RFPGA1_TXINFO 0x90c + +#define RCCK0_SYSTEM 0xa00 + +#define RCCK0_AFESETTING 0xa04 +#define RCCK0_CCA 0xa08 + +#define RCCK0_RXAGC1 0xa0c +#define RCCK0_RXAGC2 0xa10 + +#define RCCK0_RXHP 0xa14 + +#define RCCK0_DSPPARAMETER1 0xa18 +#define RCCK0_DSPPARAMETER2 0xa1c + +#define RCCK0_TXFILTER1 0xa20 +#define RCCK0_TXFILTER2 0xa24 +#define RCCK0_DEBUGPORT 0xa28 +#define RCCK0_FALSEALARMREPORT 0xa2c +#define RCCK0_TRSSIREPORT 0xa50 +#define RCCK0_RXREPORT 0xa54 +#define RCCK0_FACOUNTERLOWER 0xa5c +#define RCCK0_FACOUNTERUPPER 0xa58 +#define RCCK0_CCA_CNT 0xa60 + + +/* PageB(0xB00) */ +#define RPDP_ANTA 0xb00 +#define RPDP_ANTA_4 0xb04 +#define RPDP_ANTA_8 0xb08 +#define RPDP_ANTA_C 0xb0c +#define RPDP_ANTA_10 0xb10 +#define RPDP_ANTA_14 0xb14 +#define RPDP_ANTA_18 0xb18 +#define RPDP_ANTA_1C 0xb1c +#define RPDP_ANTA_20 0xb20 +#define RPDP_ANTA_24 0xb24 + +#define RCONFIG_PMPD_ANTA 0xb28 +#define CONFIG_RAM64X16 0xb2c + +#define RBNDA 0xb30 +#define RHSSIPAR 0xb34 + +#define RCONFIG_ANTA 0xb68 +#define RCONFIG_ANTB 0xb6c + +#define RPDP_ANTB 0xb70 +#define RPDP_ANTB_4 0xb74 +#define RPDP_ANTB_8 0xb78 +#define RPDP_ANTB_C 0xb7c +#define RPDP_ANTB_10 0xb80 +#define RPDP_ANTB_14 0xb84 +#define RPDP_ANTB_18 0xb88 +#define RPDP_ANTB_1C 0xb8c +#define RPDP_ANTB_20 0xb90 +#define RPDP_ANTB_24 0xb94 + +#define RCONFIG_PMPD_ANTB 0xb98 + +#define RBNDB 0xba0 + +#define RAPK 0xbd8 +#define RPM_RX0_ANTA 0xbdc +#define RPM_RX1_ANTA 0xbe0 +#define RPM_RX2_ANTA 0xbe4 +#define RPM_RX3_ANTA 0xbe8 +#define RPM_RX0_ANTB 0xbec +#define RPM_RX1_ANTB 0xbf0 +#define RPM_RX2_ANTB 0xbf4 +#define RPM_RX3_ANTB 0xbf8 + +/*Page C*/ +#define ROFDM0_LSTF 0xc00 + +#define ROFDM0_TRXPATHENABLE 0xc04 +#define ROFDM0_TRMUXPAR 0xc08 +#define ROFDM0_TRSWISOLATION 0xc0c + +#define ROFDM0_XARXAFE 0xc10 +#define ROFDM0_XARXIQIMBALANCE 0xc14 +#define ROFDM0_XBRXAFE 0xc18 +#define ROFDM0_XBRXIQIMBALANCE 0xc1c +#define ROFDM0_XCRXAFE 0xc20 +#define ROFDM0_XCRXIQIMBANLANCE 0xc24 +#define ROFDM0_XDRXAFE 0xc28 +#define ROFDM0_XDRXIQIMBALANCE 0xc2c + +#define ROFDM0_RXDETECTOR1 0xc30 +#define ROFDM0_RXDETECTOR2 0xc34 +#define ROFDM0_RXDETECTOR3 0xc38 +#define ROFDM0_RXDETECTOR4 0xc3c + +#define ROFDM0_RXDSP 0xc40 +#define ROFDM0_CFOANDDAGC 0xc44 +#define ROFDM0_CCADROPTHRESHOLD 0xc48 +#define ROFDM0_ECCATHRESHOLD 0xc4c + +#define ROFDM0_XAAGCCORE1 0xc50 +#define ROFDM0_XAAGCCORE2 0xc54 +#define ROFDM0_XBAGCCORE1 0xc58 +#define ROFDM0_XBAGCCORE2 0xc5c +#define ROFDM0_XCAGCCORE1 0xc60 +#define ROFDM0_XCAGCCORE2 0xc64 +#define ROFDM0_XDAGCCORE1 0xc68 +#define ROFDM0_XDAGCCORE2 0xc6c + +#define ROFDM0_AGCPARAMETER1 0xc70 +#define ROFDM0_AGCPARAMETER2 0xc74 +#define ROFDM0_AGCRSSITABLE 0xc78 +#define ROFDM0_HTSTFAGC 0xc7c + +#define ROFDM0_XATXIQIMBALANCE 0xc80 +#define ROFDM0_XATXAFE 0xc84 +#define ROFDM0_XBTXIQIMBALANCE 0xc88 +#define ROFDM0_XBTXAFE 0xc8c +#define ROFDM0_XCTXIQIMBALANCE 0xc90 +#define ROFDM0_XCTXAFE 0xc94 +#define ROFDM0_XDTXIQIMBALANCE 0xc98 +#define ROFDM0_XDTXAFE 0xc9c + +#define ROFDM0_RXIQEXTANTA 0xca0 +#define ROFDM0_TXCOEFF1 0xca4 +#define ROFDM0_TXCOEFF2 0xca8 +#define ROFDM0_TXCOEFF3 0xcac +#define ROFDM0_TXCOEFF4 0xcb0 +#define ROFDM0_TXCOEFF5 0xcb4 +#define ROFDM0_TXCOEFF6 0xcb8 + +#define ROFDM0_RXHPPARAMETER 0xce0 +#define ROFDM0_TXPSEUDONOISEWGT 0xce4 +#define ROFDM0_FRAMESYNC 0xcf0 +#define ROFDM0_DFSREPORT 0xcf4 + + +#define ROFDM1_LSTF 0xd00 +#define ROFDM1_TRXPATHENABLE 0xd04 + +#define ROFDM1_CF0 0xd08 +#define ROFDM1_CSI1 0xd10 +#define ROFDM1_SBD 0xd14 +#define ROFDM1_CSI2 0xd18 +#define ROFDM1_CFOTRACKING 0xd2c +#define ROFDM1_TRXMESAURE1 0xd34 +#define ROFDM1_INTFDET 0xd3c +#define ROFDM1_PSEUDONOISESTATEAB 0xd50 +#define ROFDM1_PSEUDONOISESTATECD 0xd54 +#define ROFDM1_RXPSEUDONOISEWGT 0xd58 + +#define ROFDM_PHYCOUNTER1 0xda0 +#define ROFDM_PHYCOUNTER2 0xda4 +#define ROFDM_PHYCOUNTER3 0xda8 + +#define ROFDM_SHORTCFOAB 0xdac +#define ROFDM_SHORTCFOCD 0xdb0 +#define ROFDM_LONGCFOAB 0xdb4 +#define ROFDM_LONGCFOCD 0xdb8 +#define ROFDM_TAILCF0AB 0xdbc +#define ROFDM_TAILCF0CD 0xdc0 +#define ROFDM_PWMEASURE1 0xdc4 +#define ROFDM_PWMEASURE2 0xdc8 +#define ROFDM_BWREPORT 0xdcc +#define ROFDM_AGCREPORT 0xdd0 +#define ROFDM_RXSNR 0xdd4 +#define ROFDM_RXEVMCSI 0xdd8 +#define ROFDM_SIGREPORT 0xddc + +#define RTXAGC_A_RATE18_06 0xe00 +#define RTXAGC_A_RATE54_24 0xe04 +#define RTXAGC_A_CCK1_MCS32 0xe08 +#define RTXAGC_A_MCS03_MCS00 0xe10 +#define RTXAGC_A_MCS07_MCS04 0xe14 +#define RTXAGC_A_MCS11_MCS08 0xe18 +#define RTXAGC_A_MCS15_MCS12 0xe1c + +#define RTXAGC_B_RATE18_06 0x830 +#define RTXAGC_B_RATE54_24 0x834 +#define RTXAGC_B_CCK1_55_MCS32 0x838 +#define RTXAGC_B_MCS03_MCS00 0x83c +#define RTXAGC_B_MCS07_MCS04 0x848 +#define RTXAGC_B_MCS11_MCS08 0x84c +#define RTXAGC_B_MCS15_MCS12 0x868 +#define RTXAGC_B_CCK11_A_CCK2_11 0x86c + +#define RFPGA0_IQK 0xe28 +#define RTX_IQK_TONE_A 0xe30 +#define RRX_IQK_TONE_A 0xe34 +#define RTX_IQK_PI_A 0xe38 +#define RRX_IQK_PI_A 0xe3c + +#define RTX_IQK 0xe40 +#define RRX_IQK 0xe44 +#define RIQK_AGC_PTS 0xe48 +#define RIQK_AGC_RSP 0xe4c +#define RTX_IQK_TONE_B 0xe50 +#define RRX_IQK_TONE_B 0xe54 +#define RTX_IQK_PI_B 0xe58 +#define RRX_IQK_PI_B 0xe5c +#define RIQK_AGC_CONT 0xe60 + +#define RBLUE_TOOTH 0xe6c +#define RRX_WAIT_CCA 0xe70 +#define RTX_CCK_RFON 0xe74 +#define RTX_CCK_BBON 0xe78 +#define RTX_OFDM_RFON 0xe7c +#define RTX_OFDM_BBON 0xe80 +#define RTX_TO_RX 0xe84 +#define RTX_TO_TX 0xe88 +#define RRX_CCK 0xe8c + +#define RTX_POWER_BEFORE_IQK_A 0xe94 +#define RTX_POWER_AFTER_IQK_A 0xe9c + +#define RRX_POWER_BEFORE_IQK_A 0xea0 +#define RRX_POWER_BEFORE_IQK_A_2 0xea4 +#define RRX_POWER_AFTER_IQK_A 0xea8 +#define RRX_POWER_AFTER_IQK_A_2 0xeac + +#define RTX_POWER_BEFORE_IQK_B 0xeb4 +#define RTX_POWER_AFTER_IQK_B 0xebc + +#define RRX_POWER_BEFORE_IQK_B 0xec0 +#define RRX_POWER_BEFORE_IQK_B_2 0xec4 +#define RRX_POWER_AFTER_IQK_B 0xec8 +#define RRX_POWER_AFTER_IQK_B_2 0xecc + +#define RRX_OFDM 0xed0 +#define RRX_WAIT_RIFS 0xed4 +#define RRX_TO_RX 0xed8 +#define RSTANDBY 0xedc +#define RSLEEP 0xee0 +#define RPMPD_ANAEN 0xeec + +#define RZEBRA1_HSSIENABLE 0x0 +#define RZEBRA1_TRXENABLE1 0x1 +#define RZEBRA1_TRXENABLE2 0x2 +#define RZEBRA1_AGC 0x4 +#define RZEBRA1_CHARGEPUMP 0x5 +#define RZEBRA1_CHANNEL 0x7 + +#define RZEBRA1_TXGAIN 0x8 +#define RZEBRA1_TXLPF 0x9 +#define RZEBRA1_RXLPF 0xb +#define RZEBRA1_RXHPFCORNER 0xc + +#define RGLOBALCTRL 0 +#define RRTL8256_TXLPF 19 +#define RRTL8256_RXLPF 11 +#define RRTL8258_TXLPF 0x11 +#define RRTL8258_RXLPF 0x13 +#define RRTL8258_RSSILPF 0xa + +#define RF_AC 0x00 + +#define RF_IQADJ_G1 0x01 +#define RF_IQADJ_G2 0x02 +#define RF_POW_TRSW 0x05 + +#define RF_GAIN_RX 0x06 +#define RF_GAIN_TX 0x07 + +#define RF_TXM_IDAC 0x08 +#define RF_BS_IQGEN 0x0F + +#define RF_MODE1 0x10 +#define RF_MODE2 0x11 + +#define RF_RX_AGC_HP 0x12 +#define RF_TX_AGC 0x13 +#define RF_BIAS 0x14 +#define RF_IPA 0x15 +#define RF_POW_ABILITY 0x17 +#define RF_MODE_AG 0x18 +#define RRFCHANNEL 0x18 +#define RF_CHNLBW 0x18 +#define RF_TOP 0x19 + +#define RF_RX_G1 0x1A +#define RF_RX_G2 0x1B + +#define RF_RX_BB2 0x1C +#define RF_RX_BB1 0x1D + +#define RF_RCK1 0x1E +#define RF_RCK2 0x1F + +#define RF_TX_G1 0x20 +#define RF_TX_G2 0x21 +#define RF_TX_G3 0x22 + +#define RF_TX_BB1 0x23 +#define RF_T_METER 0x42 + +#define RF_SYN_G1 0x25 +#define RF_SYN_G2 0x26 +#define RF_SYN_G3 0x27 +#define RF_SYN_G4 0x28 +#define RF_SYN_G5 0x29 +#define RF_SYN_G6 0x2A +#define RF_SYN_G7 0x2B +#define RF_SYN_G8 0x2C + +#define RF_RCK_OS 0x30 +#define RF_TXPA_G1 0x31 +#define RF_TXPA_G2 0x32 +#define RF_TXPA_G3 0x33 + +#define RF_TX_BIAS_A 0x35 +#define RF_TX_BIAS_D 0x36 +#define RF_LOBF_9 0x38 +#define RF_RXRF_A3 0x3C +#define RF_TRSW 0x3F + +#define RF_TXRF_A2 0x41 +#define RF_TXPA_G4 0x46 +#define RF_TXPA_A4 0x4B + +#define RF_WE_LUT 0xEF + +#define BBBRESETB 0x100 +#define BGLOBALRESETB 0x200 +#define BOFDMTXSTART 0x4 +#define BCCKTXSTART 0x8 +#define BCRC32DEBUG 0x100 +#define BPMACLOOPBACK 0x10 +#define BTXLSIG 0xffffff +#define BOFDMTXRATE 0xf +#define BOFDMTXRESERVED 0x10 +#define BOFDMTXLENGTH 0x1ffe0 +#define BOFDMTXPARITY 0x20000 +#define BTXHTSIG1 0xffffff +#define BTXHTMCSRATE 0x7f +#define BTXHTBW 0x80 +#define BTXHTLENGTH 0xffff00 +#define BTXHTSIG2 0xffffff +#define BTXHTSMOOTHING 0x1 +#define BTXHTSOUNDING 0x2 +#define BTXHTRESERVED 0x4 +#define BTXHTAGGREATION 0x8 +#define BTXHTSTBC 0x30 +#define BTXHTADVANCECODING 0x40 +#define BTXHTSHORTGI 0x80 +#define BTXHTNUMBERHT_LTF 0x300 +#define BTXHTCRC8 0x3fc00 +#define BCOUNTERRESET 0x10000 +#define BNUMOFOFDMTX 0xffff +#define BNUMOFCCKTX 0xffff0000 +#define BTXIDLEINTERVAL 0xffff +#define BOFDMSERVICE 0xffff0000 +#define BTXMACHEADER 0xffffffff +#define BTXDATAINIT 0xff +#define BTXHTMODE 0x100 +#define BTXDATATYPE 0x30000 +#define BTXRANDOMSEED 0xffffffff +#define BCCKTXPREAMBLE 0x1 +#define BCCKTXSFD 0xffff0000 +#define BCCKTXSIG 0xff +#define BCCKTXSERVICE 0xff00 +#define BCCKLENGTHEXT 0x8000 +#define BCCKTXLENGHT 0xffff0000 +#define BCCKTXCRC16 0xffff +#define BCCKTXSTATUS 0x1 +#define BOFDMTXSTATUS 0x2 +#define IS_BB_REG_OFFSET_92S(_offset) \ + ((_offset >= 0x800) && (_offset <= 0xfff)) + +#define BRFMOD 0x1 +#define BJAPANMODE 0x2 +#define BCCKTXSC 0x30 +#define BCCKEN 0x1000000 +#define BOFDMEN 0x2000000 + +#define BOFDMRXADCPHASE 0x10000 +#define BOFDMTXDACPHASE 0x40000 +#define BXATXAGC 0x3f + +#define BXBTXAGC 0xf00 +#define BXCTXAGC 0xf000 +#define BXDTXAGC 0xf0000 + +#define BPASTART 0xf0000000 +#define BTRSTART 0x00f00000 +#define BRFSTART 0x0000f000 +#define BBBSTART 0x000000f0 +#define BBBCCKSTART 0x0000000f +#define BPAEND 0xf +#define BTREND 0x0f000000 +#define BRFEND 0x000f0000 +#define BCCAMASK 0x000000f0 +#define BR2RCCAMASK 0x00000f00 +#define BHSSI_R2TDELAY 0xf8000000 +#define BHSSI_T2RDELAY 0xf80000 +#define BCONTXHSSI 0x400 +#define BIGFROMCCK 0x200 +#define BAGCADDRESS 0x3f +#define BRXHPTX 0x7000 +#define BRXHP2RX 0x38000 +#define BRXHPCCKINI 0xc0000 +#define BAGCTXCODE 0xc00000 +#define BAGCRXCODE 0x300000 + +#define B3WIREDATALENGTH 0x800 +#define B3WIREADDREAALENGTH 0x400 + +#define B3WIRERFPOWERDOWN 0x1 +#define B5GPAPEPOLARITY 0x40000000 +#define B2GPAPEPOLARITY 0x80000000 +#define BRFSW_TXDEFAULTANT 0x3 +#define BRFSW_TXOPTIONANT 0x30 +#define BRFSW_RXDEFAULTANT 0x300 +#define BRFSW_RXOPTIONANT 0x3000 +#define BRFSI_3WIREDATA 0x1 +#define BRFSI_3WIRECLOCK 0x2 +#define BRFSI_3WIRELOAD 0x4 +#define BRFSI_3WIRERW 0x8 +#define BRFSI_3WIRE 0xf + +#define BRFSI_RFENV 0x10 + +#define BRFSI_TRSW 0x20 +#define BRFSI_TRSWB 0x40 +#define BRFSI_ANTSW 0x100 +#define BRFSI_ANTSWB 0x200 +#define BRFSI_PAPE 0x400 +#define BRFSI_PAPE5G 0x800 +#define BBANDSELECT 0x1 +#define BHTSIG2_GI 0x80 +#define BHTSIG2_SMOOTHING 0x01 +#define BHTSIG2_SOUNDING 0x02 +#define BHTSIG2_AGGREATON 0x08 +#define BHTSIG2_STBC 0x30 +#define BHTSIG2_ADVCODING 0x40 +#define BHTSIG2_NUMOFHTLTF 0x300 +#define BHTSIG2_CRC8 0x3fc +#define BHTSIG1_MCS 0x7f +#define BHTSIG1_BANDWIDTH 0x80 +#define BHTSIG1_HTLENGTH 0xffff +#define BLSIG_RATE 0xf +#define BLSIG_RESERVED 0x10 +#define BLSIG_LENGTH 0x1fffe +#define BLSIG_PARITY 0x20 +#define BCCKRXPHASE 0x4 + +#define BLSSIREADADDRESS 0x7f800000 +#define BLSSIREADEDGE 0x80000000 + +#define BLSSIREADBACKDATA 0xfffff + +#define BLSSIREADOKFLAG 0x1000 +#define BCCKSAMPLERATE 0x8 +#define BREGULATOR0STANDBY 0x1 +#define BREGULATORPLLSTANDBY 0x2 +#define BREGULATOR1STANDBY 0x4 +#define BPLLPOWERUP 0x8 +#define BDPLLPOWERUP 0x10 +#define BDA10POWERUP 0x20 +#define BAD7POWERUP 0x200 +#define BDA6POWERUP 0x2000 +#define BXTALPOWERUP 0x4000 +#define B40MDCLKPOWERUP 0x8000 +#define BDA6DEBUGMODE 0x20000 +#define BDA6SWING 0x380000 + +#define BADCLKPHASE 0x4000000 +#define B80MCLKDELAY 0x18000000 +#define BAFEWATCHDOGENABLE 0x20000000 + +#define BXTALCAP01 0xc0000000 +#define BXTALCAP23 0x3 +#define BXTALCAP92X 0x0f000000 +#define BXTALCAP 0x0f000000 + +#define BINTDIFCLKENABLE 0x400 +#define BEXTSIGCLKENABLE 0x800 +#define BBANDGAP_MBIAS_POWERUP 0x10000 +#define BAD11SH_GAIN 0xc0000 +#define BAD11NPUT_RANGE 0x700000 +#define BAD110P_CURRENT 0x3800000 +#define BLPATH_LOOPBACK 0x4000000 +#define BQPATH_LOOPBACK 0x8000000 +#define BAFE_LOOPBACK 0x10000000 +#define BDA10_SWING 0x7e0 +#define BDA10_REVERSE 0x800 +#define BDA_CLK_SOURCE 0x1000 +#define BDA7INPUT_RANGE 0x6000 +#define BDA7_GAIN 0x38000 +#define BDA7OUTPUT_CM_MODE 0x40000 +#define BDA7INPUT_CM_MODE 0x380000 +#define BDA7CURRENT 0xc00000 +#define BREGULATOR_ADJUST 0x7000000 +#define BAD11POWERUP_ATTX 0x1 +#define BDA10PS_ATTX 0x10 +#define BAD11POWERUP_ATRX 0x100 +#define BDA10PS_ATRX 0x1000 +#define BCCKRX_AGC_FORMAT 0x200 +#define BPSDFFT_SAMPLE_POINT 0xc000 +#define BPSD_AVERAGE_NUM 0x3000 +#define BIQPATH_CONTROL 0xc00 +#define BPSD_FREQ 0x3ff +#define BPSD_ANTENNA_PATH 0x30 +#define BPSD_IQ_SWITCH 0x40 +#define BPSD_RX_TRIGGER 0x400000 +#define BPSD_TX_TRIGGER 0x80000000 +#define BPSD_SINE_TONE_SCALE 0x7f000000 +#define BPSD_REPORT 0xffff + +#define BOFDM_TXSC 0x30000000 +#define BCCK_TXON 0x1 +#define BOFDM_TXON 0x2 +#define BDEBUG_PAGE 0xfff +#define BDEBUG_ITEM 0xff +#define BANTL 0x10 +#define BANT_NONHT 0x100 +#define BANT_HT1 0x1000 +#define BANT_HT2 0x10000 +#define BANT_HT1S1 0x100000 +#define BANT_NONHTS1 0x1000000 + +#define BCCK_BBMODE 0x3 +#define BCCK_TXPOWERSAVING 0x80 +#define BCCK_RXPOWERSAVING 0x40 + +#define BCCK_SIDEBAND 0x10 + +#define BCCK_SCRAMBLE 0x8 +#define BCCK_ANTDIVERSITY 0x8000 +#define BCCK_CARRIER_RECOVERY 0x4000 +#define BCCK_TXRATE 0x3000 +#define BCCK_DCCANCEL 0x0800 +#define BCCK_ISICANCEL 0x0400 +#define BCCK_MATCH_FILTER 0x0200 +#define BCCK_EQUALIZER 0x0100 +#define BCCK_PREAMBLE_DETECT 0x800000 +#define BCCK_FAST_FALSECCA 0x400000 +#define BCCK_CH_ESTSTART 0x300000 +#define BCCK_CCA_COUNT 0x080000 +#define BCCK_CS_LIM 0x070000 +#define BCCK_BIST_MODE 0x80000000 +#define BCCK_CCAMASK 0x40000000 +#define BCCK_TX_DAC_PHASE 0x4 +#define BCCK_RX_ADC_PHASE 0x20000000 +#define BCCKR_CP_MODE 0x0100 +#define BCCK_TXDC_OFFSET 0xf0 +#define BCCK_RXDC_OFFSET 0xf +#define BCCK_CCA_MODE 0xc000 +#define BCCK_FALSECS_LIM 0x3f00 +#define BCCK_CS_RATIO 0xc00000 +#define BCCK_CORGBIT_SEL 0x300000 +#define BCCK_PD_LIM 0x0f0000 +#define BCCK_NEWCCA 0x80000000 +#define BCCK_RXHP_OF_IG 0x8000 +#define BCCK_RXIG 0x7f00 +#define BCCK_LNA_POLARITY 0x800000 +#define BCCK_RX1ST_BAIN 0x7f0000 +#define BCCK_RF_EXTEND 0x20000000 +#define BCCK_RXAGC_SATLEVEL 0x1f000000 +#define BCCK_RXAGC_SATCOUNT 0xe0 +#define BCCKRXRFSETTLE 0x1f +#define BCCK_FIXED_RXAGC 0x8000 +#define BCCK_ANTENNA_POLARITY 0x2000 +#define BCCK_TXFILTER_TYPE 0x0c00 +#define BCCK_RXAGC_REPORTTYPE 0x0300 +#define BCCK_RXDAGC_EN 0x80000000 +#define BCCK_RXDAGC_PERIOD 0x20000000 +#define BCCK_RXDAGC_SATLEVEL 0x1f000000 +#define BCCK_TIMING_RECOVERY 0x800000 +#define BCCK_TXC0 0x3f0000 +#define BCCK_TXC1 0x3f000000 +#define BCCK_TXC2 0x3f +#define BCCK_TXC3 0x3f00 +#define BCCK_TXC4 0x3f0000 +#define BCCK_TXC5 0x3f000000 +#define BCCK_TXC6 0x3f +#define BCCK_TXC7 0x3f00 +#define BCCK_DEBUGPORT 0xff0000 +#define BCCK_DAC_DEBUG 0x0f000000 +#define BCCK_FALSEALARM_ENABLE 0x8000 +#define BCCK_FALSEALARM_READ 0x4000 +#define BCCK_TRSSI 0x7f +#define BCCK_RXAGC_REPORT 0xfe +#define BCCK_RXREPORT_ANTSEL 0x80000000 +#define BCCK_RXREPORT_MFOFF 0x40000000 +#define BCCK_RXREPORT_SQLOSS 0x20000000 +#define BCCK_RXREPORT_PKTLOSS 0x10000000 +#define BCCK_RXREPORT_LOCKEDBIT 0x08000000 +#define BCCK_RXREPORT_RATEERROR 0x04000000 +#define BCCK_RXREPORT_RXRATE 0x03000000 +#define BCCK_RXFA_COUNTER_LOWER 0xff +#define BCCK_RXFA_COUNTER_UPPER 0xff000000 +#define BCCK_RXHPAGC_START 0xe000 +#define BCCK_RXHPAGC_FINAL 0x1c00 +#define BCCK_RXFALSEALARM_ENABLE 0x8000 +#define BCCK_FACOUNTER_FREEZE 0x4000 +#define BCCK_TXPATH_SEL 0x10000000 +#define BCCK_DEFAULT_RXPATH 0xc000000 +#define BCCK_OPTION_RXPATH 0x3000000 + +#define BNUM_OFSTF 0x3 +#define BSHIFT_L 0xc0 +#define BGI_TH 0xc +#define BRXPATH_A 0x1 +#define BRXPATH_B 0x2 +#define BRXPATH_C 0x4 +#define BRXPATH_D 0x8 +#define BTXPATH_A 0x1 +#define BTXPATH_B 0x2 +#define BTXPATH_C 0x4 +#define BTXPATH_D 0x8 +#define BTRSSI_FREQ 0x200 +#define BADC_BACKOFF 0x3000 +#define BDFIR_BACKOFF 0xc000 +#define BTRSSI_LATCH_PHASE 0x10000 +#define BRX_LDC_OFFSET 0xff +#define BRX_QDC_OFFSET 0xff00 +#define BRX_DFIR_MODE 0x1800000 +#define BRX_DCNF_TYPE 0xe000000 +#define BRXIQIMB_A 0x3ff +#define BRXIQIMB_B 0xfc00 +#define BRXIQIMB_C 0x3f0000 +#define BRXIQIMB_D 0xffc00000 +#define BDC_DC_NOTCH 0x60000 +#define BRXNB_NOTCH 0x1f000000 +#define BPD_TH 0xf +#define BPD_TH_OPT2 0xc000 +#define BPWED_TH 0x700 +#define BIFMF_WIN_L 0x800 +#define BPD_OPTION 0x1000 +#define BMF_WIN_L 0xe000 +#define BBW_SEARCH_L 0x30000 +#define BWIN_ENH_L 0xc0000 +#define BBW_TH 0x700000 +#define BED_TH2 0x3800000 +#define BBW_OPTION 0x4000000 +#define BRADIO_TH 0x18000000 +#define BWINDOW_L 0xe0000000 +#define BSBD_OPTION 0x1 +#define BFRAME_TH 0x1c +#define BFS_OPTION 0x60 +#define BDC_SLOPE_CHECK 0x80 +#define BFGUARD_COUNTER_DC_L 0xe00 +#define BFRAME_WEIGHT_SHORT 0x7000 +#define BSUB_TUNE 0xe00000 +#define BFRAME_DC_LENGTH 0xe000000 +#define BSBD_START_OFFSET 0x30000000 +#define BFRAME_TH_2 0x7 +#define BFRAME_GI2_TH 0x38 +#define BGI2_SYNC_EN 0x40 +#define BSARCH_SHORT_EARLY 0x300 +#define BSARCH_SHORT_LATE 0xc00 +#define BSARCH_GI2_LATE 0x70000 +#define BCFOANTSUM 0x1 +#define BCFOACC 0x2 +#define BCFOSTARTOFFSET 0xc +#define BCFOLOOPBACK 0x70 +#define BCFOSUMWEIGHT 0x80 +#define BDAGCENABLE 0x10000 +#define BTXIQIMB_A 0x3ff +#define BTXIQIMB_b 0xfc00 +#define BTXIQIMB_C 0x3f0000 +#define BTXIQIMB_D 0xffc00000 +#define BTXIDCOFFSET 0xff +#define BTXIQDCOFFSET 0xff00 +#define BTXDFIRMODE 0x10000 +#define BTXPESUDO_NOISEON 0x4000000 +#define BTXPESUDO_NOISE_A 0xff +#define BTXPESUDO_NOISE_B 0xff00 +#define BTXPESUDO_NOISE_C 0xff0000 +#define BTXPESUDO_NOISE_D 0xff000000 +#define BCCA_DROPOPTION 0x20000 +#define BCCA_DROPTHRES 0xfff00000 +#define BEDCCA_H 0xf +#define BEDCCA_L 0xf0 +#define BLAMBDA_ED 0x300 +#define BRX_INITIALGAIN 0x7f +#define BRX_ANTDIV_EN 0x80 +#define BRX_AGC_ADDRESS_FOR_LNA 0x7f00 +#define BRX_HIGHPOWER_FLOW 0x8000 +#define BRX_AGC_FREEZE_THRES 0xc0000 +#define BRX_FREEZESTEP_AGC1 0x300000 +#define BRX_FREEZESTEP_AGC2 0xc00000 +#define BRX_FREEZESTEP_AGC3 0x3000000 +#define BRX_FREEZESTEP_AGC0 0xc000000 +#define BRXRSSI_CMP_EN 0x10000000 +#define BRXQUICK_AGCEN 0x20000000 +#define BRXAGC_FREEZE_THRES_MODE 0x40000000 +#define BRX_OVERFLOW_CHECKTYPE 0x80000000 +#define BRX_AGCSHIFT 0x7f +#define BTRSW_TRI_ONLY 0x80 +#define BPOWER_THRES 0x300 +#define BRXAGC_EN 0x1 +#define BRXAGC_TOGETHER_EN 0x2 +#define BRXAGC_MIN 0x4 +#define BRXHP_INI 0x7 +#define BRXHP_TRLNA 0x70 +#define BRXHP_RSSI 0x700 +#define BRXHP_BBP1 0x7000 +#define BRXHP_BBP2 0x70000 +#define BRXHP_BBP3 0x700000 +#define BRSSI_H 0x7f0000 +#define BRSSI_GEN 0x7f000000 +#define BRXSETTLE_TRSW 0x7 +#define BRXSETTLE_LNA 0x38 +#define BRXSETTLE_RSSI 0x1c0 +#define BRXSETTLE_BBP 0xe00 +#define BRXSETTLE_RXHP 0x7000 +#define BRXSETTLE_ANTSW_RSSI 0x38000 +#define BRXSETTLE_ANTSW 0xc0000 +#define BRXPROCESS_TIME_DAGC 0x300000 +#define BRXSETTLE_HSSI 0x400000 +#define BRXPROCESS_TIME_BBPPW 0x800000 +#define BRXANTENNA_POWER_SHIFT 0x3000000 +#define BRSSI_TABLE_SELECT 0xc000000 +#define BRXHP_FINAL 0x7000000 +#define BRXHPSETTLE_BBP 0x7 +#define BRXHTSETTLE_HSSI 0x8 +#define BRXHTSETTLE_RXHP 0x70 +#define BRXHTSETTLE_BBPPW 0x80 +#define BRXHTSETTLE_IDLE 0x300 +#define BRXHTSETTLE_RESERVED 0x1c00 +#define BRXHT_RXHP_EN 0x8000 +#define BRXAGC_FREEZE_THRES 0x30000 +#define BRXAGC_TOGETHEREN 0x40000 +#define BRXHTAGC_MIN 0x80000 +#define BRXHTAGC_EN 0x100000 +#define BRXHTDAGC_EN 0x200000 +#define BRXHT_RXHP_BBP 0x1c00000 +#define BRXHT_RXHP_FINAL 0xe0000000 +#define BRXPW_RADIO_TH 0x3 +#define BRXPW_RADIO_EN 0x4 +#define BRXMF_HOLD 0x3800 +#define BRXPD_DELAY_TH1 0x38 +#define BRXPD_DELAY_TH2 0x1c0 +#define BRXPD_DC_COUNT_MAX 0x600 +#define BRXPD_DELAY_TH 0x8000 +#define BRXPROCESS_DELAY 0xf0000 +#define BRXSEARCHRANGE_GI2_EARLY 0x700000 +#define BRXFRAME_FUARD_COUNTER_L 0x3800000 +#define BRXSGI_GUARD_L 0xc000000 +#define BRXSGI_SEARCH_L 0x30000000 +#define BRXSGI_TH 0xc0000000 +#define BDFSCNT0 0xff +#define BDFSCNT1 0xff00 +#define BDFSFLAG 0xf0000 +#define BMF_WEIGHT_SUM 0x300000 +#define BMINIDX_TH 0x7f000000 +#define BDAFORMAT 0x40000 +#define BTXCH_EMU_ENABLE 0x01000000 +#define BTRSW_ISOLATION_A 0x7f +#define BTRSW_ISOLATION_B 0x7f00 +#define BTRSW_ISOLATION_C 0x7f0000 +#define BTRSW_ISOLATION_D 0x7f000000 +#define BEXT_LNA_GAIN 0x7c00 + +#define BSTBC_EN 0x4 +#define BANTENNA_MAPPING 0x10 +#define BNSS 0x20 +#define BCFO_ANTSUM_ID 0x200 +#define BPHY_COUNTER_RESET 0x8000000 +#define BCFO_REPORT_GET 0x4000000 +#define BOFDM_CONTINUE_TX 0x10000000 +#define BOFDM_SINGLE_CARRIER 0x20000000 +#define BOFDM_SINGLE_TONE 0x40000000 +#define BHT_DETECT 0x100 +#define BCFOEN 0x10000 +#define BCFOVALUE 0xfff00000 +#define BSIGTONE_RE 0x3f +#define BSIGTONE_IM 0x7f00 +#define BCOUNTER_CCA 0xffff +#define BCOUNTER_PARITYFAIL 0xffff0000 +#define BCOUNTER_RATEILLEGAL 0xffff +#define BCOUNTER_CRC8FAIL 0xffff0000 +#define BCOUNTER_MCSNOSUPPORT 0xffff +#define BCOUNTER_FASTSYNC 0xffff +#define BSHORTCFO 0xfff +#define BSHORTCFOT_LENGTH 12 +#define BSHORTCFOF_LENGTH 11 +#define BLONGCFO 0x7ff +#define BLONGCFOT_LENGTH 11 +#define BLONGCFOF_LENGTH 11 +#define BTAILCFO 0x1fff +#define BTAILCFOT_LENGTH 13 +#define BTAILCFOF_LENGTH 12 +#define BNOISE_EN_PWDB 0xffff +#define BCC_POWER_DB 0xffff0000 +#define BMOISE_PWDB 0xffff +#define BPOWERMEAST_LENGTH 10 +#define BPOWERMEASF_LENGTH 3 +#define BRX_HT_BW 0x1 +#define BRXSC 0x6 +#define BRX_HT 0x8 +#define BNB_INTF_DET_ON 0x1 +#define BINTF_WIN_LEN_CFG 0x30 +#define BNB_INTF_TH_CFG 0x1c0 +#define BRFGAIN 0x3f +#define BTABLESEL 0x40 +#define BTRSW 0x80 +#define BRXSNR_A 0xff +#define BRXSNR_B 0xff00 +#define BRXSNR_C 0xff0000 +#define BRXSNR_D 0xff000000 +#define BSNR_EVMT_LENGTH 8 +#define BSNR_EVMF_LENGTH 1 +#define BCSI1ST 0xff +#define BCSI2ND 0xff00 +#define BRXEVM1ST 0xff0000 +#define BRXEVM2ND 0xff000000 +#define BSIGEVM 0xff +#define BPWDB 0xff00 +#define BSGIEN 0x10000 + +#define BSFACTOR_QMA1 0xf +#define BSFACTOR_QMA2 0xf0 +#define BSFACTOR_QMA3 0xf00 +#define BSFACTOR_QMA4 0xf000 +#define BSFACTOR_QMA5 0xf0000 +#define BSFACTOR_QMA6 0xf0000 +#define BSFACTOR_QMA7 0xf00000 +#define BSFACTOR_QMA8 0xf000000 +#define BSFACTOR_QMA9 0xf0000000 +#define BCSI_SCHEME 0x100000 + +#define BNOISE_LVL_TOP_SET 0x3 +#define BCHSMOOTH 0x4 +#define BCHSMOOTH_CFG1 0x38 +#define BCHSMOOTH_CFG2 0x1c0 +#define BCHSMOOTH_CFG3 0xe00 +#define BCHSMOOTH_CFG4 0x7000 +#define BMRCMODE 0x800000 +#define BTHEVMCFG 0x7000000 + +#define BLOOP_FIT_TYPE 0x1 +#define BUPD_CFO 0x40 +#define BUPD_CFO_OFFDATA 0x80 +#define BADV_UPD_CFO 0x100 +#define BADV_TIME_CTRL 0x800 +#define BUPD_CLKO 0x1000 +#define BFC 0x6000 +#define BTRACKING_MODE 0x8000 +#define BPHCMP_ENABLE 0x10000 +#define BUPD_CLKO_LTF 0x20000 +#define BCOM_CH_CFO 0x40000 +#define BCSI_ESTI_MODE 0x80000 +#define BADV_UPD_EQZ 0x100000 +#define BUCHCFG 0x7000000 +#define BUPDEQZ 0x8000000 + +#define BRX_PESUDO_NOISE_ON 0x20000000 +#define BRX_PESUDO_NOISE_A 0xff +#define BRX_PESUDO_NOISE_B 0xff00 +#define BRX_PESUDO_NOISE_C 0xff0000 +#define BRX_PESUDO_NOISE_D 0xff000000 +#define BRX_PESUDO_NOISESTATE_A 0xffff +#define BRX_PESUDO_NOISESTATE_B 0xffff0000 +#define BRX_PESUDO_NOISESTATE_C 0xffff +#define BRX_PESUDO_NOISESTATE_D 0xffff0000 + +#define BZEBRA1_HSSIENABLE 0x8 +#define BZEBRA1_TRXCONTROL 0xc00 +#define BZEBRA1_TRXGAINSETTING 0x07f +#define BZEBRA1_RXCOUNTER 0xc00 +#define BZEBRA1_TXCHANGEPUMP 0x38 +#define BZEBRA1_RXCHANGEPUMP 0x7 +#define BZEBRA1_CHANNEL_NUM 0xf80 +#define BZEBRA1_TXLPFBW 0x400 +#define BZEBRA1_RXLPFBW 0x600 + +#define BRTL8256REG_MODE_CTRL1 0x100 +#define BRTL8256REG_MODE_CTRL0 0x40 +#define BRTL8256REG_TXLPFBW 0x18 +#define BRTL8256REG_RXLPFBW 0x600 + +#define BRTL8258_TXLPFBW 0xc +#define BRTL8258_RXLPFBW 0xc00 +#define BRTL8258_RSSILPFBW 0xc0 + +#define BBYTE0 0x1 +#define BBYTE1 0x2 +#define BBYTE2 0x4 +#define BBYTE3 0x8 +#define BWORD0 0x3 +#define BWORD1 0xc +#define BWORD 0xf + +#define BENABLE 0x1 +#define BDISABLE 0x0 + +#define LEFT_ANTENNA 0x0 +#define RIGHT_ANTENNA 0x1 + +#define TCHECK_TXSTATUS 500 +#define TUPDATE_RXCOUNTER 100 + +#define REG_UN_used_register 0x01bf + +/* WOL bit information */ +#define HAL92C_WOL_PTK_UPDATE_EVENT BIT(0) +#define HAL92C_WOL_GTK_UPDATE_EVENT BIT(1) +#define HAL92C_WOL_DISASSOC_EVENT BIT(2) +#define HAL92C_WOL_DEAUTH_EVENT BIT(3) +#define HAL92C_WOL_FW_DISCONNECT_EVENT BIT(4) + +#define WOL_REASON_PTK_UPDATE BIT(0) +#define WOL_REASON_GTK_UPDATE BIT(1) +#define WOL_REASON_DISASSOC BIT(2) +#define WOL_REASON_DEAUTH BIT(3) +#define WOL_REASON_FW_DISCONNECT BIT(4) + +/* 2 EFUSE_TEST (For RTL8723 partially) */ +#define EFUSE_SEL(x) (((x) & 0x3) << 8) +#define EFUSE_SEL_MASK 0x300 +#define EFUSE_WIFI_SEL_0 0x0 + +#define WL_HWPDN_EN BIT(0) /* Enable GPIO[9] as WiFi HW PDn source*/ +#define WL_HWPDN_SL BIT(1) /* WiFi HW PDn polarity control*/ + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/rf.c b/drivers/net/wireless/rtlwifi/rtl8723be/rf.c new file mode 100644 index 00000000000..486294930a7 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/rf.c @@ -0,0 +1,504 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "rf.h" +#include "dm.h" + +static bool _rtl8723be_phy_rf6052_config_parafile(struct ieee80211_hw *hw); + +void rtl8723be_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + switch (bandwidth) { + case HT_CHANNEL_WIDTH_20: + rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & + 0xfffff3ff) | BIT(10) | BIT(11)); + rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, + rtlphy->rfreg_chnlval[0]); + break; + case HT_CHANNEL_WIDTH_20_40: + rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & + 0xfffff3ff) | BIT(10)); + rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, + rtlphy->rfreg_chnlval[0]); + break; + default: + RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG, + "unknown bandwidth: %#X\n", bandwidth); + break; + } +} + +void rtl8723be_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u32 tx_agc[2] = {0, 0}, tmpval; + bool turbo_scanoff = false; + u8 idx1, idx2; + u8 *ptr; + u8 direction; + u32 pwrtrac_value; + + if (rtlefuse->eeprom_regulatory != 0) + turbo_scanoff = true; + + if (mac->act_scanning) { + tx_agc[RF90_PATH_A] = 0x3f3f3f3f; + tx_agc[RF90_PATH_B] = 0x3f3f3f3f; + + if (turbo_scanoff) { + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + tx_agc[idx1] = ppowerlevel[idx1] | + (ppowerlevel[idx1] << 8) | + (ppowerlevel[idx1] << 16) | + (ppowerlevel[idx1] << 24); + } + } + } else { + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + tx_agc[idx1] = ppowerlevel[idx1] | + (ppowerlevel[idx1] << 8) | + (ppowerlevel[idx1] << 16) | + (ppowerlevel[idx1] << 24); + } + if (rtlefuse->eeprom_regulatory == 0) { + tmpval = + (rtlphy->mcs_offset[0][6]) + + (rtlphy->mcs_offset[0][7] << 8); + tx_agc[RF90_PATH_A] += tmpval; + + tmpval = (rtlphy->mcs_offset[0][14]) + + (rtlphy->mcs_offset[0][15] << + 24); + tx_agc[RF90_PATH_B] += tmpval; + } + } + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + ptr = (u8 *)(&(tx_agc[idx1])); + for (idx2 = 0; idx2 < 4; idx2++) { + if (*ptr > RF6052_MAX_TX_PWR) + *ptr = RF6052_MAX_TX_PWR; + ptr++; + } + } + rtl8723be_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value); + if (direction == 1) { + tx_agc[0] += pwrtrac_value; + tx_agc[1] += pwrtrac_value; + } else if (direction == 2) { + tx_agc[0] -= pwrtrac_value; + tx_agc[1] -= pwrtrac_value; + } + tmpval = tx_agc[RF90_PATH_A] & 0xff; + rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_A_CCK1_MCS32); + + tmpval = tx_agc[RF90_PATH_A] >> 8; + + rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_B_CCK11_A_CCK2_11); + + tmpval = tx_agc[RF90_PATH_B] >> 24; + rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_B_CCK11_A_CCK2_11); + + tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff; + rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_B_CCK1_55_MCS32); +} + +static void rtl8723be_phy_get_power_base(struct ieee80211_hw *hw, + u8 *ppowerlevel_ofdm, + u8 *ppowerlevel_bw20, + u8 *ppowerlevel_bw40, + u8 channel, u32 *ofdmbase, + u32 *mcsbase) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 powerbase0, powerbase1; + u8 i, powerlevel[2]; + + for (i = 0; i < 2; i++) { + powerbase0 = ppowerlevel_ofdm[i]; + + powerbase0 = (powerbase0 << 24) | (powerbase0 << 16) | + (powerbase0 << 8) | powerbase0; + *(ofdmbase + i) = powerbase0; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + " [OFDM power base index rf(%c) = 0x%x]\n", + ((i == 0) ? 'A' : 'B'), *(ofdmbase + i)); + } + + for (i = 0; i < 2; i++) { + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) + powerlevel[i] = ppowerlevel_bw20[i]; + else + powerlevel[i] = ppowerlevel_bw40[i]; + powerbase1 = powerlevel[i]; + powerbase1 = (powerbase1 << 24) | (powerbase1 << 16) | + (powerbase1 << 8) | powerbase1; + + *(mcsbase + i) = powerbase1; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + " [MCS power base index rf(%c) = 0x%x]\n", + ((i == 0) ? 'A' : 'B'), *(mcsbase + i)); + } +} + +static void txpwr_by_regulatory(struct ieee80211_hw *hw, u8 channel, u8 index, + u32 *powerbase0, u32 *powerbase1, + u32 *p_outwriteval) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 i, chnlgroup = 0, pwr_diff_limit[4]; + u8 pwr_diff = 0, customer_pwr_diff; + u32 writeval, customer_limit, rf; + + for (rf = 0; rf < 2; rf++) { + switch (rtlefuse->eeprom_regulatory) { + case 0: + chnlgroup = 0; + + writeval = + rtlphy->mcs_offset[chnlgroup][index + (rf ? 8 : 0)] + + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "RTK better performance, " + "writeval(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeval); + break; + case 1: + if (rtlphy->pwrgroup_cnt == 1) { + chnlgroup = 0; + } else { + if (channel < 3) + chnlgroup = 0; + else if (channel < 6) + chnlgroup = 1; + else if (channel < 9) + chnlgroup = 2; + else if (channel < 12) + chnlgroup = 3; + else if (channel < 14) + chnlgroup = 4; + else if (channel == 14) + chnlgroup = 5; + } + writeval = rtlphy->mcs_offset[chnlgroup] + [index + (rf ? 8 : 0)] + ((index < 2) ? + powerbase0[rf] : + powerbase1[rf]); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Realtek regulatory, 20MHz, " + "writeval(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeval); + + break; + case 2: + writeval = + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Better regulatory, " + "writeval(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeval); + break; + case 3: + chnlgroup = 0; + + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "customer's limit, 40MHz " + "rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), + rtlefuse->pwrgroup_ht40[rf] + [channel-1]); + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "customer's limit, 20MHz " + "rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), + rtlefuse->pwrgroup_ht20[rf] + [channel-1]); + } + + if (index < 2) + pwr_diff = + rtlefuse->txpwr_legacyhtdiff[rf][channel-1]; + else if (rtlphy->current_chan_bw == + HT_CHANNEL_WIDTH_20) + pwr_diff = + rtlefuse->txpwr_ht20diff[rf][channel-1]; + + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) + customer_pwr_diff = + rtlefuse->pwrgroup_ht40[rf][channel-1]; + else + customer_pwr_diff = + rtlefuse->pwrgroup_ht20[rf][channel-1]; + + if (pwr_diff > customer_pwr_diff) + pwr_diff = 0; + else + pwr_diff = customer_pwr_diff - pwr_diff; + + for (i = 0; i < 4; i++) { + pwr_diff_limit[i] = + (u8)((rtlphy->mcs_offset + [chnlgroup][index + (rf ? 8 : 0)] & + (0x7f << (i * 8))) >> (i * 8)); + + if (pwr_diff_limit[i] > pwr_diff) + pwr_diff_limit[i] = pwr_diff; + } + + customer_limit = (pwr_diff_limit[3] << 24) | + (pwr_diff_limit[2] << 16) | + (pwr_diff_limit[1] << 8) | + (pwr_diff_limit[0]); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Customer's limit rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), customer_limit); + + writeval = customer_limit + ((index < 2) ? + powerbase0[rf] : + powerbase1[rf]); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Customer, writeval rf(%c)= 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeval); + break; + default: + chnlgroup = 0; + writeval = + rtlphy->mcs_offset[chnlgroup] + [index + (rf ? 8 : 0)] + + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "RTK better performance, writeval " + "rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeval); + break; + } + + if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1) + writeval = writeval - 0x06060606; + else if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_BT2) + writeval = writeval - 0x0c0c0c0c; + *(p_outwriteval + rf) = writeval; + } +} + +static void _rtl8723be_write_ofdm_power_reg(struct ieee80211_hw *hw, + u8 index, u32 *value) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u16 regoffset_a[6] = { + RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24, + RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04, + RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12 + }; + u16 regoffset_b[6] = { + RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24, + RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04, + RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12 + }; + u8 i, rf, pwr_val[4]; + u32 writeval; + u16 regoffset; + + for (rf = 0; rf < 2; rf++) { + writeval = value[rf]; + for (i = 0; i < 4; i++) { + pwr_val[i] = (u8) ((writeval & (0x7f << + (i * 8))) >> (i * 8)); + + if (pwr_val[i] > RF6052_MAX_TX_PWR) + pwr_val[i] = RF6052_MAX_TX_PWR; + } + writeval = (pwr_val[3] << 24) | (pwr_val[2] << 16) | + (pwr_val[1] << 8) | pwr_val[0]; + + if (rf == 0) + regoffset = regoffset_a[index]; + else + regoffset = regoffset_b[index]; + rtl_set_bbreg(hw, regoffset, MASKDWORD, writeval); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "Set 0x%x = %08x\n", regoffset, writeval); + } +} + +void rtl8723be_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel_ofdm, + u8 *ppowerlevel_bw20, + u8 *ppowerlevel_bw40, u8 channel) +{ + u32 writeval[2], powerbase0[2], powerbase1[2]; + u8 index; + u8 direction; + u32 pwrtrac_value; + + rtl8723be_phy_get_power_base(hw, ppowerlevel_ofdm, ppowerlevel_bw20, + ppowerlevel_bw40, channel, + &powerbase0[0], &powerbase1[0]); + + rtl8723be_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value); + + for (index = 0; index < 6; index++) { + txpwr_by_regulatory(hw, channel, index, &powerbase0[0], + &powerbase1[0], &writeval[0]); + if (direction == 1) { + writeval[0] += pwrtrac_value; + writeval[1] += pwrtrac_value; + } else if (direction == 2) { + writeval[0] -= pwrtrac_value; + writeval[1] -= pwrtrac_value; + } + _rtl8723be_write_ofdm_power_reg(hw, index, &writeval[0]); + } +} + +bool rtl8723be_phy_rf6052_config(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + if (rtlphy->rf_type == RF_1T1R) + rtlphy->num_total_rfpath = 1; + else + rtlphy->num_total_rfpath = 2; + + return _rtl8723be_phy_rf6052_config_parafile(hw); +} + +static bool _rtl8723be_phy_rf6052_config_parafile(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct bb_reg_def *pphyreg; + u32 u4_regvalue = 0; + u8 rfpath; + bool rtstatus = true; + + for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { + pphyreg = &rtlphy->phyreg_def[rfpath]; + + switch (rfpath) { + case RF90_PATH_A: + case RF90_PATH_C: + u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV); + break; + case RF90_PATH_B: + case RF90_PATH_D: + u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV << 16); + break; + } + + rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); + udelay(1); + + rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); + udelay(1); + + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, + B3WIREADDREAALENGTH, 0x0); + udelay(1); + + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0); + udelay(1); + + switch (rfpath) { + case RF90_PATH_A: + rtstatus = rtl8723be_phy_config_rf_with_headerfile(hw, + (enum radio_path)rfpath); + break; + case RF90_PATH_B: + rtstatus = rtl8723be_phy_config_rf_with_headerfile(hw, + (enum radio_path)rfpath); + break; + case RF90_PATH_C: + break; + case RF90_PATH_D: + break; + } + + switch (rfpath) { + case RF90_PATH_A: + case RF90_PATH_C: + rtl_set_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV, u4_regvalue); + break; + case RF90_PATH_B: + case RF90_PATH_D: + rtl_set_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV << 16, u4_regvalue); + break; + } + + if (!rtstatus) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Radio[%d] Fail!!", rfpath); + return false; + } + } + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "\n"); + return rtstatus; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/rf.h b/drivers/net/wireless/rtlwifi/rtl8723be/rf.h new file mode 100644 index 00000000000..a6fea106ced --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/rf.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_RF_H__ +#define __RTL8723BE_RF_H__ + +#define RF6052_MAX_TX_PWR 0x3F +#define RF6052_MAX_REG 0x3F + +void rtl8723be_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, + u8 bandwidth); +void rtl8723be_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl8723be_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel_ofdm, + u8 *ppowerlevel_bw20, + u8 *ppowerlevel_bw40, + u8 channel); +bool rtl8723be_phy_rf6052_config(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c new file mode 100644 index 00000000000..ff12bf41644 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c @@ -0,0 +1,387 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../core.h" +#include "../pci.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "../rtl8723com/phy_common.h" +#include "dm.h" +#include "hw.h" +#include "fw.h" +#include "../rtl8723com/fw_common.h" +#include "sw.h" +#include "trx.h" +#include "led.h" +#include "table.h" +#include "../btcoexist/rtl_btc.h" + +#include <linux/vmalloc.h> +#include <linux/module.h> + +static void rtl8723be_init_aspm_vars(struct ieee80211_hw *hw) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + /*close ASPM for AMD defaultly */ + rtlpci->const_amdpci_aspm = 0; + + /* ASPM PS mode. + * 0 - Disable ASPM, + * 1 - Enable ASPM without Clock Req, + * 2 - Enable ASPM with Clock Req, + * 3 - Alwyas Enable ASPM with Clock Req, + * 4 - Always Enable ASPM without Clock Req. + * set defult to RTL8192CE:3 RTL8192E:2 + */ + rtlpci->const_pci_aspm = 3; + + /*Setting for PCI-E device */ + rtlpci->const_devicepci_aspm_setting = 0x03; + + /*Setting for PCI-E bridge */ + rtlpci->const_hostpci_aspm_setting = 0x02; + + /* In Hw/Sw Radio Off situation. + * 0 - Default, + * 1 - From ASPM setting without low Mac Pwr, + * 2 - From ASPM setting with low Mac Pwr, + * 3 - Bus D3 + * set default to RTL8192CE:0 RTL8192SE:2 + */ + rtlpci->const_hwsw_rfoff_d3 = 0; + + /* This setting works for those device with + * backdoor ASPM setting such as EPHY setting. + * 0 - Not support ASPM, + * 1 - Support ASPM, + * 2 - According to chipset. + */ + rtlpci->const_support_pciaspm = 1; +} + +int rtl8723be_init_sw_vars(struct ieee80211_hw *hw) +{ + int err = 0; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + + rtl8723be_bt_reg_init(hw); + rtlpci->msi_support = rtlpriv->cfg->mod_params->msi_support; + rtlpriv->btcoexist.btc_ops = rtl_btc_get_ops_pointer(); + + rtlpriv->dm.dm_initialgain_enable = 1; + rtlpriv->dm.dm_flag = 0; + rtlpriv->dm.disable_framebursting = 0; + rtlpriv->dm.thermalvalue = 0; + rtlpci->transmit_config = CFENDFORM | BIT(15) | BIT(24) | BIT(25); + + mac->ht_enable = true; + + /* compatible 5G band 88ce just 2.4G band & smsp */ + rtlpriv->rtlhal.current_bandtype = BAND_ON_2_4G; + rtlpriv->rtlhal.bandset = BAND_ON_2_4G; + rtlpriv->rtlhal.macphymode = SINGLEMAC_SINGLEPHY; + + rtlpci->receive_config = (RCR_APPFCS | + RCR_APP_MIC | + RCR_APP_ICV | + RCR_APP_PHYST_RXFF | + RCR_HTC_LOC_CTRL | + RCR_AMF | + RCR_ACF | + RCR_ADF | + RCR_AICV | + RCR_AB | + RCR_AM | + RCR_APM | + 0); + + rtlpci->irq_mask[0] = (u32) (IMR_PSTIMEOUT | + IMR_HSISR_IND_ON_INT | + IMR_C2HCMD | + IMR_HIGHDOK | + IMR_MGNTDOK | + IMR_BKDOK | + IMR_BEDOK | + IMR_VIDOK | + IMR_VODOK | + IMR_RDU | + IMR_ROK | + 0); + + rtlpci->irq_mask[1] = (u32)(IMR_RXFOVW | 0); + + /* for debug level */ + rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; + /* for LPS & IPS */ + rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; + rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; + rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpriv->psc.reg_fwctrl_lps = 3; + rtlpriv->psc.reg_max_lps_awakeintvl = 5; + /* for ASPM, you can close aspm through + * set const_support_pciaspm = 0 + */ + rtl8723be_init_aspm_vars(hw); + + if (rtlpriv->psc.reg_fwctrl_lps == 1) + rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE; + else if (rtlpriv->psc.reg_fwctrl_lps == 2) + rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE; + else if (rtlpriv->psc.reg_fwctrl_lps == 3) + rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; + + /* for firmware buf */ + rtlpriv->rtlhal.pfirmware = vzalloc(0x8000); + if (!rtlpriv->rtlhal.pfirmware) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Can't alloc buffer for fw.\n"); + return 1; + } + + rtlpriv->max_fw_size = 0x8000; + pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name); + err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, + rtlpriv->io.dev, GFP_KERNEL, hw, + rtl_fw_cb); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Failed to request firmware!\n"); + return 1; + } + return 0; +} + +void rtl8723be_deinit_sw_vars(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->cfg->ops->get_btc_status()) + rtlpriv->btcoexist.btc_ops->btc_halt_notify(); + if (rtlpriv->rtlhal.pfirmware) { + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; + } +} + +/* get bt coexist status */ +bool rtl8723be_get_btc_status(void) +{ + return true; +} + +static bool is_fw_header(struct rtl92c_firmware_header *hdr) +{ + return (hdr->signature & 0xfff0) == 0x5300; +} + +static struct rtl_hal_ops rtl8723be_hal_ops = { + .init_sw_vars = rtl8723be_init_sw_vars, + .deinit_sw_vars = rtl8723be_deinit_sw_vars, + .read_eeprom_info = rtl8723be_read_eeprom_info, + .interrupt_recognized = rtl8723be_interrupt_recognized, + .hw_init = rtl8723be_hw_init, + .hw_disable = rtl8723be_card_disable, + .hw_suspend = rtl8723be_suspend, + .hw_resume = rtl8723be_resume, + .enable_interrupt = rtl8723be_enable_interrupt, + .disable_interrupt = rtl8723be_disable_interrupt, + .set_network_type = rtl8723be_set_network_type, + .set_chk_bssid = rtl8723be_set_check_bssid, + .set_qos = rtl8723be_set_qos, + .set_bcn_reg = rtl8723be_set_beacon_related_registers, + .set_bcn_intv = rtl8723be_set_beacon_interval, + .update_interrupt_mask = rtl8723be_update_interrupt_mask, + .get_hw_reg = rtl8723be_get_hw_reg, + .set_hw_reg = rtl8723be_set_hw_reg, + .update_rate_tbl = rtl8723be_update_hal_rate_tbl, + .fill_tx_desc = rtl8723be_tx_fill_desc, + .fill_tx_cmddesc = rtl8723be_tx_fill_cmddesc, + .query_rx_desc = rtl8723be_rx_query_desc, + .set_channel_access = rtl8723be_update_channel_access_setting, + .radio_onoff_checking = rtl8723be_gpio_radio_on_off_checking, + .set_bw_mode = rtl8723be_phy_set_bw_mode, + .switch_channel = rtl8723be_phy_sw_chnl, + .dm_watchdog = rtl8723be_dm_watchdog, + .scan_operation_backup = rtl8723be_phy_scan_operation_backup, + .set_rf_power_state = rtl8723be_phy_set_rf_power_state, + .led_control = rtl8723be_led_control, + .set_desc = rtl8723be_set_desc, + .get_desc = rtl8723be_get_desc, + .is_tx_desc_closed = rtl8723be_is_tx_desc_closed, + .tx_polling = rtl8723be_tx_polling, + .enable_hw_sec = rtl8723be_enable_hw_security_config, + .set_key = rtl8723be_set_key, + .init_sw_leds = rtl8723be_init_sw_leds, + .get_bbreg = rtl8723_phy_query_bb_reg, + .set_bbreg = rtl8723_phy_set_bb_reg, + .get_rfreg = rtl8723be_phy_query_rf_reg, + .set_rfreg = rtl8723be_phy_set_rf_reg, + .fill_h2c_cmd = rtl8723be_fill_h2c_cmd, + .get_btc_status = rtl8723be_get_btc_status, + .is_fw_header = is_fw_header, +}; + +static struct rtl_mod_params rtl8723be_mod_params = { + .sw_crypto = false, + .inactiveps = true, + .swctrl_lps = false, + .fwctrl_lps = true, + .msi_support = false, + .debug = DBG_EMERG, +}; + +static struct rtl_hal_cfg rtl8723be_hal_cfg = { + .bar_id = 2, + .write_readback = true, + .name = "rtl8723be_pci", + .fw_name = "rtlwifi/rtl8723befw.bin", + .ops = &rtl8723be_hal_ops, + .mod_params = &rtl8723be_mod_params, + .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, + .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN, + .maps[SYS_CLK] = REG_SYS_CLKR, + .maps[MAC_RCR_AM] = AM, + .maps[MAC_RCR_AB] = AB, + .maps[MAC_RCR_ACRC32] = ACRC32, + .maps[MAC_RCR_ACF] = ACF, + .maps[MAC_RCR_AAP] = AAP, + + .maps[EFUSE_ACCESS] = REG_EFUSE_ACCESS, + + .maps[EFUSE_TEST] = REG_EFUSE_TEST, + .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, + .maps[EFUSE_CLK] = 0, + .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL, + .maps[EFUSE_PWC_EV12V] = PWC_EV12V, + .maps[EFUSE_FEN_ELDR] = FEN_ELDR, + .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN, + .maps[EFUSE_ANA8M] = ANA8M, + .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE, + .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION, + .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN, + .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES, + + .maps[RWCAM] = REG_CAMCMD, + .maps[WCAMI] = REG_CAMWRITE, + .maps[RCAMO] = REG_CAMREAD, + .maps[CAMDBG] = REG_CAMDBG, + .maps[SECR] = REG_SECCFG, + .maps[SEC_CAM_NONE] = CAM_NONE, + .maps[SEC_CAM_WEP40] = CAM_WEP40, + .maps[SEC_CAM_TKIP] = CAM_TKIP, + .maps[SEC_CAM_AES] = CAM_AES, + .maps[SEC_CAM_WEP104] = CAM_WEP104, + + .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6, + .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5, + .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4, + .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3, + .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2, + .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1, + .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7, + .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6, + .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5, + .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4, + .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3, + .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2, + .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1, + + .maps[RTL_IMR_TXFOVW] = IMR_TXFOVW, + .maps[RTL_IMR_PSTIMEOUT] = IMR_PSTIMEOUT, + .maps[RTL_IMR_BCNINT] = IMR_BCNDMAINT0, + .maps[RTL_IMR_RXFOVW] = IMR_RXFOVW, + .maps[RTL_IMR_RDU] = IMR_RDU, + .maps[RTL_IMR_ATIMEND] = IMR_ATIMEND, + .maps[RTL_IMR_BDOK] = IMR_BCNDOK0, + .maps[RTL_IMR_MGNTDOK] = IMR_MGNTDOK, + .maps[RTL_IMR_TBDER] = IMR_TBDER, + .maps[RTL_IMR_HIGHDOK] = IMR_HIGHDOK, + .maps[RTL_IMR_TBDOK] = IMR_TBDOK, + .maps[RTL_IMR_BKDOK] = IMR_BKDOK, + .maps[RTL_IMR_BEDOK] = IMR_BEDOK, + .maps[RTL_IMR_VIDOK] = IMR_VIDOK, + .maps[RTL_IMR_VODOK] = IMR_VODOK, + .maps[RTL_IMR_ROK] = IMR_ROK, + .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNDMAINT0 | IMR_TBDOK | IMR_TBDER), + + .maps[RTL_RC_CCK_RATE1M] = DESC92C_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC92C_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC92C_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC92C_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC92C_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC92C_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC92C_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC92C_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC92C_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC92C_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC92C_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC92C_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC92C_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC92C_RATEMCS15, +}; + +static DEFINE_PCI_DEVICE_TABLE(rtl8723be_pci_id) = { + {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xb723, rtl8723be_hal_cfg)}, + {}, +}; + +MODULE_DEVICE_TABLE(pci, rtl8723be_pci_id); + +MODULE_AUTHOR("PageHe <page_he@realsil.com.cn>"); +MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek 8723BE 802.11n PCI wireless"); +MODULE_FIRMWARE("rtlwifi/rtl8723befw.bin"); + +module_param_named(swenc, rtl8723be_mod_params.sw_crypto, bool, 0444); +module_param_named(debug, rtl8723be_mod_params.debug, int, 0444); +module_param_named(ips, rtl8723be_mod_params.inactiveps, bool, 0444); +module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444); +module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); +module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444); +MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); +MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); +MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\n"); +MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); +MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); + +static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); + +static struct pci_driver rtl8723be_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8723be_pci_id, + .probe = rtl_pci_probe, + .remove = rtl_pci_disconnect, + + .driver.pm = &rtlwifi_pm_ops, +}; + +module_pci_driver(rtl8723be_driver); diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.h b/drivers/net/wireless/rtlwifi/rtl8723be/sw.h new file mode 100644 index 00000000000..a7b25e76995 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_SW_H__ +#define __RTL8723BE_SW_H__ + +int rtl8723be_init_sw_vars(struct ieee80211_hw *hw); +void rtl8723be_deinit_sw_vars(struct ieee80211_hw *hw); +void rtl8723be_init_var_map(struct ieee80211_hw *hw); +bool rtl8723be_get_btc_status(void); + + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/table.c b/drivers/net/wireless/rtlwifi/rtl8723be/table.c new file mode 100644 index 00000000000..4b283cde042 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/table.c @@ -0,0 +1,572 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Created on 2010/ 5/18, 1:41 + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "table.h" +u32 RTL8723BEPHY_REG_1TARRAY[] = { + 0x800, 0x80040000, + 0x804, 0x00000003, + 0x808, 0x0000FC00, + 0x80C, 0x0000000A, + 0x810, 0x10001331, + 0x814, 0x020C3D10, + 0x818, 0x02200385, + 0x81C, 0x00000000, + 0x820, 0x01000100, + 0x824, 0x00390204, + 0x828, 0x00000000, + 0x82C, 0x00000000, + 0x830, 0x00000000, + 0x834, 0x00000000, + 0x838, 0x00000000, + 0x83C, 0x00000000, + 0x840, 0x00010000, + 0x844, 0x00000000, + 0x848, 0x00000000, + 0x84C, 0x00000000, + 0x850, 0x00000000, + 0x854, 0x00000000, + 0x858, 0x569A11A9, + 0x85C, 0x01000014, + 0x860, 0x66F60110, + 0x864, 0x061F0649, + 0x868, 0x00000000, + 0x86C, 0x27272700, + 0x870, 0x07000760, + 0x874, 0x25004000, + 0x878, 0x00000808, + 0x87C, 0x00000000, + 0x880, 0xB0000C1C, + 0x884, 0x00000001, + 0x888, 0x00000000, + 0x88C, 0xCCC000C0, + 0x890, 0x00000800, + 0x894, 0xFFFFFFFE, + 0x898, 0x40302010, + 0x89C, 0x00706050, + 0x900, 0x00000000, + 0x904, 0x00000023, + 0x908, 0x00000000, + 0x90C, 0x81121111, + 0x910, 0x00000002, + 0x914, 0x00000201, + 0x948, 0x00000000, + 0xA00, 0x00D047C8, + 0xA04, 0x80FF000C, + 0xA08, 0x8C838300, + 0xA0C, 0x2E7F120F, + 0xA10, 0x9500BB78, + 0xA14, 0x1114D028, + 0xA18, 0x00881117, + 0xA1C, 0x89140F00, + 0xA20, 0x1A1B0000, + 0xA24, 0x090E1317, + 0xA28, 0x00000204, + 0xA2C, 0x00D30000, + 0xA70, 0x101FBF00, + 0xA74, 0x00000007, + 0xA78, 0x00000900, + 0xA7C, 0x225B0606, + 0xA80, 0x21806490, + 0xB2C, 0x00000000, + 0xC00, 0x48071D40, + 0xC04, 0x03A05611, + 0xC08, 0x000000E4, + 0xC0C, 0x6C6C6C6C, + 0xC10, 0x08800000, + 0xC14, 0x40000100, + 0xC18, 0x08800000, + 0xC1C, 0x40000100, + 0xC20, 0x00000000, + 0xC24, 0x00000000, + 0xC28, 0x00000000, + 0xC2C, 0x00000000, + 0xC30, 0x69E9AC44, + 0xC34, 0x469652AF, + 0xC38, 0x49795994, + 0xC3C, 0x0A97971C, + 0xC40, 0x1F7C403F, + 0xC44, 0x000100B7, + 0xC48, 0xEC020107, + 0xC4C, 0x007F037F, + 0xC50, 0x69553420, + 0xC54, 0x43BC0094, + 0xC58, 0x00023169, + 0xC5C, 0x00250492, + 0xC60, 0x00000000, + 0xC64, 0x7112848B, + 0xC68, 0x47C00BFF, + 0xC6C, 0x00000036, + 0xC70, 0x2C7F000D, + 0xC74, 0x020610DB, + 0xC78, 0x0000001F, + 0xC7C, 0x00B91612, + 0xC80, 0x390000E4, + 0xC84, 0x20F60000, + 0xC88, 0x40000100, + 0xC8C, 0x20200000, + 0xC90, 0x00020E1A, + 0xC94, 0x00000000, + 0xC98, 0x00020E1A, + 0xC9C, 0x00007F7F, + 0xCA0, 0x00000000, + 0xCA4, 0x000300A0, + 0xCA8, 0x00000000, + 0xCAC, 0x00000000, + 0xCB0, 0x00000000, + 0xCB4, 0x00000000, + 0xCB8, 0x00000000, + 0xCBC, 0x28000000, + 0xCC0, 0x00000000, + 0xCC4, 0x00000000, + 0xCC8, 0x00000000, + 0xCCC, 0x00000000, + 0xCD0, 0x00000000, + 0xCD4, 0x00000000, + 0xCD8, 0x64B22427, + 0xCDC, 0x00766932, + 0xCE0, 0x00222222, + 0xCE4, 0x00000000, + 0xCE8, 0x37644302, + 0xCEC, 0x2F97D40C, + 0xD00, 0x00000740, + 0xD04, 0x40020401, + 0xD08, 0x0000907F, + 0xD0C, 0x20010201, + 0xD10, 0xA0633333, + 0xD14, 0x3333BC53, + 0xD18, 0x7A8F5B6F, + 0xD2C, 0xCC979975, + 0xD30, 0x00000000, + 0xD34, 0x80608000, + 0xD38, 0x00000000, + 0xD3C, 0x00127353, + 0xD40, 0x00000000, + 0xD44, 0x00000000, + 0xD48, 0x00000000, + 0xD4C, 0x00000000, + 0xD50, 0x6437140A, + 0xD54, 0x00000000, + 0xD58, 0x00000282, + 0xD5C, 0x30032064, + 0xD60, 0x4653DE68, + 0xD64, 0x04518A3C, + 0xD68, 0x00002101, + 0xD6C, 0x2A201C16, + 0xD70, 0x1812362E, + 0xD74, 0x322C2220, + 0xD78, 0x000E3C24, + 0xE00, 0x2D2D2D2D, + 0xE04, 0x2D2D2D2D, + 0xE08, 0x0390272D, + 0xE10, 0x2D2D2D2D, + 0xE14, 0x2D2D2D2D, + 0xE18, 0x2D2D2D2D, + 0xE1C, 0x2D2D2D2D, + 0xE28, 0x00000000, + 0xE30, 0x1000DC1F, + 0xE34, 0x10008C1F, + 0xE38, 0x02140102, + 0xE3C, 0x681604C2, + 0xE40, 0x01007C00, + 0xE44, 0x01004800, + 0xE48, 0xFB000000, + 0xE4C, 0x000028D1, + 0xE50, 0x1000DC1F, + 0xE54, 0x10008C1F, + 0xE58, 0x02140102, + 0xE5C, 0x28160D05, + 0xE60, 0x00000008, + 0xE68, 0x001B2556, + 0xE6C, 0x00C00096, + 0xE70, 0x00C00096, + 0xE74, 0x01000056, + 0xE78, 0x01000014, + 0xE7C, 0x01000056, + 0xE80, 0x01000014, + 0xE84, 0x00C00096, + 0xE88, 0x01000056, + 0xE8C, 0x00C00096, + 0xED0, 0x00C00096, + 0xED4, 0x00C00096, + 0xED8, 0x00C00096, + 0xEDC, 0x000000D6, + 0xEE0, 0x000000D6, + 0xEEC, 0x01C00016, + 0xF14, 0x00000003, + 0xF4C, 0x00000000, + 0xF00, 0x00000300, + 0x820, 0x01000100, + 0x800, 0x83040000, +}; + +u32 RTL8723BEPHY_REG_ARRAY_PG[] = { + 0, 0, 0, 0x00000e08, 0x0000ff00, 0x00004000, + 0, 0, 0, 0x0000086c, 0xffffff00, 0x34363800, + 0, 0, 0, 0x00000e00, 0xffffffff, 0x42444646, + 0, 0, 0, 0x00000e04, 0xffffffff, 0x30343840, + 0, 0, 0, 0x00000e10, 0xffffffff, 0x38404244, + 0, 0, 0, 0x00000e14, 0xffffffff, 0x26303436 +}; + +u32 RTL8723BE_RADIOA_1TARRAY[] = { + 0x000, 0x00010000, + 0x0B0, 0x000DFFE0, + 0x0FE, 0x00000000, + 0x0FE, 0x00000000, + 0x0FE, 0x00000000, + 0x0B1, 0x00000018, + 0x0FE, 0x00000000, + 0x0FE, 0x00000000, + 0x0FE, 0x00000000, + 0x0B2, 0x00084C00, + 0x0B5, 0x0000D2CC, + 0x0B6, 0x000925AA, + 0x0B7, 0x00000010, + 0x0B8, 0x0000907F, + 0x05C, 0x00000002, + 0x07C, 0x00000002, + 0x07E, 0x00000005, + 0x08B, 0x0006FC00, + 0x0B0, 0x000FF9F0, + 0x01C, 0x000739D2, + 0x01E, 0x00000000, + 0x0DF, 0x00000780, + 0x050, 0x00067435, + 0x051, 0x0006B04E, + 0x052, 0x000007D2, + 0x053, 0x00000000, + 0x054, 0x00050400, + 0x055, 0x0004026E, + 0x0DD, 0x0000004C, + 0x070, 0x00067435, + 0x071, 0x0006B04E, + 0x072, 0x000007D2, + 0x073, 0x00000000, + 0x074, 0x00050400, + 0x075, 0x0004026E, + 0x0EF, 0x00000100, + 0x034, 0x0000ADD7, + 0x035, 0x00005C00, + 0x034, 0x00009DD4, + 0x035, 0x00005000, + 0x034, 0x00008DD1, + 0x035, 0x00004400, + 0x034, 0x00007DCE, + 0x035, 0x00003800, + 0x034, 0x00006CD1, + 0x035, 0x00004400, + 0x034, 0x00005CCE, + 0x035, 0x00003800, + 0x034, 0x000048CE, + 0x035, 0x00004400, + 0x034, 0x000034CE, + 0x035, 0x00003800, + 0x034, 0x00002451, + 0x035, 0x00004400, + 0x034, 0x0000144E, + 0x035, 0x00003800, + 0x034, 0x00000051, + 0x035, 0x00004400, + 0x0EF, 0x00000000, + 0x0EF, 0x00000100, + 0x0ED, 0x00000010, + 0x044, 0x0000ADD7, + 0x044, 0x00009DD4, + 0x044, 0x00008DD1, + 0x044, 0x00007DCE, + 0x044, 0x00006CC1, + 0x044, 0x00005CCE, + 0x044, 0x000044D1, + 0x044, 0x000034CE, + 0x044, 0x00002451, + 0x044, 0x0000144E, + 0x044, 0x00000051, + 0x0EF, 0x00000000, + 0x0ED, 0x00000000, + 0x0EF, 0x00002000, + 0x03B, 0x000380EF, + 0x03B, 0x000302FE, + 0x03B, 0x00028CE6, + 0x03B, 0x000200BC, + 0x03B, 0x000188A5, + 0x03B, 0x00010FBC, + 0x03B, 0x00008F71, + 0x03B, 0x00000900, + 0x0EF, 0x00000000, + 0x0ED, 0x00000001, + 0x040, 0x000380EF, + 0x040, 0x000302FE, + 0x040, 0x00028CE6, + 0x040, 0x000200BC, + 0x040, 0x000188A5, + 0x040, 0x00010FBC, + 0x040, 0x00008F71, + 0x040, 0x00000900, + 0x0ED, 0x00000000, + 0x082, 0x00080000, + 0x083, 0x00008000, + 0x084, 0x00048D80, + 0x085, 0x00068000, + 0x0A2, 0x00080000, + 0x0A3, 0x00008000, + 0x0A4, 0x00048D80, + 0x0A5, 0x00068000, + 0x000, 0x00033D80, +}; + +u32 RTL8723BEMAC_1T_ARRAY[] = { + 0x02F, 0x00000030, + 0x035, 0x00000000, + 0x428, 0x0000000A, + 0x429, 0x00000010, + 0x430, 0x00000000, + 0x431, 0x00000000, + 0x432, 0x00000000, + 0x433, 0x00000001, + 0x434, 0x00000004, + 0x435, 0x00000005, + 0x436, 0x00000007, + 0x437, 0x00000008, + 0x43C, 0x00000004, + 0x43D, 0x00000005, + 0x43E, 0x00000007, + 0x43F, 0x00000008, + 0x440, 0x0000005D, + 0x441, 0x00000001, + 0x442, 0x00000000, + 0x444, 0x00000010, + 0x445, 0x00000000, + 0x446, 0x00000000, + 0x447, 0x00000000, + 0x448, 0x00000000, + 0x449, 0x000000F0, + 0x44A, 0x0000000F, + 0x44B, 0x0000003E, + 0x44C, 0x00000010, + 0x44D, 0x00000000, + 0x44E, 0x00000000, + 0x44F, 0x00000000, + 0x450, 0x00000000, + 0x451, 0x000000F0, + 0x452, 0x0000000F, + 0x453, 0x00000000, + 0x456, 0x0000005E, + 0x460, 0x00000066, + 0x461, 0x00000066, + 0x4C8, 0x000000FF, + 0x4C9, 0x00000008, + 0x4CC, 0x000000FF, + 0x4CD, 0x000000FF, + 0x4CE, 0x00000001, + 0x500, 0x00000026, + 0x501, 0x000000A2, + 0x502, 0x0000002F, + 0x503, 0x00000000, + 0x504, 0x00000028, + 0x505, 0x000000A3, + 0x506, 0x0000005E, + 0x507, 0x00000000, + 0x508, 0x0000002B, + 0x509, 0x000000A4, + 0x50A, 0x0000005E, + 0x50B, 0x00000000, + 0x50C, 0x0000004F, + 0x50D, 0x000000A4, + 0x50E, 0x00000000, + 0x50F, 0x00000000, + 0x512, 0x0000001C, + 0x514, 0x0000000A, + 0x516, 0x0000000A, + 0x525, 0x0000004F, + 0x550, 0x00000010, + 0x551, 0x00000010, + 0x559, 0x00000002, + 0x55C, 0x00000050, + 0x55D, 0x000000FF, + 0x605, 0x00000030, + 0x608, 0x0000000E, + 0x609, 0x0000002A, + 0x620, 0x000000FF, + 0x621, 0x000000FF, + 0x622, 0x000000FF, + 0x623, 0x000000FF, + 0x624, 0x000000FF, + 0x625, 0x000000FF, + 0x626, 0x000000FF, + 0x627, 0x000000FF, + 0x638, 0x00000050, + 0x63C, 0x0000000A, + 0x63D, 0x0000000A, + 0x63E, 0x0000000E, + 0x63F, 0x0000000E, + 0x640, 0x00000040, + 0x642, 0x00000040, + 0x643, 0x00000000, + 0x652, 0x000000C8, + 0x66E, 0x00000005, + 0x700, 0x00000021, + 0x701, 0x00000043, + 0x702, 0x00000065, + 0x703, 0x00000087, + 0x708, 0x00000021, + 0x709, 0x00000043, + 0x70A, 0x00000065, + 0x70B, 0x00000087, +}; + +u32 RTL8723BEAGCTAB_1TARRAY[] = { + 0xC78, 0xFD000001, + 0xC78, 0xFC010001, + 0xC78, 0xFB020001, + 0xC78, 0xFA030001, + 0xC78, 0xF9040001, + 0xC78, 0xF8050001, + 0xC78, 0xF7060001, + 0xC78, 0xF6070001, + 0xC78, 0xF5080001, + 0xC78, 0xF4090001, + 0xC78, 0xF30A0001, + 0xC78, 0xF20B0001, + 0xC78, 0xF10C0001, + 0xC78, 0xF00D0001, + 0xC78, 0xEF0E0001, + 0xC78, 0xEE0F0001, + 0xC78, 0xED100001, + 0xC78, 0xEC110001, + 0xC78, 0xEB120001, + 0xC78, 0xEA130001, + 0xC78, 0xE9140001, + 0xC78, 0xE8150001, + 0xC78, 0xE7160001, + 0xC78, 0xAA170001, + 0xC78, 0xA9180001, + 0xC78, 0xA8190001, + 0xC78, 0xA71A0001, + 0xC78, 0xA61B0001, + 0xC78, 0xA51C0001, + 0xC78, 0xA41D0001, + 0xC78, 0xA31E0001, + 0xC78, 0x671F0001, + 0xC78, 0x66200001, + 0xC78, 0x65210001, + 0xC78, 0x64220001, + 0xC78, 0x63230001, + 0xC78, 0x62240001, + 0xC78, 0x61250001, + 0xC78, 0x47260001, + 0xC78, 0x46270001, + 0xC78, 0x45280001, + 0xC78, 0x44290001, + 0xC78, 0x432A0001, + 0xC78, 0x422B0001, + 0xC78, 0x292C0001, + 0xC78, 0x282D0001, + 0xC78, 0x272E0001, + 0xC78, 0x262F0001, + 0xC78, 0x25300001, + 0xC78, 0x24310001, + 0xC78, 0x09320001, + 0xC78, 0x08330001, + 0xC78, 0x07340001, + 0xC78, 0x06350001, + 0xC78, 0x05360001, + 0xC78, 0x04370001, + 0xC78, 0x03380001, + 0xC78, 0x02390001, + 0xC78, 0x013A0001, + 0xC78, 0x003B0001, + 0xC78, 0x003C0001, + 0xC78, 0x003D0001, + 0xC78, 0x003E0001, + 0xC78, 0x003F0001, + 0xC78, 0xFC400001, + 0xC78, 0xFB410001, + 0xC78, 0xFA420001, + 0xC78, 0xF9430001, + 0xC78, 0xF8440001, + 0xC78, 0xF7450001, + 0xC78, 0xF6460001, + 0xC78, 0xF5470001, + 0xC78, 0xF4480001, + 0xC78, 0xF3490001, + 0xC78, 0xF24A0001, + 0xC78, 0xF14B0001, + 0xC78, 0xF04C0001, + 0xC78, 0xEF4D0001, + 0xC78, 0xEE4E0001, + 0xC78, 0xED4F0001, + 0xC78, 0xEC500001, + 0xC78, 0xEB510001, + 0xC78, 0xEA520001, + 0xC78, 0xE9530001, + 0xC78, 0xE8540001, + 0xC78, 0xE7550001, + 0xC78, 0xE6560001, + 0xC78, 0xE5570001, + 0xC78, 0xAA580001, + 0xC78, 0xA9590001, + 0xC78, 0xA85A0001, + 0xC78, 0xA75B0001, + 0xC78, 0xA65C0001, + 0xC78, 0xA55D0001, + 0xC78, 0xA45E0001, + 0xC78, 0x675F0001, + 0xC78, 0x66600001, + 0xC78, 0x65610001, + 0xC78, 0x64620001, + 0xC78, 0x63630001, + 0xC78, 0x62640001, + 0xC78, 0x61650001, + 0xC78, 0x47660001, + 0xC78, 0x46670001, + 0xC78, 0x45680001, + 0xC78, 0x44690001, + 0xC78, 0x436A0001, + 0xC78, 0x426B0001, + 0xC78, 0x296C0001, + 0xC78, 0x286D0001, + 0xC78, 0x276E0001, + 0xC78, 0x266F0001, + 0xC78, 0x25700001, + 0xC78, 0x24710001, + 0xC78, 0x09720001, + 0xC78, 0x08730001, + 0xC78, 0x07740001, + 0xC78, 0x06750001, + 0xC78, 0x05760001, + 0xC78, 0x04770001, + 0xC78, 0x03780001, + 0xC78, 0x02790001, + 0xC78, 0x017A0001, + 0xC78, 0x007B0001, + 0xC78, 0x007C0001, + 0xC78, 0x007D0001, + 0xC78, 0x007E0001, + 0xC78, 0x007F0001, + 0xC50, 0x69553422, + 0xC50, 0x69553420, +}; diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/table.h b/drivers/net/wireless/rtlwifi/rtl8723be/table.h new file mode 100644 index 00000000000..932760a8482 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/table.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Created on 2010/ 5/18, 1:41 + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_TABLE__H_ +#define __RTL8723BE_TABLE__H_ + +#include <linux/types.h> +#define RTL8723BEPHY_REG_1TARRAYLEN 388 +extern u32 RTL8723BEPHY_REG_1TARRAY[]; +#define RTL8723BEPHY_REG_ARRAY_PGLEN 36 +extern u32 RTL8723BEPHY_REG_ARRAY_PG[]; +#define RTL8723BE_RADIOA_1TARRAYLEN 206 +extern u32 RTL8723BE_RADIOA_1TARRAY[]; +#define RTL8723BEMAC_1T_ARRAYLEN 194 +extern u32 RTL8723BEMAC_1T_ARRAY[]; +#define RTL8723BEAGCTAB_1TARRAYLEN 260 +extern u32 RTL8723BEAGCTAB_1TARRAY[]; + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c new file mode 100644 index 00000000000..969eaea5edd --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c @@ -0,0 +1,959 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "../base.h" +#include "../stats.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "trx.h" +#include "led.h" +#include "dm.h" + +static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) +{ + __le16 fc = rtl_get_fc(skb); + + if (unlikely(ieee80211_is_beacon(fc))) + return QSLT_BEACON; + if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) + return QSLT_MGNT; + + return skb->priority; +} + +/* mac80211's rate_idx is like this: + * + * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ + * + * B/G rate: + * (rx_status->flag & RX_FLAG_HT) = 0, + * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, + * + * N rate: + * (rx_status->flag & RX_FLAG_HT) = 1, + * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 + * + * 5G band:rx_status->band == IEEE80211_BAND_5GHZ + * A rate: + * (rx_status->flag & RX_FLAG_HT) = 0, + * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, + * + * N rate: + * (rx_status->flag & RX_FLAG_HT) = 1, + * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 + */ +static int _rtl8723be_rate_mapping(struct ieee80211_hw *hw, + bool isht, u8 desc_rate) +{ + int rate_idx; + + if (!isht) { + if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { + switch (desc_rate) { + case DESC92C_RATE1M: + rate_idx = 0; + break; + case DESC92C_RATE2M: + rate_idx = 1; + break; + case DESC92C_RATE5_5M: + rate_idx = 2; + break; + case DESC92C_RATE11M: + rate_idx = 3; + break; + case DESC92C_RATE6M: + rate_idx = 4; + break; + case DESC92C_RATE9M: + rate_idx = 5; + break; + case DESC92C_RATE12M: + rate_idx = 6; + break; + case DESC92C_RATE18M: + rate_idx = 7; + break; + case DESC92C_RATE24M: + rate_idx = 8; + break; + case DESC92C_RATE36M: + rate_idx = 9; + break; + case DESC92C_RATE48M: + rate_idx = 10; + break; + case DESC92C_RATE54M: + rate_idx = 11; + break; + default: + rate_idx = 0; + break; + } + } else { + switch (desc_rate) { + case DESC92C_RATE6M: + rate_idx = 0; + break; + case DESC92C_RATE9M: + rate_idx = 1; + break; + case DESC92C_RATE12M: + rate_idx = 2; + break; + case DESC92C_RATE18M: + rate_idx = 3; + break; + case DESC92C_RATE24M: + rate_idx = 4; + break; + case DESC92C_RATE36M: + rate_idx = 5; + break; + case DESC92C_RATE48M: + rate_idx = 6; + break; + case DESC92C_RATE54M: + rate_idx = 7; + break; + default: + rate_idx = 0; + break; + } + } + } else { + switch (desc_rate) { + case DESC92C_RATEMCS0: + rate_idx = 0; + break; + case DESC92C_RATEMCS1: + rate_idx = 1; + break; + case DESC92C_RATEMCS2: + rate_idx = 2; + break; + case DESC92C_RATEMCS3: + rate_idx = 3; + break; + case DESC92C_RATEMCS4: + rate_idx = 4; + break; + case DESC92C_RATEMCS5: + rate_idx = 5; + break; + case DESC92C_RATEMCS6: + rate_idx = 6; + break; + case DESC92C_RATEMCS7: + rate_idx = 7; + break; + case DESC92C_RATEMCS8: + rate_idx = 8; + break; + case DESC92C_RATEMCS9: + rate_idx = 9; + break; + case DESC92C_RATEMCS10: + rate_idx = 10; + break; + case DESC92C_RATEMCS11: + rate_idx = 11; + break; + case DESC92C_RATEMCS12: + rate_idx = 12; + break; + case DESC92C_RATEMCS13: + rate_idx = 13; + break; + case DESC92C_RATEMCS14: + rate_idx = 14; + break; + case DESC92C_RATEMCS15: + rate_idx = 15; + break; + default: + rate_idx = 0; + break; + } + } + return rate_idx; +} + +static void _rtl8723be_query_rxphystatus(struct ieee80211_hw *hw, + struct rtl_stats *pstatus, u8 *pdesc, + struct rx_fwinfo_8723be *p_drvinfo, + bool packet_match_bssid, + bool packet_toself, + bool packet_beacon) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv); + struct phy_sts_cck_8723e_t *cck_buf; + struct phy_status_rpt *p_phystrpt = (struct phy_status_rpt *)p_drvinfo; + struct rtl_dm *rtldm = rtl_dm(rtl_priv(hw)); + char rx_pwr_all = 0, rx_pwr[4]; + u8 rf_rx_num = 0, evm, pwdb_all; + u8 i, max_spatial_stream; + u32 rssi, total_rssi = 0; + bool is_cck = pstatus->is_cck; + u8 lan_idx, vga_idx; + + /* Record it for next packet processing */ + pstatus->packet_matchbssid = packet_match_bssid; + pstatus->packet_toself = packet_toself; + pstatus->packet_beacon = packet_beacon; + pstatus->rx_mimo_sig_qual[0] = -1; + pstatus->rx_mimo_sig_qual[1] = -1; + + if (is_cck) { + u8 cck_highpwr; + u8 cck_agc_rpt; + /* CCK Driver info Structure is not the same as OFDM packet. */ + cck_buf = (struct phy_sts_cck_8723e_t *)p_drvinfo; + cck_agc_rpt = cck_buf->cck_agc_rpt; + + /* (1)Hardware does not provide RSSI for CCK + * (2)PWDB, Average PWDB cacluated by + * hardware (for rate adaptive) + */ + if (ppsc->rfpwr_state == ERFON) + cck_highpwr = (u8) rtl_get_bbreg(hw, + RFPGA0_XA_HSSIPARAMETER2, + BIT(9)); + else + cck_highpwr = false; + + lan_idx = ((cck_agc_rpt & 0xE0) >> 5); + vga_idx = (cck_agc_rpt & 0x1f); + switch (lan_idx) { + case 7: + if (vga_idx <= 27)/*VGA_idx = 27~2*/ + rx_pwr_all = -100 + 2 * (27 - vga_idx); + else + rx_pwr_all = -100; + break; + case 6:/*VGA_idx = 2~0*/ + rx_pwr_all = -48 + 2 * (2 - vga_idx); + break; + case 5:/*VGA_idx = 7~5*/ + rx_pwr_all = -42 + 2 * (7 - vga_idx); + break; + case 4:/*VGA_idx = 7~4*/ + rx_pwr_all = -36 + 2 * (7 - vga_idx); + break; + case 3:/*VGA_idx = 7~0*/ + rx_pwr_all = -24 + 2 * (7 - vga_idx); + break; + case 2: + if (cck_highpwr)/*VGA_idx = 5~0*/ + rx_pwr_all = -12 + 2 * (5 - vga_idx); + else + rx_pwr_all = -6 + 2 * (5 - vga_idx); + break; + case 1: + rx_pwr_all = 8 - 2 * vga_idx; + break; + case 0: + rx_pwr_all = 14 - 2 * vga_idx; + break; + default: + break; + } + rx_pwr_all += 6; + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); + /* CCK gain is smaller than OFDM/MCS gain, */ + /* so we add gain diff by experiences, + * the val is 6 + */ + pwdb_all += 6; + if (pwdb_all > 100) + pwdb_all = 100; + /* modify the offset to make the same gain index with OFDM. */ + if (pwdb_all > 34 && pwdb_all <= 42) + pwdb_all -= 2; + else if (pwdb_all > 26 && pwdb_all <= 34) + pwdb_all -= 6; + else if (pwdb_all > 14 && pwdb_all <= 26) + pwdb_all -= 8; + else if (pwdb_all > 4 && pwdb_all <= 14) + pwdb_all -= 4; + if (!cck_highpwr) { + if (pwdb_all >= 80) + pwdb_all = ((pwdb_all - 80) << 1) + + ((pwdb_all - 80) >> 1) + 80; + else if ((pwdb_all <= 78) && (pwdb_all >= 20)) + pwdb_all += 3; + if (pwdb_all > 100) + pwdb_all = 100; + } + + pstatus->rx_pwdb_all = pwdb_all; + pstatus->recvsignalpower = rx_pwr_all; + + /* (3) Get Signal Quality (EVM) */ + if (packet_match_bssid) { + u8 sq; + + if (pstatus->rx_pwdb_all > 40) { + sq = 100; + } else { + sq = cck_buf->sq_rpt; + if (sq > 64) + sq = 0; + else if (sq < 20) + sq = 100; + else + sq = ((64 - sq) * 100) / 44; + } + + pstatus->signalquality = sq; + pstatus->rx_mimo_sig_qual[0] = sq; + pstatus->rx_mimo_sig_qual[1] = -1; + } + } else { + rtlpriv->dm.rfpath_rxenable[0] = true; + rtlpriv->dm.rfpath_rxenable[1] = true; + + /* (1)Get RSSI for HT rate */ + for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) { + /* we will judge RF RX path now. */ + if (rtlpriv->dm.rfpath_rxenable[i]) + rf_rx_num++; + + rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f)*2) - 110; + + /* Translate DBM to percentage. */ + rssi = rtl_query_rxpwrpercentage(rx_pwr[i]); + total_rssi += rssi; + + /* Get Rx snr value in DB */ + rtlpriv->stats.rx_snr_db[i] = + (long)(p_drvinfo->rxsnr[i] / 2); + + /* Record Signal Strength for next packet */ + if (packet_match_bssid) + pstatus->rx_mimo_signalstrength[i] = (u8) rssi; + } + + /* (2)PWDB, Avg cacluated by hardware (for rate adaptive) */ + rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; + + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); + pstatus->rx_pwdb_all = pwdb_all; + pstatus->rxpower = rx_pwr_all; + pstatus->recvsignalpower = rx_pwr_all; + + /* (3)EVM of HT rate */ + if (pstatus->is_ht && pstatus->rate >= DESC92C_RATEMCS8 && + pstatus->rate <= DESC92C_RATEMCS15) + max_spatial_stream = 2; + else + max_spatial_stream = 1; + + for (i = 0; i < max_spatial_stream; i++) { + evm = rtl_evm_db_to_percentage(p_drvinfo->rxevm[i]); + + if (packet_match_bssid) { + /* Fill value in RFD, Get the first + * spatial stream only + */ + if (i == 0) + pstatus->signalquality = + (u8) (evm & 0xff); + pstatus->rx_mimo_sig_qual[i] = + (u8) (evm & 0xff); + } + } + if (packet_match_bssid) { + for (i = RF90_PATH_A; i <= RF90_PATH_B; i++) + rtl_priv(hw)->dm.cfo_tail[i] = + (char)p_phystrpt->path_cfotail[i]; + + rtl_priv(hw)->dm.packet_count++; + if (rtl_priv(hw)->dm.packet_count == 0xffffffff) + rtl_priv(hw)->dm.packet_count = 0; + } + } + + /* UI BSS List signal strength(in percentage), + * make it good looking, from 0~100. + */ + if (is_cck) + pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw, + pwdb_all)); + else if (rf_rx_num != 0) + pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw, + total_rssi /= rf_rx_num)); + /*HW antenna diversity*/ + rtldm->fat_table.antsel_rx_keep_0 = p_phystrpt->ant_sel; + rtldm->fat_table.antsel_rx_keep_1 = p_phystrpt->ant_sel_b; + rtldm->fat_table.antsel_rx_keep_2 = p_phystrpt->antsel_rx_keep_2; +} + +static void _rtl8723be_translate_rx_signal_stuff(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct rtl_stats *pstatus, + u8 *pdesc, + struct rx_fwinfo_8723be *p_drvinfo) +{ + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct ieee80211_hdr *hdr; + u8 *tmp_buf; + u8 *praddr; + u8 *psaddr; + u16 fc, type; + bool packet_matchbssid, packet_toself, packet_beacon; + + tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift; + + hdr = (struct ieee80211_hdr *)tmp_buf; + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(hdr->frame_control); + praddr = hdr->addr1; + psaddr = ieee80211_get_SA(hdr); + memcpy(pstatus->psaddr, psaddr, ETH_ALEN); + + packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && + (!ether_addr_equal(mac->bssid, (fc & IEEE80211_FCTL_TODS) ? + hdr->addr1 : (fc & IEEE80211_FCTL_FROMDS) ? + hdr->addr2 : hdr->addr3)) && + (!pstatus->hwerror) && + (!pstatus->crc) && (!pstatus->icv)); + + packet_toself = packet_matchbssid && + (!ether_addr_equal(praddr, rtlefuse->dev_addr)); + + /* YP: packet_beacon is not initialized, + * this assignment is neccesary, + * otherwise it counld be true in this case + * the situation is much worse in Kernel 3.10 + */ + if (ieee80211_is_beacon(hdr->frame_control)) + packet_beacon = true; + else + packet_beacon = false; + + if (packet_beacon && packet_matchbssid) + rtl_priv(hw)->dm.dbginfo.num_qry_beacon_pkt++; + + _rtl8723be_query_rxphystatus(hw, pstatus, pdesc, p_drvinfo, + packet_matchbssid, + packet_toself, + packet_beacon); + + rtl_process_phyinfo(hw, tmp_buf, pstatus); +} + +static void _rtl8723be_insert_emcontent(struct rtl_tcb_desc *ptcb_desc, + u8 *virtualaddress) +{ + u32 dwtmp = 0; + memset(virtualaddress, 0, 8); + + SET_EARLYMODE_PKTNUM(virtualaddress, ptcb_desc->empkt_num); + if (ptcb_desc->empkt_num == 1) { + dwtmp = ptcb_desc->empkt_len[0]; + } else { + dwtmp = ptcb_desc->empkt_len[0]; + dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4; + dwtmp += ptcb_desc->empkt_len[1]; + } + SET_EARLYMODE_LEN0(virtualaddress, dwtmp); + + if (ptcb_desc->empkt_num <= 3) { + dwtmp = ptcb_desc->empkt_len[2]; + } else { + dwtmp = ptcb_desc->empkt_len[2]; + dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4; + dwtmp += ptcb_desc->empkt_len[3]; + } + SET_EARLYMODE_LEN1(virtualaddress, dwtmp); + if (ptcb_desc->empkt_num <= 5) { + dwtmp = ptcb_desc->empkt_len[4]; + } else { + dwtmp = ptcb_desc->empkt_len[4]; + dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4; + dwtmp += ptcb_desc->empkt_len[5]; + } + SET_EARLYMODE_LEN2_1(virtualaddress, dwtmp & 0xF); + SET_EARLYMODE_LEN2_2(virtualaddress, dwtmp >> 4); + if (ptcb_desc->empkt_num <= 7) { + dwtmp = ptcb_desc->empkt_len[6]; + } else { + dwtmp = ptcb_desc->empkt_len[6]; + dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4; + dwtmp += ptcb_desc->empkt_len[7]; + } + SET_EARLYMODE_LEN3(virtualaddress, dwtmp); + if (ptcb_desc->empkt_num <= 9) { + dwtmp = ptcb_desc->empkt_len[8]; + } else { + dwtmp = ptcb_desc->empkt_len[8]; + dwtmp += ((dwtmp % 4) ? (4 - dwtmp % 4) : 0) + 4; + dwtmp += ptcb_desc->empkt_len[9]; + } + SET_EARLYMODE_LEN4(virtualaddress, dwtmp); +} + +bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, + struct rtl_stats *status, + struct ieee80211_rx_status *rx_status, + u8 *pdesc, struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rx_fwinfo_8723be *p_drvinfo; + struct ieee80211_hdr *hdr; + + u32 phystatus = GET_RX_DESC_PHYST(pdesc); + status->packet_report_type = (u8)GET_RX_STATUS_DESC_RPT_SEL(pdesc); + if (status->packet_report_type == TX_REPORT2) + status->length = (u16) GET_RX_RPT2_DESC_PKT_LEN(pdesc); + else + status->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); + status->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) * + RX_DRV_INFO_SIZE_UNIT; + status->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03); + status->icv = (u16) GET_RX_DESC_ICV(pdesc); + status->crc = (u16) GET_RX_DESC_CRC32(pdesc); + status->hwerror = (status->crc | status->icv); + status->decrypted = !GET_RX_DESC_SWDEC(pdesc); + status->rate = (u8) GET_RX_DESC_RXMCS(pdesc); + status->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc); + status->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); + status->isfirst_ampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); + if (status->packet_report_type == NORMAL_RX) + status->timestamp_low = GET_RX_DESC_TSFL(pdesc); + status->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); + status->is_ht = (bool)GET_RX_DESC_RXHT(pdesc); + + status->is_cck = RTL8723E_RX_HAL_IS_CCK_RATE(status->rate); + + status->macid = GET_RX_DESC_MACID(pdesc); + if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc)) + status->wake_match = BIT(2); + else if (GET_RX_STATUS_DESC_MAGIC_MATCH(pdesc)) + status->wake_match = BIT(1); + else if (GET_RX_STATUS_DESC_UNICAST_MATCH(pdesc)) + status->wake_match = BIT(0); + else + status->wake_match = 0; + if (status->wake_match) + RT_TRACE(rtlpriv, COMP_RXDESC, DBG_LOUD, + "GGGGGGGGGGGGGet Wakeup Packet!! WakeMatch=%d\n", + status->wake_match); + rx_status->freq = hw->conf.chandef.chan->center_freq; + rx_status->band = hw->conf.chandef.chan->band; + + + hdr = (struct ieee80211_hdr *)(skb->data + status->rx_drvinfo_size + + status->rx_bufshift); + + if (status->crc) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (status->rx_is40Mhzpacket) + rx_status->flag |= RX_FLAG_40MHZ; + + if (status->is_ht) + rx_status->flag |= RX_FLAG_HT; + + rx_status->flag |= RX_FLAG_MACTIME_START; + + /* hw will set status->decrypted true, if it finds the + * frame is open data frame or mgmt frame. + * So hw will not decryption robust managment frame + * for IEEE80211w but still set status->decrypted + * true, so here we should set it back to undecrypted + * for IEEE80211w frame, and mac80211 sw will help + * to decrypt it + */ + if (status->decrypted) { + if (!hdr) { + WARN_ON_ONCE(true); + pr_err("decrypted is true but hdr NULL in skb %p\n", + rtl_get_hdr(skb)); + return false; + } + + if ((_ieee80211_is_robust_mgmt_frame(hdr)) && + (ieee80211_has_protected(hdr->frame_control))) + rx_status->flag &= ~RX_FLAG_DECRYPTED; + else + rx_status->flag |= RX_FLAG_DECRYPTED; + } + + /* rate_idx: index of data rate into band's + * supported rates or MCS index if HT rates + * are use (RX_FLAG_HT) + * Notice: this is diff with windows define + */ + rx_status->rate_idx = _rtl8723be_rate_mapping(hw, status->is_ht, + status->rate); + + rx_status->mactime = status->timestamp_low; + if (phystatus) { + p_drvinfo = (struct rx_fwinfo_8723be *)(skb->data + + status->rx_bufshift); + + _rtl8723be_translate_rx_signal_stuff(hw, skb, status, + pdesc, p_drvinfo); + } + + /*rx_status->qual = status->signal; */ + rx_status->signal = status->recvsignalpower + 10; + if (status->packet_report_type == TX_REPORT2) { + status->macid_valid_entry[0] = + GET_RX_RPT2_DESC_MACID_VALID_1(pdesc); + status->macid_valid_entry[1] = + GET_RX_RPT2_DESC_MACID_VALID_2(pdesc); + } + return true; +} + +void rtl8723be_tx_fill_desc(struct ieee80211_hw *hw, + struct ieee80211_hdr *hdr, u8 *pdesc_tx, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, + u8 hw_queue, struct rtl_tcb_desc *ptcb_desc) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 *pdesc = pdesc_tx; + u16 seq_number; + __le16 fc = hdr->frame_control; + unsigned int buf_len = 0; + unsigned int skb_len = skb->len; + u8 fw_qsel = _rtl8723be_map_hwqueue_to_fwqueue(skb, hw_queue); + bool firstseg = ((hdr->seq_ctrl & + cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0); + bool lastseg = ((hdr->frame_control & + cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0); + dma_addr_t mapping; + u8 bw_40 = 0; + u8 short_gi = 0; + + if (mac->opmode == NL80211_IFTYPE_STATION) { + bw_40 = mac->bw_40; + } else if (mac->opmode == NL80211_IFTYPE_AP || + mac->opmode == NL80211_IFTYPE_ADHOC) { + if (sta) + bw_40 = sta->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; + rtl_get_tcb_desc(hw, info, sta, skb, ptcb_desc); + /* reserve 8 byte for AMPDU early mode */ + if (rtlhal->earlymode_enable) { + skb_push(skb, EM_HDR_LEN); + memset(skb->data, 0, EM_HDR_LEN); + } + buf_len = skb->len; + mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len, + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "DMA mapping error"); + return; + } + CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_8723be)); + if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) { + firstseg = true; + lastseg = true; + } + if (firstseg) { + if (rtlhal->earlymode_enable) { + SET_TX_DESC_PKT_OFFSET(pdesc, 1); + SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN + + EM_HDR_LEN); + if (ptcb_desc->empkt_num) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "Insert 8 byte.pTcb->EMPktNum:%d\n", + ptcb_desc->empkt_num); + _rtl8723be_insert_emcontent(ptcb_desc, + (u8 *)(skb->data)); + } + } else { + SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); + } + + /* ptcb_desc->use_driver_rate = true; */ + SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate); + if (ptcb_desc->hw_rate > DESC92C_RATEMCS0) + short_gi = (ptcb_desc->use_shortgi) ? 1 : 0; + else + short_gi = (ptcb_desc->use_shortpreamble) ? 1 : 0; + + SET_TX_DESC_DATA_SHORTGI(pdesc, short_gi); + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + SET_TX_DESC_AGG_ENABLE(pdesc, 1); + SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14); + } + SET_TX_DESC_SEQ(pdesc, seq_number); + SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcb_desc->rts_enable && + !ptcb_desc->cts_enable) ? + 1 : 0)); + SET_TX_DESC_HW_RTS_ENABLE(pdesc, 0); + SET_TX_DESC_CTS2SELF(pdesc, ((ptcb_desc->cts_enable) ? + 1 : 0)); + + SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); + + SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); + SET_TX_DESC_RTS_SHORT(pdesc, + ((ptcb_desc->rts_rate <= DESC92C_RATE54M) ? + (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : + (ptcb_desc->rts_use_shortgi ? 1 : 0))); + + if (ptcb_desc->btx_enable_sw_calc_duration) + SET_TX_DESC_NAV_USE_HDR(pdesc, 1); + + if (bw_40) { + if (ptcb_desc->packet_bw) { + SET_TX_DESC_DATA_BW(pdesc, 1); + SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3); + } else { + SET_TX_DESC_DATA_BW(pdesc, 0); + SET_TX_DESC_TX_SUB_CARRIER(pdesc, mac->cur_40_prime_sc); + } + } else { + SET_TX_DESC_DATA_BW(pdesc, 0); + SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); + } + + SET_TX_DESC_LINIP(pdesc, 0); + SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb_len); + if (sta) { + u8 ampdu_density = sta->ht_cap.ampdu_density; + SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density); + } + if (info->control.hw_key) { + struct ieee80211_key_conf *keyconf = + info->control.hw_key; + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: + SET_TX_DESC_SEC_TYPE(pdesc, 0x1); + break; + case WLAN_CIPHER_SUITE_CCMP: + SET_TX_DESC_SEC_TYPE(pdesc, 0x3); + break; + default: + SET_TX_DESC_SEC_TYPE(pdesc, 0x0); + break; + } + } + + SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel); + SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F); + SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF); + SET_TX_DESC_DISABLE_FB(pdesc, ptcb_desc->disable_ratefallback ? + 1 : 0); + SET_TX_DESC_USE_RATE(pdesc, ptcb_desc->use_driver_rate ? 1 : 0); + + if (ieee80211_is_data_qos(fc)) { + if (mac->rdg_en) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "Enable RDG function.\n"); + SET_TX_DESC_RDG_ENABLE(pdesc, 1); + SET_TX_DESC_HTC(pdesc, 1); + } + } + } + + SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0)); + SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0)); + SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) buf_len); + SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); + SET_TX_DESC_RATE_ID(pdesc, ptcb_desc->ratr_index); + SET_TX_DESC_MACID(pdesc, ptcb_desc->mac_id); + + if (!ieee80211_is_data_qos(fc)) { + SET_TX_DESC_HWSEQ_EN(pdesc, 1); + SET_TX_DESC_HWSEQ_SEL(pdesc, 0); + } + SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1)); + if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) || + is_broadcast_ether_addr(ieee80211_get_DA(hdr))) { + SET_TX_DESC_BMC(pdesc, 1); + } + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n"); +} + +void rtl8723be_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, + bool b_firstseg, bool b_lastseg, + struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + u8 fw_queue = QSLT_BEACON; + + dma_addr_t mapping = pci_map_single(rtlpci->pdev, + skb->data, skb->len, + PCI_DMA_TODEVICE); + + if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "DMA mapping error"); + return; + } + CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); + + SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); + + SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M); + + SET_TX_DESC_SEQ(pdesc, 0); + + SET_TX_DESC_LINIP(pdesc, 0); + + SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); + + SET_TX_DESC_FIRST_SEG(pdesc, 1); + SET_TX_DESC_LAST_SEG(pdesc, 1); + + SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16)(skb->len)); + + SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); + + SET_TX_DESC_RATE_ID(pdesc, 0); + SET_TX_DESC_MACID(pdesc, 0); + + SET_TX_DESC_OWN(pdesc, 1); + + SET_TX_DESC_PKT_SIZE(pdesc, (u16)(skb->len)); + + SET_TX_DESC_FIRST_SEG(pdesc, 1); + SET_TX_DESC_LAST_SEG(pdesc, 1); + + SET_TX_DESC_USE_RATE(pdesc, 1); +} + +void rtl8723be_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val) +{ + if (istx) { + switch (desc_name) { + case HW_DESC_OWN: + SET_TX_DESC_OWN(pdesc, 1); + break; + case HW_DESC_TX_NEXTDESC_ADDR: + SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *)val); + break; + default: + RT_ASSERT(false, "ERR txdesc :%d not process\n", + desc_name); + break; + } + } else { + switch (desc_name) { + case HW_DESC_RXOWN: + SET_RX_DESC_OWN(pdesc, 1); + break; + case HW_DESC_RXBUFF_ADDR: + SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *)val); + break; + case HW_DESC_RXPKT_LEN: + SET_RX_DESC_PKT_LEN(pdesc, *(u32 *)val); + break; + case HW_DESC_RXERO: + SET_RX_DESC_EOR(pdesc, 1); + break; + default: + RT_ASSERT(false, "ERR rxdesc :%d not process\n", + desc_name); + break; + } + } +} + +u32 rtl8723be_get_desc(u8 *pdesc, bool istx, u8 desc_name) +{ + u32 ret = 0; + + if (istx) { + switch (desc_name) { + case HW_DESC_OWN: + ret = GET_TX_DESC_OWN(pdesc); + break; + case HW_DESC_TXBUFF_ADDR: + ret = GET_TX_DESC_TX_BUFFER_ADDRESS(pdesc); + break; + default: + RT_ASSERT(false, "ERR txdesc :%d not process\n", + desc_name); + break; + } + } else { + switch (desc_name) { + case HW_DESC_OWN: + ret = GET_RX_DESC_OWN(pdesc); + break; + case HW_DESC_RXPKT_LEN: + ret = GET_RX_DESC_PKT_LEN(pdesc); + break; + default: + RT_ASSERT(false, "ERR rxdesc :%d not process\n", + desc_name); + break; + } + } + return ret; +} + +bool rtl8723be_is_tx_desc_closed(struct ieee80211_hw *hw, + u8 hw_queue, u16 index) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue]; + u8 *entry = (u8 *)(&ring->desc[ring->idx]); + u8 own = (u8) rtl8723be_get_desc(entry, true, HW_DESC_OWN); + + /*beacon packet will only use the first + *descriptor by default, and the own may not + *be cleared by the hardware + */ + if (own) + return false; + else + return true; +} + +void rtl8723be_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + if (hw_queue == BEACON_QUEUE) { + rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4)); + } else { + rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, + BIT(0) << (hw_queue)); + } +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.h b/drivers/net/wireless/rtlwifi/rtl8723be/trx.h new file mode 100644 index 00000000000..102f33dcc98 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.h @@ -0,0 +1,617 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723BE_TRX_H__ +#define __RTL8723BE_TRX_H__ + +#define TX_DESC_SIZE 40 +#define TX_DESC_AGGR_SUBFRAME_SIZE 32 + +#define RX_DESC_SIZE 32 +#define RX_DRV_INFO_SIZE_UNIT 8 + +#define TX_DESC_NEXT_DESC_OFFSET 40 +#define USB_HWDESC_HEADER_LEN 40 +#define CRCLENGTH 4 + +#define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 0, 16, __val) +#define SET_TX_DESC_OFFSET(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 16, 8, __val) +#define SET_TX_DESC_BMC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 24, 1, __val) +#define SET_TX_DESC_HTC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 25, 1, __val) +#define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 26, 1, __val) +#define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 27, 1, __val) +#define SET_TX_DESC_LINIP(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 28, 1, __val) +#define SET_TX_DESC_NO_ACM(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 29, 1, __val) +#define SET_TX_DESC_GF(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val) +#define SET_TX_DESC_OWN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val) + +#define GET_TX_DESC_PKT_SIZE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 0, 16) +#define GET_TX_DESC_OFFSET(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 16, 8) +#define GET_TX_DESC_BMC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 24, 1) +#define GET_TX_DESC_HTC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 25, 1) +#define GET_TX_DESC_LAST_SEG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 26, 1) +#define GET_TX_DESC_FIRST_SEG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 27, 1) +#define GET_TX_DESC_LINIP(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 28, 1) +#define GET_TX_DESC_NO_ACM(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 29, 1) +#define GET_TX_DESC_GF(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 30, 1) +#define GET_TX_DESC_OWN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 31, 1) + +#define SET_TX_DESC_MACID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 0, 7, __val) +#define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 8, 5, __val) +#define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 13, 1, __val) +#define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 14, 1, __val) +#define SET_TX_DESC_PIFS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 15, 1, __val) +#define SET_TX_DESC_RATE_ID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 16, 5, __val) +#define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 21, 1, __val) +#define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 22, 2, __val) +#define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 24, 5, __val) + + +#define SET_TX_DESC_PAID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 0, 9, __val) +#define SET_TX_DESC_CCA_RTS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 10, 2, __val) +#define SET_TX_DESC_AGG_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 12, 1, __val) +#define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 13, 1, __val) +#define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 14, 2, __val) +#define SET_TX_DESC_AGG_BREAK(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 16, 1, __val) +#define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 17, 1, __val) +#define SET_TX_DESC_RAW(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 18, 1, __val) +#define SET_TX_DESC_SPE_RPT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 19, 1, __val) +#define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 20, 3, __val) +#define SET_TX_DESC_BT_INT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 23, 1, __val) +#define SET_TX_DESC_GID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 24, 6, __val) + + +#define SET_TX_DESC_WHEADER_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 0, 4, __val) +#define SET_TX_DESC_CHK_EN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 4, 1, __val) +#define SET_TX_DESC_EARLY_MODE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 5, 1, __val) +#define SET_TX_DESC_HWSEQ_SEL(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 6, 2, __val) +#define SET_TX_DESC_USE_RATE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 8, 1, __val) +#define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 9, 1, __val) +#define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 10, 1, __val) +#define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 11, 1, __val) +#define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 12, 1, __val) +#define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 13, 1, __val) +#define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 15, 1, __val) +#define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 16, 1, __val) +#define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 17, 5, __val) +#define SET_TX_DESC_NDPA(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 22, 2, __val) +#define SET_TX_DESC_AMPDU_MAX_TIME(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 24, 8, __val) + + +#define SET_TX_DESC_TX_RATE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 0, 7, __val) +#define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 8, 5, __val) +#define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 13, 4, __val) +#define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 17, 1, __val) +#define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 18, 6, __val) +#define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 24, 5, __val) + + +#define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 0, 4, __val) +#define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 4, 1, __val) +#define SET_TX_DESC_DATA_BW(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 5, 2, __val) +#define SET_TX_DESC_DATA_LDPC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 7, 1, __val) +#define SET_TX_DESC_DATA_STBC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 8, 2, __val) +#define SET_TX_DESC_CTROL_STBC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 10, 2, __val) +#define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 12, 1, __val) +#define SET_TX_DESC_RTS_SC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 13, 4, __val) + + +#define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 16, __val) + +#define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 0, 16) + +#define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+32, 15, 1, __val) + +#define SET_TX_DESC_SEQ(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+36, 12, 12, __val) + +#define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+40, 0, 32, __val) + +#define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+40, 0, 32) + + +#define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+48, 0, 32, __val) + +#define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+48, 0, 32) + +#define GET_RX_DESC_PKT_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 0, 14) +#define GET_RX_DESC_CRC32(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 14, 1) +#define GET_RX_DESC_ICV(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 15, 1) +#define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 16, 4) +#define GET_RX_DESC_SECURITY(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 20, 3) +#define GET_RX_DESC_QOS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 23, 1) +#define GET_RX_DESC_SHIFT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 24, 2) +#define GET_RX_DESC_PHYST(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 26, 1) +#define GET_RX_DESC_SWDEC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 27, 1) +#define GET_RX_DESC_LS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 28, 1) +#define GET_RX_DESC_FS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 29, 1) +#define GET_RX_DESC_EOR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 30, 1) +#define GET_RX_DESC_OWN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 31, 1) + +#define SET_RX_DESC_PKT_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 0, 14, __val) +#define SET_RX_DESC_EOR(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val) +#define SET_RX_DESC_OWN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val) + +#define GET_RX_DESC_MACID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 0, 7) +#define GET_RX_DESC_TID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 8, 4) +#define GET_RX_DESC_AMSDU(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 13, 1) +#define GET_RX_STATUS_DESC_RXID_MATCH(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 14, 1) +#define GET_RX_DESC_PAGGR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 15, 1) +#define GET_RX_DESC_A1_FIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 16, 4) +#define GET_RX_DESC_CHKERR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 20, 1) +#define GET_RX_DESC_IPVER(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 21, 1) +#define GET_RX_STATUS_DESC_IS_TCPUDP(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 22, 1) +#define GET_RX_STATUS_DESC_CHK_VLD(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 23, 1) +#define GET_RX_DESC_PAM(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 24, 1) +#define GET_RX_DESC_PWR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 25, 1) +#define GET_RX_DESC_MD(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 26, 1) +#define GET_RX_DESC_MF(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 27, 1) +#define GET_RX_DESC_TYPE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 28, 2) +#define GET_RX_DESC_MC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 30, 1) +#define GET_RX_DESC_BC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 31, 1) + + +#define GET_RX_DESC_SEQ(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 0, 12) +#define GET_RX_DESC_FRAG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 12, 4) +#define GET_RX_STATUS_DESC_RX_IS_QOS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 16, 1) +#define GET_RX_STATUS_DESC_WLANHD_IV_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 18, 6) +#define GET_RX_STATUS_DESC_RPT_SEL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 28, 1) + + +#define GET_RX_DESC_RXMCS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 0, 7) +#define GET_RX_DESC_RXHT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 6, 1) +#define GET_RX_STATUS_DESC_RX_GF(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 7, 1) +#define GET_RX_DESC_HTC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 10, 1) +#define GET_RX_STATUS_DESC_EOSP(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 11, 1) +#define GET_RX_STATUS_DESC_BSSID_FIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 12, 2) + +#define GET_RX_STATUS_DESC_PATTERN_MATCH(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 29, 1) +#define GET_RX_STATUS_DESC_UNICAST_MATCH(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 30, 1) +#define GET_RX_STATUS_DESC_MAGIC_MATCH(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 31, 1) + +#define GET_RX_DESC_SPLCP(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 0, 1) +#define GET_RX_STATUS_DESC_LDPC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 1, 1) +#define GET_RX_STATUS_DESC_STBC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 2, 1) +#define GET_RX_DESC_BW(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 4, 2) + +#define GET_RX_DESC_TSFL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 0, 32) + +#define GET_RX_DESC_BUFF_ADDR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 0, 32) +#define GET_RX_DESC_BUFF_ADDR64(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 0, 32) + +#define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 0, 32, __val) +#define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 32, __val) + + +/* TX report 2 format in Rx desc*/ + +#define GET_RX_RPT2_DESC_PKT_LEN(__rxstatusdesc) \ + LE_BITS_TO_4BYTE(__rxstatusdesc, 0, 9) +#define GET_RX_RPT2_DESC_MACID_VALID_1(__rxstatusdesc) \ + LE_BITS_TO_4BYTE(__rxstatusdesc+16, 0, 32) +#define GET_RX_RPT2_DESC_MACID_VALID_2(__rxstatusdesc) \ + LE_BITS_TO_4BYTE(__rxstatusdesc+20, 0, 32) + +#define SET_EARLYMODE_PKTNUM(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr, 0, 4, __value) +#define SET_EARLYMODE_LEN0(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr, 4, 12, __value) +#define SET_EARLYMODE_LEN1(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr, 16, 12, __value) +#define SET_EARLYMODE_LEN2_1(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr, 28, 4, __value) +#define SET_EARLYMODE_LEN2_2(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr+4, 0, 8, __value) +#define SET_EARLYMODE_LEN3(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr+4, 8, 12, __value) +#define SET_EARLYMODE_LEN4(__paddr, __value) \ + SET_BITS_TO_LE_4BYTE(__paddr+4, 20, 12, __value) + +#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ +do { \ + if (_size > TX_DESC_NEXT_DESC_OFFSET) \ + memset(__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \ + else \ + memset(__pdesc, 0, _size); \ +} while (0) + +struct phy_rx_agc_info_t { + #ifdef __LITTLE_ENDIAN + u8 gain:7, trsw:1; + #else + u8 trsw:1, gain:7; + #endif +}; +struct phy_status_rpt { + struct phy_rx_agc_info_t path_agc[2]; + u8 ch_corr[2]; + u8 cck_sig_qual_ofdm_pwdb_all; + u8 cck_agc_rpt_ofdm_cfosho_a; + u8 cck_rpt_b_ofdm_cfosho_b; + u8 rsvd_1;/* ch_corr_msb; */ + u8 noise_power_db_msb; + char path_cfotail[2]; + u8 pcts_mask[2]; + char stream_rxevm[2]; + u8 path_rxsnr[2]; + u8 noise_power_db_lsb; + u8 rsvd_2[3]; + u8 stream_csi[2]; + u8 stream_target_csi[2]; + u8 sig_evm; + u8 rsvd_3; +#ifdef __LITTLE_ENDIAN + u8 antsel_rx_keep_2:1; /*ex_intf_flg:1;*/ + u8 sgi_en:1; + u8 rxsc:2; + u8 idle_long:1; + u8 r_ant_train_en:1; + u8 ant_sel_b:1; + u8 ant_sel:1; +#else /* _BIG_ENDIAN_ */ + u8 ant_sel:1; + u8 ant_sel_b:1; + u8 r_ant_train_en:1; + u8 idle_long:1; + u8 rxsc:2; + u8 sgi_en:1; + u8 antsel_rx_keep_2:1; /*ex_intf_flg:1;*/ +#endif +} __packed; + +struct rx_fwinfo_8723be { + u8 gain_trsw[4]; + u8 pwdb_all; + u8 cfosho[4]; + u8 cfotail[4]; + char rxevm[2]; + char rxsnr[4]; + u8 pdsnr[2]; + u8 csi_current[2]; + u8 csi_target[2]; + u8 sigevm; + u8 max_ex_pwr; + u8 ex_intf_flag:1; + u8 sgi_en:1; + u8 rxsc:2; + u8 reserve:4; +} __packed; + +struct tx_desc_8723be { + u32 pktsize:16; + u32 offset:8; + u32 bmc:1; + u32 htc:1; + u32 lastseg:1; + u32 firstseg:1; + u32 linip:1; + u32 noacm:1; + u32 gf:1; + u32 own:1; + + u32 macid:6; + u32 rsvd0:2; + u32 queuesel:5; + u32 rd_nav_ext:1; + u32 lsig_txop_en:1; + u32 pifs:1; + u32 rateid:4; + u32 nav_usehdr:1; + u32 en_descid:1; + u32 sectype:2; + u32 pktoffset:8; + + u32 rts_rc:6; + u32 data_rc:6; + u32 agg_en:1; + u32 rdg_en:1; + u32 bar_retryht:2; + u32 agg_break:1; + u32 morefrag:1; + u32 raw:1; + u32 ccx:1; + u32 ampdudensity:3; + u32 bt_int:1; + u32 ant_sela:1; + u32 ant_selb:1; + u32 txant_cck:2; + u32 txant_l:2; + u32 txant_ht:2; + + u32 nextheadpage:8; + u32 tailpage:8; + u32 seq:12; + u32 cpu_handle:1; + u32 tag1:1; + u32 trigger_int:1; + u32 hwseq_en:1; + + u32 rtsrate:5; + u32 apdcfe:1; + u32 qos:1; + u32 hwseq_ssn:1; + u32 userrate:1; + u32 dis_rtsfb:1; + u32 dis_datafb:1; + u32 cts2self:1; + u32 rts_en:1; + u32 hwrts_en:1; + u32 portid:1; + u32 pwr_status:3; + u32 waitdcts:1; + u32 cts2ap_en:1; + u32 txsc:2; + u32 stbc:2; + u32 txshort:1; + u32 txbw:1; + u32 rtsshort:1; + u32 rtsbw:1; + u32 rtssc:2; + u32 rtsstbc:2; + + u32 txrate:6; + u32 shortgi:1; + u32 ccxt:1; + u32 txrate_fb_lmt:5; + u32 rtsrate_fb_lmt:4; + u32 retrylmt_en:1; + u32 txretrylmt:6; + u32 usb_txaggnum:8; + + u32 txagca:5; + u32 txagcb:5; + u32 usemaxlen:1; + u32 maxaggnum:5; + u32 mcsg1maxlen:4; + u32 mcsg2maxlen:4; + u32 mcsg3maxlen:4; + u32 mcs7sgimaxlen:4; + + u32 txbuffersize:16; + u32 sw_offset30:8; + u32 sw_offset31:4; + u32 rsvd1:1; + u32 antsel_c:1; + u32 null_0:1; + u32 null_1:1; + + u32 txbuffaddr; + u32 txbufferaddr64; + u32 nextdescaddress; + u32 nextdescaddress64; + + u32 reserve_pass_pcie_mm_limit[4]; +} __packed; + +struct rx_desc_8723be { + u32 length:14; + u32 crc32:1; + u32 icverror:1; + u32 drv_infosize:4; + u32 security:3; + u32 qos:1; + u32 shift:2; + u32 phystatus:1; + u32 swdec:1; + u32 lastseg:1; + u32 firstseg:1; + u32 eor:1; + u32 own:1; + + u32 macid:6; + u32 tid:4; + u32 hwrsvd:5; + u32 paggr:1; + u32 faggr:1; + u32 a1_fit:4; + u32 a2_fit:4; + u32 pam:1; + u32 pwr:1; + u32 moredata:1; + u32 morefrag:1; + u32 type:2; + u32 mc:1; + u32 bc:1; + + u32 seq:12; + u32 frag:4; + u32 nextpktlen:14; + u32 nextind:1; + u32 rsvd:1; + + u32 rxmcs:6; + u32 rxht:1; + u32 amsdu:1; + u32 splcp:1; + u32 bandwidth:1; + u32 htc:1; + u32 tcpchk_rpt:1; + u32 ipcchk_rpt:1; + u32 tcpchk_valid:1; + u32 hwpcerr:1; + u32 hwpcind:1; + u32 iv0:16; + + u32 iv1; + + u32 tsfl; + + u32 bufferaddress; + u32 bufferaddress64; + +} __packed; + +void rtl8723be_tx_fill_desc(struct ieee80211_hw *hw, + struct ieee80211_hdr *hdr, u8 *pdesc, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, struct sk_buff *skb, + u8 hw_queue, struct rtl_tcb_desc *ptcb_desc); +bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, + struct rtl_stats *status, + struct ieee80211_rx_status *rx_status, + u8 *pdesc, struct sk_buff *skb); +void rtl8723be_set_desc(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); +u32 rtl8723be_get_desc(u8 *pdesc, bool istx, u8 desc_name); +bool rtl8723be_is_tx_desc_closed(struct ieee80211_hw *hw, + u8 hw_queue, u16 index); +void rtl8723be_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); +void rtl8723be_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, + bool b_firstseg, bool b_lastseg, + struct sk_buff *skb); +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/Makefile b/drivers/net/wireless/rtlwifi/rtl8723com/Makefile new file mode 100644 index 00000000000..345a68adcf3 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/Makefile @@ -0,0 +1,9 @@ +rtl8723-common-objs := \ + main.o \ + dm_common.o \ + fw_common.o \ + phy_common.o + +obj-$(CONFIG_RTL8723_COMMON) += rtl8723-common.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c new file mode 100644 index 00000000000..4e254b72bf4 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.c @@ -0,0 +1,65 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "dm_common.h" +#include "../rtl8723ae/dm.h" +#include <linux/module.h> + +/* These routines are common to RTL8723AE and RTL8723bE */ + +void rtl8723_dm_init_dynamic_txpower(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.dynamic_txpower_enable = false; + + rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; +} +EXPORT_SYMBOL_GPL(rtl8723_dm_init_dynamic_txpower); + +void rtl8723_dm_init_edca_turbo(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.current_turbo_edca = false; + rtlpriv->dm.is_any_nonbepkts = false; + rtlpriv->dm.is_cur_rdlstate = false; +} +EXPORT_SYMBOL_GPL(rtl8723_dm_init_edca_turbo); + +void rtl8723_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm_pstable.pre_ccastate = CCA_MAX; + rtlpriv->dm_pstable.cur_ccasate = CCA_MAX; + rtlpriv->dm_pstable.pre_rfstate = RF_MAX; + rtlpriv->dm_pstable.cur_rfstate = RF_MAX; + rtlpriv->dm_pstable.rssi_val_min = 0; + rtlpriv->dm_pstable.initialize = 0; +} +EXPORT_SYMBOL_GPL(rtl8723_dm_init_dynamic_bb_powersaving); diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h new file mode 100644 index 00000000000..5c1b94ce2f8 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/dm_common.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __DM_COMMON_H__ +#define __DM_COMMON_H__ + +void rtl8723_dm_init_dynamic_txpower(struct ieee80211_hw *hw); +void rtl8723_dm_init_edca_turbo(struct ieee80211_hw *hw); +void rtl8723_dm_init_dynamic_bb_powersaving(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c new file mode 100644 index 00000000000..540278ff462 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.c @@ -0,0 +1,329 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "../base.h" +#include "fw_common.h" +#include <linux/module.h> + +void rtl8723_enable_fw_download(struct ieee80211_hw *hw, bool enable) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmp; + + if (enable) { + tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04); + + tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); + rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01); + + tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2); + rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7); + } else { + tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); + rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe); + + rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00); + } +} +EXPORT_SYMBOL_GPL(rtl8723_enable_fw_download); + +void rtl8723_fw_block_write(struct ieee80211_hw *hw, + const u8 *buffer, u32 size) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 blocksize = sizeof(u32); + u8 *bufferptr = (u8 *)buffer; + u32 *pu4byteptr = (u32 *)buffer; + u32 i, offset, blockcount, remainsize; + + blockcount = size / blocksize; + remainsize = size % blocksize; + + for (i = 0; i < blockcount; i++) { + offset = i * blocksize; + rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset), + *(pu4byteptr + i)); + } + if (remainsize) { + offset = blockcount * blocksize; + bufferptr += offset; + for (i = 0; i < remainsize; i++) { + rtl_write_byte(rtlpriv, + (FW_8192C_START_ADDRESS + offset + i), + *(bufferptr + i)); + } + } +} +EXPORT_SYMBOL_GPL(rtl8723_fw_block_write); + +void rtl8723_fw_page_write(struct ieee80211_hw *hw, + u32 page, const u8 *buffer, u32 size) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 value8; + u8 u8page = (u8) (page & 0x07); + + value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page; + + rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8); + rtl8723_fw_block_write(hw, buffer, size); +} +EXPORT_SYMBOL_GPL(rtl8723_fw_page_write); + +static void rtl8723_fill_dummy(u8 *pfwbuf, u32 *pfwlen) +{ + u32 fwlen = *pfwlen; + u8 remain = (u8) (fwlen % 4); + + remain = (remain == 0) ? 0 : (4 - remain); + + while (remain > 0) { + pfwbuf[fwlen] = 0; + fwlen++; + remain--; + } + *pfwlen = fwlen; +} + +void rtl8723_write_fw(struct ieee80211_hw *hw, + enum version_8723e version, + u8 *buffer, u32 size) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 *bufferptr = buffer; + u32 pagenums, remainsize; + u32 page, offset; + + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "FW size is %d bytes,\n", size); + + rtl8723_fill_dummy(bufferptr, &size); + + pagenums = size / FW_8192C_PAGE_SIZE; + remainsize = size % FW_8192C_PAGE_SIZE; + + if (pagenums > 8) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Page numbers should not greater then 8\n"); + } + for (page = 0; page < pagenums; page++) { + offset = page * FW_8192C_PAGE_SIZE; + rtl8723_fw_page_write(hw, page, (bufferptr + offset), + FW_8192C_PAGE_SIZE); + } + if (remainsize) { + offset = pagenums * FW_8192C_PAGE_SIZE; + page = pagenums; + rtl8723_fw_page_write(hw, page, (bufferptr + offset), + remainsize); + } +} +EXPORT_SYMBOL_GPL(rtl8723_write_fw); + +void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw) +{ + u8 u1tmp; + u8 delay = 100; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20); + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + + while (u1tmp & BIT(2)) { + delay--; + if (delay == 0) + break; + udelay(50); + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + } + if (delay == 0) { + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, u1tmp&(~BIT(2))); + } +} +EXPORT_SYMBOL_GPL(rtl8723ae_firmware_selfreset); + +void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw) +{ + u8 u1b_tmp; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); + rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp & (~BIT(0)))); + + u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp & (~BIT(2)))); + udelay(50); + + u1b_tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); + rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1b_tmp | BIT(0))); + + u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, (u1b_tmp | BIT(2))); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + " _8051Reset8723be(): 8051 reset success .\n"); +} +EXPORT_SYMBOL_GPL(rtl8723be_firmware_selfreset); + +int rtl8723_fw_free_to_go(struct ieee80211_hw *hw, bool is_8723be) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + int err = -EIO; + u32 counter = 0; + u32 value32; + + do { + value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); + } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) && + (!(value32 & FWDL_CHKSUM_RPT))); + + if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "chksum report fail ! REG_MCUFWDL:0x%08x .\n", + value32); + goto exit; + } + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32); + + value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL) | MCUFWDL_RDY; + value32 &= ~WINTINI_RDY; + rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); + + if (is_8723be) + rtl8723be_firmware_selfreset(hw); + counter = 0; + + do { + value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); + if (value32 & WINTINI_RDY) { + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "Polling FW ready success!! " + "REG_MCUFWDL:0x%08x .\n", + value32); + err = 0; + goto exit; + } + udelay(FW_8192C_POLLING_DELAY); + + } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT); + + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", + value32); + +exit: + return err; +} +EXPORT_SYMBOL_GPL(rtl8723_fw_free_to_go); + +int rtl8723_download_fw(struct ieee80211_hw *hw, + bool is_8723be) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl92c_firmware_header *pfwheader; + u8 *pfwdata; + u32 fwsize; + int err; + enum version_8723e version = rtlhal->version; + + if (!rtlhal->pfirmware) + return 1; + + pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware; + pfwdata = rtlhal->pfirmware; + fwsize = rtlhal->fwsize; + RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, + "normal Firmware SIZE %d\n", fwsize); + + if (rtlpriv->cfg->ops->is_fw_header(pfwheader)) { + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, + "Firmware Version(%d), Signature(%#x), Size(%d)\n", + pfwheader->version, pfwheader->signature, + (int)sizeof(struct rtl92c_firmware_header)); + + pfwdata = pfwdata + sizeof(struct rtl92c_firmware_header); + fwsize = fwsize - sizeof(struct rtl92c_firmware_header); + } + if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) { + rtl_write_byte(rtlpriv, REG_MCUFWDL, 0); + if (is_8723be) + rtl8723be_firmware_selfreset(hw); + else + rtl8723ae_firmware_selfreset(hw); + } + rtl8723_enable_fw_download(hw, true); + rtl8723_write_fw(hw, version, pfwdata, fwsize); + rtl8723_enable_fw_download(hw, false); + + err = rtl8723_fw_free_to_go(hw, is_8723be); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Firmware is not ready to run!\n"); + } else { + RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, + "Firmware is ready to run!\n"); + } + return 0; +} +EXPORT_SYMBOL_GPL(rtl8723_download_fw); + +bool rtl8723_cmd_send_packet(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring; + struct rtl_tx_desc *pdesc; + struct sk_buff *pskb = NULL; + u8 own; + unsigned long flags; + + ring = &rtlpci->tx_ring[BEACON_QUEUE]; + + pskb = __skb_dequeue(&ring->queue); + if (pskb) + kfree_skb(pskb); + + spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); + + pdesc = &ring->desc[0]; + own = (u8) rtlpriv->cfg->ops->get_desc((u8 *)pdesc, true, HW_DESC_OWN); + + rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb); + + __skb_queue_tail(&ring->queue, skb); + + spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); + + rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE); + + return true; +} +EXPORT_SYMBOL_GPL(rtl8723_cmd_send_packet); diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h new file mode 100644 index 00000000000..cf1cc5804d0 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/fw_common.h @@ -0,0 +1,126 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __FW_COMMON_H__ +#define __FW_COMMON_H__ + +#define REG_SYS_FUNC_EN 0x0002 +#define REG_MCUFWDL 0x0080 +#define FW_8192C_START_ADDRESS 0x1000 +#define FW_8192C_PAGE_SIZE 4096 +#define FW_8192C_POLLING_TIMEOUT_COUNT 6000 +#define FW_8192C_POLLING_DELAY 5 + +#define MCUFWDL_RDY BIT(1) +#define FWDL_CHKSUM_RPT BIT(2) +#define WINTINI_RDY BIT(6) + +#define REG_RSV_CTRL 0x001C +#define REG_HMETFR 0x01CC + +enum version_8723e { + VERSION_TEST_UMC_CHIP_8723 = 0x0081, + VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089, + VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089, + VERSION_TEST_CHIP_1T1R_8723B = 0x0106, + VERSION_NORMAL_SMIC_CHIP_1T1R_8723B = 0x010E, + VERSION_UNKNOWN = 0xFF, +}; + +enum rtl8723ae_h2c_cmd { + H2C_AP_OFFLOAD = 0, + H2C_SETPWRMODE = 1, + H2C_JOINBSSRPT = 2, + H2C_RSVDPAGE = 3, + H2C_RSSI_REPORT = 4, + H2C_P2P_PS_CTW_CMD = 5, + H2C_P2P_PS_OFFLOAD = 6, + H2C_RA_MASK = 7, + MAX_H2CCMD +}; + +enum rtl8723be_cmd { + H2C_8723BE_RSVDPAGE = 0, + H2C_8723BE_JOINBSSRPT = 1, + H2C_8723BE_SCAN = 2, + H2C_8723BE_KEEP_ALIVE_CTRL = 3, + H2C_8723BE_DISCONNECT_DECISION = 4, + H2C_8723BE_INIT_OFFLOAD = 6, + H2C_8723BE_AP_OFFLOAD = 8, + H2C_8723BE_BCN_RSVDPAGE = 9, + H2C_8723BE_PROBERSP_RSVDPAGE = 10, + + H2C_8723BE_SETPWRMODE = 0x20, + H2C_8723BE_PS_TUNING_PARA = 0x21, + H2C_8723BE_PS_TUNING_PARA2 = 0x22, + H2C_8723BE_PS_LPS_PARA = 0x23, + H2C_8723BE_P2P_PS_OFFLOAD = 0x24, + + H2C_8723BE_WO_WLAN = 0x80, + H2C_8723BE_REMOTE_WAKE_CTRL = 0x81, + H2C_8723BE_AOAC_GLOBAL_INFO = 0x82, + H2C_8723BE_AOAC_RSVDPAGE = 0x83, + H2C_8723BE_RSSI_REPORT = 0x42, + H2C_8723BE_RA_MASK = 0x40, + H2C_8723BE_SELECTIVE_SUSPEND_ROF_CMD, + H2C_8723BE_P2P_PS_MODE, + H2C_8723BE_PSD_RESULT, + /*Not defined CTW CMD for P2P yet*/ + H2C_8723BE_P2P_PS_CTW_CMD, + MAX_8723BE_H2CCMD +}; + +struct rtl92c_firmware_header { + u16 signature; + u8 category; + u8 function; + u16 version; + u8 subversion; + u8 rsvd1; + u8 month; + u8 date; + u8 hour; + u8 minute; + u16 ramcodesize; + u16 rsvd2; + u32 svnindex; + u32 rsvd3; + u32 rsvd4; + u32 rsvd5; +}; + +void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw); +void rtl8723be_firmware_selfreset(struct ieee80211_hw *hw); +void rtl8723_enable_fw_download(struct ieee80211_hw *hw, bool enable); +void rtl8723_fw_block_write(struct ieee80211_hw *hw, + const u8 *buffer, u32 size); +void rtl8723_fw_page_write(struct ieee80211_hw *hw, + u32 page, const u8 *buffer, u32 size); +void rtl8723_write_fw(struct ieee80211_hw *hw, + enum version_8723e version, + u8 *buffer, u32 size); +int rtl8723_fw_free_to_go(struct ieee80211_hw *hw, bool is_8723be); +int rtl8723_download_fw(struct ieee80211_hw *hw, bool is_8723be); +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/main.c b/drivers/net/wireless/rtlwifi/rtl8723com/main.c new file mode 100644 index 00000000000..9014a94fac6 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/main.c @@ -0,0 +1,33 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include <linux/module.h> + + +MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek RTL8723AE/RTL8723BE 802.11n PCI wireless common routines"); diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c new file mode 100644 index 00000000000..d73b659bd2b --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.c @@ -0,0 +1,434 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "phy_common.h" +#include "../rtl8723ae/reg.h" +#include <linux/module.h> + +/* These routines are common to RTL8723AE and RTL8723bE */ + +u32 rtl8723_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 returnvalue, originalvalue, bitshift; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); + originalvalue = rtl_read_dword(rtlpriv, regaddr); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); + returnvalue = (originalvalue & bitmask) >> bitshift; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "BBR MASK = 0x%x Addr[0x%x]= 0x%x\n", + bitmask, regaddr, originalvalue); + + return returnvalue; +} +EXPORT_SYMBOL_GPL(rtl8723_phy_query_bb_reg); + +void rtl8723_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, + u32 bitmask, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 originalvalue, bitshift; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x)\n", + regaddr, bitmask, data); + + if (bitmask != MASKDWORD) { + originalvalue = rtl_read_dword(rtlpriv, regaddr); + bitshift = rtl8723_phy_calculate_bit_shift(bitmask); + data = ((originalvalue & (~bitmask)) | (data << bitshift)); + } + + rtl_write_dword(rtlpriv, regaddr, data); + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x)\n", + regaddr, bitmask, data); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_set_bb_reg); + +u32 rtl8723_phy_calculate_bit_shift(u32 bitmask) +{ + u32 i; + + for (i = 0; i <= 31; i++) { + if (((bitmask >> i) & 0x1) == 1) + break; + } + return i; +} +EXPORT_SYMBOL_GPL(rtl8723_phy_calculate_bit_shift); + +u32 rtl8723_phy_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; + u32 newoffset; + u32 tmplong, tmplong2; + u8 rfpi_enable = 0; + u32 retvalue; + + offset &= 0xff; + newoffset = offset; + if (RT_CANNOT_IO(hw)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n"); + return 0xFFFFFFFF; + } + tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD); + if (rfpath == RF90_PATH_A) + tmplong2 = tmplong; + else + tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD); + tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) | + (newoffset << 23) | BLSSIREADEDGE; + rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, + tmplong & (~BLSSIREADEDGE)); + mdelay(1); + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2); + mdelay(2); + if (rfpath == RF90_PATH_A) + rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, + BIT(8)); + else if (rfpath == RF90_PATH_B) + rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1, + BIT(8)); + if (rfpi_enable) + retvalue = rtl_get_bbreg(hw, pphyreg->rf_rbpi, + BLSSIREADBACKDATA); + else + retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb, + BLSSIREADBACKDATA); + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "RFR-%d Addr[0x%x]= 0x%x\n", + rfpath, pphyreg->rf_rb, retvalue); + return retvalue; +} +EXPORT_SYMBOL_GPL(rtl8723_phy_rf_serial_read); + +void rtl8723_phy_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 offset, u32 data) +{ + u32 data_and_addr; + u32 newoffset; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; + + if (RT_CANNOT_IO(hw)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n"); + return; + } + offset &= 0xff; + newoffset = offset; + data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; + rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr); + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "RFW-%d Addr[0x%x]= 0x%x\n", rfpath, + pphyreg->rf3wire_offset, data_and_addr); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_rf_serial_write); + +long rtl8723_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + u8 txpwridx) +{ + long offset; + long pwrout_dbm; + + switch (wirelessmode) { + case WIRELESS_MODE_B: + offset = -7; + break; + case WIRELESS_MODE_G: + case WIRELESS_MODE_N_24G: + default: + offset = -8; + break; + } + pwrout_dbm = txpwridx / 2 + offset; + return pwrout_dbm; +} +EXPORT_SYMBOL_GPL(rtl8723_phy_txpwr_idx_to_dbm); + +void rtl8723_phy_init_bb_rf_reg_def(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW; + rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW; + rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW; + rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW; + + rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB; + rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB; + rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB; + rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB; + + rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE; + rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE; + + rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE; + rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE; + + rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = + RFPGA0_XA_LSSIPARAMETER; + rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = + RFPGA0_XB_LSSIPARAMETER; + + rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = rFPGA0_XAB_RFPARAMETER; + rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = rFPGA0_XAB_RFPARAMETER; + rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER; + rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER; + + rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE; + rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE; + rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE; + rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE; + + rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1; + rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1; + + rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2; + rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2; + + rtlphy->phyreg_def[RF90_PATH_A].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL; + rtlphy->phyreg_def[RF90_PATH_B].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL; + rtlphy->phyreg_def[RF90_PATH_C].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL; + rtlphy->phyreg_def[RF90_PATH_D].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL; + + rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1; + rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1; + rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1; + rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1; + + rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2; + rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2; + rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2; + rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2; + + rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbal = ROFDM0_XARXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbal = ROFDM0_XBRXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbal = ROFDM0_XCRXIQIMBANLANCE; + rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbal = ROFDM0_XDRXIQIMBALANCE; + + rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE; + rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE; + rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE; + rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE; + + rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbal = ROFDM0_XATXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbal = ROFDM0_XBTXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbal = ROFDM0_XCTXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbal = ROFDM0_XDTXIQIMBALANCE; + + rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE; + rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE; + rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE; + rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE; + + rtlphy->phyreg_def[RF90_PATH_A].rf_rb = RFPGA0_XA_LSSIREADBACK; + rtlphy->phyreg_def[RF90_PATH_B].rf_rb = RFPGA0_XB_LSSIREADBACK; + rtlphy->phyreg_def[RF90_PATH_C].rf_rb = RFPGA0_XC_LSSIREADBACK; + rtlphy->phyreg_def[RF90_PATH_D].rf_rb = RFPGA0_XD_LSSIREADBACK; + + rtlphy->phyreg_def[RF90_PATH_A].rf_rbpi = TRANSCEIVEA_HSPI_READBACK; + rtlphy->phyreg_def[RF90_PATH_B].rf_rbpi = TRANSCEIVEB_HSPI_READBACK; +} +EXPORT_SYMBOL_GPL(rtl8723_phy_init_bb_rf_reg_def); + +bool rtl8723_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, + u32 cmdtableidx, + u32 cmdtablesz, + enum swchnlcmd_id cmdid, + u32 para1, u32 para2, + u32 msdelay) +{ + struct swchnlcmd *pcmd; + + if (cmdtable == NULL) { + RT_ASSERT(false, "cmdtable cannot be NULL.\n"); + return false; + } + + if (cmdtableidx >= cmdtablesz) + return false; + + pcmd = cmdtable + cmdtableidx; + pcmd->cmdid = cmdid; + pcmd->para1 = para1; + pcmd->para2 = para2; + pcmd->msdelay = msdelay; + return true; +} +EXPORT_SYMBOL_GPL(rtl8723_phy_set_sw_chnl_cmdarray); + +void rtl8723_phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw, + bool iqk_ok, + long result[][8], + u8 final_candidate, + bool btxonly) +{ + u32 oldval_0, x, tx0_a, reg; + long y, tx0_c; + + if (final_candidate == 0xFF) { + return; + } else if (iqk_ok) { + oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE, + MASKDWORD) >> 22) & 0x3FF; + x = result[final_candidate][0]; + if ((x & 0x00000200) != 0) + x = x | 0xFFFFFC00; + tx0_a = (x * oldval_0) >> 8; + rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x3FF, tx0_a); + rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31), + ((x * oldval_0 >> 7) & 0x1)); + y = result[final_candidate][1]; + if ((y & 0x00000200) != 0) + y = y | 0xFFFFFC00; + tx0_c = (y * oldval_0) >> 8; + rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000, + ((tx0_c & 0x3C0) >> 6)); + rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x003F0000, + (tx0_c & 0x3F)); + rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29), + ((y * oldval_0 >> 7) & 0x1)); + if (btxonly) + return; + reg = result[final_candidate][2]; + rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg); + reg = result[final_candidate][3] & 0x3F; + rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg); + reg = (result[final_candidate][3] >> 6) & 0xF; + rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg); + } +} +EXPORT_SYMBOL_GPL(rtl8723_phy_path_a_fill_iqk_matrix); + +void rtl8723_save_adda_registers(struct ieee80211_hw *hw, u32 *addareg, + u32 *addabackup, u32 registernum) +{ + u32 i; + + for (i = 0; i < registernum; i++) + addabackup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD); +} +EXPORT_SYMBOL_GPL(rtl8723_save_adda_registers); + +void rtl8723_phy_save_mac_registers(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i; + + for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) + macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]); + macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_save_mac_registers); + +void rtl8723_phy_reload_adda_registers(struct ieee80211_hw *hw, + u32 *addareg, u32 *addabackup, + u32 regiesternum) +{ + u32 i; + + for (i = 0; i < regiesternum; i++) + rtl_set_bbreg(hw, addareg[i], MASKDWORD, addabackup[i]); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_reload_adda_registers); + +void rtl8723_phy_reload_mac_registers(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i; + + for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) + rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]); + rtl_write_dword(rtlpriv, macreg[i], macbackup[i]); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_reload_mac_registers); + +void rtl8723_phy_path_adda_on(struct ieee80211_hw *hw, u32 *addareg, + bool is_patha_on, bool is2t) +{ + u32 pathon; + u32 i; + + pathon = is_patha_on ? 0x04db25a4 : 0x0b1b25a4; + if (!is2t) { + pathon = 0x0bdb25a0; + rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0); + } else { + rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathon); + } + + for (i = 1; i < IQK_ADDA_REG_NUM; i++) + rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathon); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_path_adda_on); + +void rtl8723_phy_mac_setting_calibration(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i = 0; + + rtl_write_byte(rtlpriv, macreg[i], 0x3F); + + for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++) + rtl_write_byte(rtlpriv, macreg[i], + (u8) (macbackup[i] & (~BIT(3)))); + rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5)))); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_mac_setting_calibration); + +void rtl8723_phy_path_a_standby(struct ieee80211_hw *hw) +{ + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0); + rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_path_a_standby); + +void rtl8723_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode) +{ + u32 mode; + + mode = pi_mode ? 0x01000100 : 0x01000000; + rtl_set_bbreg(hw, 0x820, MASKDWORD, mode); + rtl_set_bbreg(hw, 0x828, MASKDWORD, mode); +} +EXPORT_SYMBOL_GPL(rtl8723_phy_pi_mode_switch); diff --git a/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h new file mode 100644 index 00000000000..83b891a9adb --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723com/phy_common.h @@ -0,0 +1,89 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2014 Realtek Corporation. + * + * 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. + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __PHY_COMMON__ +#define __PHY_COMMON__ + +#define RT_CANNOT_IO(hw) false + +enum swchnlcmd_id { + CMDID_END, + CMDID_SET_TXPOWEROWER_LEVEL, + CMDID_BBREGWRITE10, + CMDID_WRITEPORT_ULONG, + CMDID_WRITEPORT_USHORT, + CMDID_WRITEPORT_UCHAR, + CMDID_RF_WRITEREG, +}; + +struct swchnlcmd { + enum swchnlcmd_id cmdid; + u32 para1; + u32 para2; + u32 msdelay; +}; + +u32 rtl8723_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +void rtl8723_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, + u32 bitmask, u32 data); +u32 rtl8723_phy_calculate_bit_shift(u32 bitmask); +u32 rtl8723_phy_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset); +void rtl8723_phy_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 offset, u32 data); +long rtl8723_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + u8 txpwridx); +void rtl8723_phy_init_bb_rf_reg_def(struct ieee80211_hw *hw); +bool rtl8723_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, + u32 cmdtableidx, + u32 cmdtablesz, + enum swchnlcmd_id cmdid, + u32 para1, u32 para2, + u32 msdelay); +void rtl8723_phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw, + bool iqk_ok, + long result[][8], + u8 final_candidate, + bool btxonly); +void rtl8723_save_adda_registers(struct ieee80211_hw *hw, u32 *addareg, + u32 *addabackup, u32 registernum); +void rtl8723_phy_save_mac_registers(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup); +void rtl8723_phy_reload_adda_registers(struct ieee80211_hw *hw, + u32 *addareg, u32 *addabackup, + u32 regiesternum); +void rtl8723_phy_reload_mac_registers(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup); +void rtl8723_phy_path_adda_on(struct ieee80211_hw *hw, u32 *addareg, + bool is_patha_on, bool is2t); +void rtl8723_phy_mac_setting_calibration(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup); +void rtl8723_phy_path_a_standby(struct ieee80211_hw *hw); +void rtl8723_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode); + +#endif diff --git a/drivers/net/wireless/rtlwifi/stats.c b/drivers/net/wireless/rtlwifi/stats.c index 8ed31744a05..4f083fc1d36 100644 --- a/drivers/net/wireless/rtlwifi/stats.c +++ b/drivers/net/wireless/rtlwifi/stats.c @@ -176,6 +176,7 @@ static void rtl_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstatus) struct rtl_sta_info *drv_priv = NULL; struct ieee80211_sta *sta = NULL; long undec_sm_pwdb; + long undec_sm_cck; rcu_read_lock(); if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION) @@ -185,12 +186,16 @@ static void rtl_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstatus) if (sta) { drv_priv = (struct rtl_sta_info *) sta->drv_priv; undec_sm_pwdb = drv_priv->rssi_stat.undec_sm_pwdb; + undec_sm_cck = drv_priv->rssi_stat.undec_sm_cck; } else { undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; + undec_sm_cck = rtlpriv->dm.undec_sm_cck; } if (undec_sm_pwdb < 0) undec_sm_pwdb = pstatus->rx_pwdb_all; + if (undec_sm_cck < 0) + undec_sm_cck = pstatus->rx_pwdb_all; if (pstatus->rx_pwdb_all > (u32) undec_sm_pwdb) { undec_sm_pwdb = (((undec_sm_pwdb) * (RX_SMOOTH_FACTOR - 1)) + @@ -200,6 +205,15 @@ static void rtl_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstatus) undec_sm_pwdb = (((undec_sm_pwdb) * (RX_SMOOTH_FACTOR - 1)) + (pstatus->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); } + if (pstatus->rx_pwdb_all > (u32) undec_sm_cck) { + undec_sm_cck = (((undec_sm_pwdb) * + (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); + undec_sm_cck = undec_sm_cck + 1; + } else { + undec_sm_pwdb = (((undec_sm_cck) * (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); + } if (sta) { drv_priv->rssi_stat.undec_sm_pwdb = undec_sm_pwdb; diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index e56778cac9b..0398d3ea15b 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -410,7 +410,7 @@ static void rtl_usb_init_sw(struct ieee80211_hw *hw) mac->current_ampdu_factor = 3; /* QOS */ - rtlusb->acm_method = eAcmWay2_SW; + rtlusb->acm_method = EACMWAY2_SW; /* IRQ */ /* HIMR - turn all on */ @@ -455,7 +455,6 @@ static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; @@ -476,14 +475,14 @@ static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, rtlpriv->stats.rxbytesunicast += skb->len; } - rtl_is_special_data(hw, skb, false); - if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); if (unicast) rtlpriv->link_info.num_rx_inperiod++; } + /* static bcn for roaming */ + rtl_beacon_statistic(hw, skb); } } @@ -498,7 +497,6 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; @@ -519,8 +517,6 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, rtlpriv->stats.rxbytesunicast += skb->len; } - rtl_is_special_data(hw, skb, false); - if (ieee80211_is_data(fc)) { rtlpriv->cfg->ops->led_control(hw, LED_CTL_RX); @@ -555,7 +551,7 @@ static void _rtl_rx_pre_process(struct ieee80211_hw *hw, struct sk_buff *skb) } } -#define __RX_SKB_MAX_QUEUED 32 +#define __RX_SKB_MAX_QUEUED 64 static void _rtl_rx_work(unsigned long param) { @@ -582,12 +578,15 @@ static void _rtl_rx_work(unsigned long param) static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr, unsigned int len) { +#if NET_IP_ALIGN != 0 unsigned int padding = 0; +#endif /* make function no-op when possible */ if (NET_IP_ALIGN == 0 || len < sizeof(*hdr)) return 0; +#if NET_IP_ALIGN != 0 /* alignment calculation as in lbtf_rx() / carl9170_rx_copy_data() */ /* TODO: deduplicate common code, define helper function instead? */ @@ -608,6 +607,7 @@ static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr, padding ^= NET_IP_ALIGN; return padding; +#endif } #define __RADIO_TAP_SIZE_RSV 32 @@ -994,7 +994,7 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, seq_number += 1; seq_number <<= 4; } - rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, info, sta, skb, + rtlpriv->cfg->ops->fill_tx_desc(hw, hdr, (u8 *)pdesc, NULL, info, sta, skb, hw_queue, &tcb_desc); if (!ieee80211_has_morefrags(hdr->frame_control)) { if (qc) diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 703258742d2..407a7936d36 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -41,6 +41,38 @@ #include <linux/completion.h> #include "debug.h" +#define MASKBYTE0 0xff +#define MASKBYTE1 0xff00 +#define MASKBYTE2 0xff0000 +#define MASKBYTE3 0xff000000 +#define MASKHWORD 0xffff0000 +#define MASKLWORD 0x0000ffff +#define MASKDWORD 0xffffffff +#define MASK12BITS 0xfff +#define MASKH4BITS 0xf0000000 +#define MASKOFDM_D 0xffc00000 +#define MASKCCK 0x3f3f3f3f + +#define MASK4BITS 0x0f +#define MASK20BITS 0xfffff +#define RFREG_OFFSET_MASK 0xfffff + +#define MASKBYTE0 0xff +#define MASKBYTE1 0xff00 +#define MASKBYTE2 0xff0000 +#define MASKBYTE3 0xff000000 +#define MASKHWORD 0xffff0000 +#define MASKLWORD 0x0000ffff +#define MASKDWORD 0xffffffff +#define MASK12BITS 0xfff +#define MASKH4BITS 0xf0000000 +#define MASKOFDM_D 0xffc00000 +#define MASKCCK 0x3f3f3f3f + +#define MASK4BITS 0x0f +#define MASK20BITS 0xfffff +#define RFREG_OFFSET_MASK 0xfffff + #define RF_CHANGE_BY_INIT 0 #define RF_CHANGE_BY_IPS BIT(28) #define RF_CHANGE_BY_PS BIT(29) @@ -49,6 +81,7 @@ #define IQK_ADDA_REG_NUM 16 #define IQK_MAC_REG_NUM 4 +#define IQK_THRESHOLD 8 #define MAX_KEY_LEN 61 #define KEY_BUF_SIZE 5 @@ -77,11 +110,7 @@ #define RTL_SLOT_TIME_9 9 #define RTL_SLOT_TIME_20 20 -/*related with tcp/ip. */ -/*if_ehther.h*/ -#define ETH_P_PAE 0x888E /*Port Access Entity (IEEE 802.1X) */ -#define ETH_P_IP 0x0800 /*Internet Protocol packet */ -#define ETH_P_ARP 0x0806 /*Address Resolution packet */ +/*related to tcp/ip. */ #define SNAP_SIZE 6 #define PROTOC_TYPE_SIZE 2 @@ -90,7 +119,18 @@ #define MAC80211_4ADDR_LEN 30 #define CHANNEL_MAX_NUMBER (14 + 24 + 21) /* 14 is the max channel no */ +#define CHANNEL_MAX_NUMBER_2G 14 +#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to + *"phy_GetChnlGroup8812A" and + * "Hal_ReadTxPowerInfo8812A" + */ +#define CHANNEL_MAX_NUMBER_5G_80M 7 #define CHANNEL_GROUP_MAX (3 + 9) /* ch1~3, 4~9, 10~14 = three groups */ +#define CHANNEL_MAX_NUMBER_5G 54 /* Please refer to + *"phy_GetChnlGroup8812A" and + * "Hal_ReadTxPowerInfo8812A" + */ +#define CHANNEL_MAX_NUMBER_5G_80M 7 #define MAX_PG_GROUP 13 #define CHANNEL_GROUP_MAX_2G 3 #define CHANNEL_GROUP_IDX_5GL 3 @@ -100,6 +140,7 @@ #define CHANNEL_MAX_NUMBER_2G 14 #define AVG_THERMAL_NUM 8 #define AVG_THERMAL_NUM_88E 4 +#define AVG_THERMAL_NUM_8723BE 4 #define MAX_TID_COUNT 9 /* for early mode */ @@ -111,6 +152,24 @@ #define MAX_CHNL_GROUP_24G 6 #define MAX_CHNL_GROUP_5G 14 +#define TX_PWR_BY_RATE_NUM_BAND 2 +#define TX_PWR_BY_RATE_NUM_RF 4 +#define TX_PWR_BY_RATE_NUM_SECTION 12 +#define MAX_BASE_NUM_IN_PHY_REG_PG_24G 6 +#define MAX_BASE_NUM_IN_PHY_REG_PG_5G 5 + +#define RTL8192EE_SEG_NUM 1 /* 0:2 seg, 1: 4 seg, 2: 8 seg */ + +#define DEL_SW_IDX_SZ 30 +#define BAND_NUM 3 + +enum rf_tx_num { + RF_1TX = 0, + RF_2TX, + RF_MAX_TX_NUM, + RF_TX_NUM_NONIMPLEMENT, +}; + struct txpower_info_2g { u8 index_cck_base[MAX_RF_PATH][MAX_CHNL_GROUP_24G]; u8 index_bw40_base[MAX_RF_PATH][MAX_CHNL_GROUP_24G]; @@ -119,6 +178,8 @@ struct txpower_info_2g { u8 ofdm_diff[MAX_RF_PATH][MAX_TX_COUNT]; u8 bw20_diff[MAX_RF_PATH][MAX_TX_COUNT]; u8 bw40_diff[MAX_RF_PATH][MAX_TX_COUNT]; + u8 bw80_diff[MAX_RF_PATH][MAX_TX_COUNT]; + u8 bw160_diff[MAX_RF_PATH][MAX_TX_COUNT]; }; struct txpower_info_5g { @@ -127,6 +188,17 @@ struct txpower_info_5g { u8 ofdm_diff[MAX_RF_PATH][MAX_TX_COUNT]; u8 bw20_diff[MAX_RF_PATH][MAX_TX_COUNT]; u8 bw40_diff[MAX_RF_PATH][MAX_TX_COUNT]; + u8 bw80_diff[MAX_RF_PATH][MAX_TX_COUNT]; + u8 bw160_diff[MAX_RF_PATH][MAX_TX_COUNT]; +}; + +enum rate_section { + CCK = 0, + OFDM, + HT_MCS0_MCS7, + HT_MCS8_MCS15, + VHT_1SSMCS0_1SSMCS9, + VHT_2SSMCS0_2SSMCS9, }; enum intf_type { @@ -162,7 +234,10 @@ enum hardware_type { HARDWARE_TYPE_RTL8192DU, HARDWARE_TYPE_RTL8723AE, HARDWARE_TYPE_RTL8723U, + HARDWARE_TYPE_RTL8723BE, HARDWARE_TYPE_RTL8188EE, + HARDWARE_TYPE_RTL8821AE, + HARDWARE_TYPE_RTL8812AE, /* keep it last */ HARDWARE_TYPE_NUM @@ -192,8 +267,6 @@ enum hardware_type { (IS_HARDWARE_TYPE_8192DE(rtlhal) || IS_HARDWARE_TYPE_8192DU(rtlhal)) #define IS_HARDWARE_TYPE_8723(rtlhal) \ (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal)) -#define IS_HARDWARE_TYPE_8723U(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U) #define RX_HAL_IS_CCK_RATE(_pdesc)\ (_pdesc->rxmcs == DESC92_RATE1M || \ @@ -201,8 +274,16 @@ enum hardware_type { _pdesc->rxmcs == DESC92_RATE5_5M || \ _pdesc->rxmcs == DESC92_RATE11M) +#define RTL8723E_RX_HAL_IS_CCK_RATE(rxmcs) \ + ((rxmcs) == DESC92_RATE1M || \ + (rxmcs) == DESC92_RATE2M || \ + (rxmcs) == DESC92_RATE5_5M || \ + (rxmcs) == DESC92_RATE11M) + enum scan_operation_backup_opt { SCAN_OPT_BACKUP = 0, + SCAN_OPT_BACKUP_BAND0 = 0, + SCAN_OPT_BACKUP_BAND1, SCAN_OPT_RESTORE, SCAN_OPT_MAX }; @@ -237,7 +318,9 @@ struct bb_reg_def { enum io_type { IO_CMD_PAUSE_DM_BY_SCAN = 0, - IO_CMD_RESUME_DM_BY_SCAN = 1, + IO_CMD_PAUSE_BAND0_DM_BY_SCAN = 0, + IO_CMD_PAUSE_BAND1_DM_BY_SCAN = 1, + IO_CMD_RESUME_DM_BY_SCAN = 2, }; enum hw_variables { @@ -304,6 +387,7 @@ enum hw_variables { HW_VAR_SET_RPWM, HW_VAR_H2C_FW_PWRMODE, HW_VAR_H2C_FW_JOINBSSRPT, + HW_VAR_H2C_FW_MEDIASTATUSRPT, HW_VAR_H2C_FW_P2P_PS_OFFLOAD, HW_VAR_FW_PSMODE_STATUS, HW_VAR_RESUME_CLK_ON, @@ -336,6 +420,8 @@ enum hw_variables { HAL_DEF_WOWLAN, HW_VAR_MRC, + HW_VAR_KEEP_ALIVE, + HW_VAR_NAV_UPPER, HW_VAR_MGT_FILTER, HW_VAR_CTRL_FILTER, @@ -354,34 +440,34 @@ enum rt_oem_id { RT_CID_8187_HW_LED = 3, RT_CID_8187_NETGEAR = 4, RT_CID_WHQL = 5, - RT_CID_819x_CAMEO = 6, - RT_CID_819x_RUNTOP = 7, - RT_CID_819x_Senao = 8, + RT_CID_819X_CAMEO = 6, + RT_CID_819X_RUNTOP = 7, + RT_CID_819X_SENAO = 8, RT_CID_TOSHIBA = 9, - RT_CID_819x_Netcore = 10, - RT_CID_Nettronix = 11, + RT_CID_819X_NETCORE = 10, + RT_CID_NETTRONIX = 11, RT_CID_DLINK = 12, RT_CID_PRONET = 13, RT_CID_COREGA = 14, - RT_CID_819x_ALPHA = 15, - RT_CID_819x_Sitecom = 16, + RT_CID_819X_ALPHA = 15, + RT_CID_819X_SITECOM = 16, RT_CID_CCX = 17, - RT_CID_819x_Lenovo = 18, - RT_CID_819x_QMI = 19, - RT_CID_819x_Edimax_Belkin = 20, - RT_CID_819x_Sercomm_Belkin = 21, - RT_CID_819x_CAMEO1 = 22, - RT_CID_819x_MSI = 23, - RT_CID_819x_Acer = 24, - RT_CID_819x_HP = 27, - RT_CID_819x_CLEVO = 28, - RT_CID_819x_Arcadyan_Belkin = 29, - RT_CID_819x_SAMSUNG = 30, - RT_CID_819x_WNC_COREGA = 31, - RT_CID_819x_Foxcoon = 32, - RT_CID_819x_DELL = 33, - RT_CID_819x_PRONETS = 34, - RT_CID_819x_Edimax_ASUS = 35, + RT_CID_819X_LENOVO = 18, + RT_CID_819X_QMI = 19, + RT_CID_819X_EDIMAX_BELKIN = 20, + RT_CID_819X_SERCOMM_BELKIN = 21, + RT_CID_819X_CAMEO1 = 22, + RT_CID_819X_MSI = 23, + RT_CID_819X_ACER = 24, + RT_CID_819X_HP = 27, + RT_CID_819X_CLEVO = 28, + RT_CID_819X_ARCADYAN_BELKIN = 29, + RT_CID_819X_SAMSUNG = 30, + RT_CID_819X_WNC_COREGA = 31, + RT_CID_819X_FOXCOON = 32, + RT_CID_819X_DELL = 33, + RT_CID_819X_PRONETS = 34, + RT_CID_819X_EDIMAX_ASUS = 35, RT_CID_NETGEAR = 36, RT_CID_PLANEX = 37, RT_CID_CC_C = 38, @@ -395,6 +481,7 @@ enum hw_descs { HW_DESC_RXBUFF_ADDR, HW_DESC_RXPKT_LEN, HW_DESC_RXERO, + HW_DESC_RX_PREPARE, }; enum prime_sc { @@ -413,6 +500,7 @@ enum rf_type { enum ht_channel_width { HT_CHANNEL_WIDTH_20 = 0, HT_CHANNEL_WIDTH_20_40 = 1, + HT_CHANNEL_WIDTH_80 = 2, }; /* Ref: 802.11i sepc D10.0 7.3.2.25.1 @@ -477,6 +565,9 @@ enum rtl_var_map { MAC_RCR_ACRC32, MAC_RCR_ACF, MAC_RCR_AAP, + MAC_HIMR, + MAC_HIMRE, + MAC_HSISR, /*efuse map */ EFUSE_TEST, @@ -614,7 +705,7 @@ enum rtl_led_pin { enum acm_method { eAcmWay0_SwAndHw = 0, eAcmWay1_HW = 1, - eAcmWay2_SW = 2, + EACMWAY2_SW = 2, }; enum macphy_mode { @@ -651,7 +742,9 @@ enum wireless_mode { WIRELESS_MODE_G = 0x04, WIRELESS_MODE_AUTO = 0x08, WIRELESS_MODE_N_24G = 0x10, - WIRELESS_MODE_N_5G = 0x20 + WIRELESS_MODE_N_5G = 0x20, + WIRELESS_MODE_AC_5G = 0x40, + WIRELESS_MODE_AC_24G = 0x80 }; #define IS_WIRELESS_MODE_A(wirelessmode) \ @@ -675,6 +768,8 @@ enum ratr_table_mode { RATR_INX_WIRELESS_B = 6, RATR_INX_WIRELESS_MC = 7, RATR_INX_WIRELESS_A = 8, + RATR_INX_WIRELESS_AC_5N = 8, + RATR_INX_WIRELESS_AC_24N = 9, }; enum rtl_link_state { @@ -809,8 +904,12 @@ struct wireless_stats { long signal_strength; u8 rx_rssi_percentage[4]; + u8 rx_evm_dbm[4]; u8 rx_evm_percentage[2]; + u16 rx_cfo_short[4]; + u16 rx_cfo_tail[4]; + struct rt_smooth_data ui_rssi; struct rt_smooth_data ui_link_quality; }; @@ -823,9 +922,9 @@ struct rate_adaptive { u32 high_rssi_thresh_for_ra; u32 high2low_rssi_thresh_for_ra; u8 low2high_rssi_thresh_for_ra40m; - u32 low_rssi_thresh_for_ra40M; + u32 low_rssi_thresh_for_ra40m; u8 low2high_rssi_thresh_for_ra20m; - u32 low_rssi_thresh_for_ra20M; + u32 low_rssi_thresh_for_ra20m; u32 upper_rssi_threshold_ratr; u32 middleupper_rssi_threshold_ratr; u32 middle_rssi_threshold_ratr; @@ -839,6 +938,10 @@ struct rate_adaptive { u32 ping_rssi_thresh_for_ra; u32 last_ratr; u8 pre_ratr_state; + u8 ldpc_thres; + bool use_ldpc; + bool lower_rts_rate; + bool is_special_data; }; struct regd_pair_mapping { @@ -847,6 +950,16 @@ struct regd_pair_mapping { u16 reg_2ghz_ctl; }; +struct dynamic_primary_cca { + u8 pricca_flag; + u8 intf_flag; + u8 intf_type; + u8 dup_rts_flag; + u8 monitor_flag; + u8 ch_offset; + u8 mf_state; +}; + struct rtl_regulatory { char alpha2[2]; u16 country_code; @@ -982,16 +1095,29 @@ struct rtl_phy { u32 iqk_bb_backup[10]; bool iqk_initialized; + bool rfpath_rx_enable[MAX_RF_PATH]; + u8 reg_837; /* Dual mac */ bool need_iqk; struct iqk_matrix_regs iqk_matrix[IQK_MATRIX_SETTINGS_NUM]; bool rfpi_enable; + bool iqk_in_progress; u8 pwrgroup_cnt; u8 cck_high_power; /* MAX_PG_GROUP groups of pwr diff by rates */ u32 mcs_offset[MAX_PG_GROUP][16]; + u32 tx_power_by_rate_offset[TX_PWR_BY_RATE_NUM_BAND] + [TX_PWR_BY_RATE_NUM_RF] + [TX_PWR_BY_RATE_NUM_RF] + [TX_PWR_BY_RATE_NUM_SECTION]; + u8 txpwr_by_rate_base_24g[TX_PWR_BY_RATE_NUM_RF] + [TX_PWR_BY_RATE_NUM_RF] + [MAX_BASE_NUM_IN_PHY_REG_PG_24G]; + u8 txpwr_by_rate_base_5g[TX_PWR_BY_RATE_NUM_RF] + [TX_PWR_BY_RATE_NUM_RF] + [MAX_BASE_NUM_IN_PHY_REG_PG_5G]; u8 default_initialgain[4]; /* the current Tx power level */ @@ -1004,6 +1130,7 @@ struct rtl_phy { bool apk_done; u32 reg_rf3c[2]; /* pathA / pathB */ + u32 backup_rf_0x1a;/*92ee*/ /* bfsync */ u8 framesync; u32 framesync_c34; @@ -1012,6 +1139,7 @@ struct rtl_phy { struct phy_parameters hwparam_tables[MAX_TAB]; u16 rf_pathmap; + u8 hw_rof_enable; /*Enable GPIO[9] as WL RF HW PDn source*/ enum rt_polarity_ctl polarity_ctl; }; @@ -1039,6 +1167,7 @@ struct rtl_ht_agg { struct rssi_sta { long undec_sm_pwdb; + long undec_sm_cck; }; struct rtl_tid_data { @@ -1138,6 +1267,7 @@ struct rtl_mac { u8 use_cts_protect; u8 cur_40_prime_sc; u8 cur_40_prime_sc_bk; + u8 cur_80_prime_sc; u64 tsf; u8 retry_short; u8 retry_long; @@ -1218,6 +1348,7 @@ struct rtl_hal { bool being_init_adapter; bool bbrf_ready; bool mac_func_enable; + bool pre_edcca_enable; struct bt_coexist_8723 hal_coex_8723; enum intf_type interface; @@ -1239,6 +1370,7 @@ struct rtl_hal { /*Reserve page start offset except beacon in TxQ. */ u8 fw_rsvdpage_startoffset; u8 h2c_txcmd_seq; + u8 current_ra_rate; /* FW Cmd IO related */ u16 fwcmd_iomap; @@ -1278,6 +1410,9 @@ struct rtl_hal { bool disable_amsdu_8k; bool master_of_dmsp; bool slave_of_dmsp; + + u16 rx_tag;/*for 92ee*/ + u8 rts_en; }; struct rtl_security { @@ -1326,11 +1461,23 @@ struct fast_ant_training { bool becomelinked; }; +struct dm_phy_dbg_info { + char rx_snrdb[4]; + u64 num_qry_phy_status; + u64 num_qry_phy_status_cck; + u64 num_qry_phy_status_ofdm; + u16 num_qry_beacon_pkt; + u16 num_non_be_pkt; + s32 rx_evm[4]; +}; + struct rtl_dm { /*PHY status for Dynamic Management */ long entry_min_undec_sm_pwdb; + long undec_sm_cck; long undec_sm_pwdb; /*out dm */ long entry_max_undec_sm_pwdb; + s32 ofdm_pkt_cnt; bool dm_initialgain_enable; bool dynamic_txpower_enable; bool current_turbo_edca; @@ -1345,6 +1492,7 @@ struct rtl_dm { bool inform_fw_driverctrldm; bool current_mrc_switch; u8 txpowercount; + u8 powerindex_backup[6]; u8 thermalvalue_rxgain; u8 thermalvalue_iqk; @@ -1356,33 +1504,90 @@ struct rtl_dm { bool done_txpower; u8 dynamic_txhighpower_lvl; /*Tx high power level */ u8 dm_flag; /*Indicate each dynamic mechanism's status. */ + u8 dm_flag_tmp; u8 dm_type; + u8 dm_rssi_sel; u8 txpower_track_control; bool interrupt_migration; bool disable_tx_int; - char ofdm_index[2]; + char ofdm_index[MAX_RF_PATH]; + u8 default_ofdm_index; + u8 default_cck_index; char cck_index; - char delta_power_index; - char delta_power_index_last; - char power_index_offset; + char delta_power_index[MAX_RF_PATH]; + char delta_power_index_last[MAX_RF_PATH]; + char power_index_offset[MAX_RF_PATH]; + char absolute_ofdm_swing_idx[MAX_RF_PATH]; + char remnant_ofdm_swing_idx[MAX_RF_PATH]; + char remnant_cck_idx; + bool modify_txagc_flag_path_a; + bool modify_txagc_flag_path_b; + + bool one_entry_only; + struct dm_phy_dbg_info dbginfo; + + /* Dynamic ATC switch */ + bool atc_status; + bool large_cfo_hit; + bool is_freeze; + int cfo_tail[2]; + int cfo_ave_pre; + int crystal_cap; + u8 cfo_threshold; + u32 packet_count; + u32 packet_count_pre; + u8 tx_rate; /*88e tx power tracking*/ - u8 swing_idx_ofdm[2]; + u8 swing_idx_ofdm[MAX_RF_PATH]; u8 swing_idx_ofdm_cur; - u8 swing_idx_ofdm_base; + u8 swing_idx_ofdm_base[MAX_RF_PATH]; bool swing_flag_ofdm; u8 swing_idx_cck; u8 swing_idx_cck_cur; u8 swing_idx_cck_base; bool swing_flag_cck; + char swing_diff_2g; + char swing_diff_5g; + + u8 delta_swing_table_idx_24gccka_p[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24gccka_n[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24gcckb_p[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24gcckb_n[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24ga_p[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24ga_n[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24gb_p[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24gb_n[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_5ga_p[BAND_NUM][DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_5ga_n[BAND_NUM][DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_5gb_p[BAND_NUM][DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_5gb_n[BAND_NUM][DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24ga_p_8188e[DEL_SW_IDX_SZ]; + u8 delta_swing_table_idx_24ga_n_8188e[DEL_SW_IDX_SZ]; + /* DMSP */ bool supp_phymode_switch; + /* DulMac */ struct fast_ant_training fat_table; + + u8 resp_tx_path; + u8 path_sel; + u32 patha_sum; + u32 pathb_sum; + u32 patha_cnt; + u32 pathb_cnt; + + u8 pre_channel; + u8 *p_channel; + u8 linked_interval; + + u64 last_tx_ok_cnt; + u64 last_rx_ok_cnt; }; -#define EFUSE_MAX_LOGICAL_SIZE 256 +#define EFUSE_MAX_LOGICAL_SIZE 512 struct rtl_efuse { bool autoLoad_ok; @@ -1422,12 +1627,9 @@ struct rtl_efuse { u8 eeprom_tssi_5g[3][2]; /* for 5GL/5GM/5GH band. */ u8 eeprom_pwrlimit_ht20[CHANNEL_GROUP_MAX]; u8 eeprom_pwrlimit_ht40[CHANNEL_GROUP_MAX]; - u8 eeprom_chnlarea_txpwr_cck[2][CHANNEL_GROUP_MAX_2G]; - u8 eeprom_chnlarea_txpwr_ht40_1s[2][CHANNEL_GROUP_MAX]; - u8 eprom_chnl_txpwr_ht40_2sdf[2][CHANNEL_GROUP_MAX]; - u8 txpwrlevel_cck[2][CHANNEL_MAX_NUMBER_2G]; - u8 txpwrlevel_ht40_1s[2][CHANNEL_MAX_NUMBER]; /*For HT 40MHZ pwr */ - u8 txpwrlevel_ht40_2s[2][CHANNEL_MAX_NUMBER]; /*For HT 40MHZ pwr */ + u8 eeprom_chnlarea_txpwr_cck[MAX_RF_PATH][CHANNEL_GROUP_MAX_2G]; + u8 eeprom_chnlarea_txpwr_ht40_1s[MAX_RF_PATH][CHANNEL_GROUP_MAX]; + u8 eprom_chnl_txpwr_ht40_2sdf[MAX_RF_PATH][CHANNEL_GROUP_MAX]; u8 internal_pa_5g[2]; /* pathA / pathB */ u8 eeprom_c9; @@ -1438,9 +1640,38 @@ struct rtl_efuse { u8 pwrgroup_ht20[2][CHANNEL_MAX_NUMBER]; u8 pwrgroup_ht40[2][CHANNEL_MAX_NUMBER]; - char txpwr_ht20diff[2][CHANNEL_MAX_NUMBER]; /*HT 20<->40 Pwr diff */ - /*For HT<->legacy pwr diff*/ - u8 txpwr_legacyhtdiff[2][CHANNEL_MAX_NUMBER]; + u8 txpwrlevel_cck[MAX_RF_PATH][CHANNEL_MAX_NUMBER_2G]; + /*For HT 40MHZ pwr */ + u8 txpwrlevel_ht40_1s[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + /*For HT 40MHZ pwr */ + u8 txpwrlevel_ht40_2s[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + + /*--------------------------------------------------------* + * 8192CE\8192SE\8192DE\8723AE use the following 4 arrays, + * other ICs (8188EE\8723BE\8192EE\8812AE...) + * define new arrays in Windows code. + * BUT, in linux code, we use the same array for all ICs. + * + * The Correspondance relation between two arrays is: + * txpwr_cckdiff[][] == CCK_24G_Diff[][] + * txpwr_ht20diff[][] == BW20_24G_Diff[][] + * txpwr_ht40diff[][] == BW40_24G_Diff[][] + * txpwr_legacyhtdiff[][] == OFDM_24G_Diff[][] + * + * Sizes of these arrays are decided by the larger ones. + */ + char txpwr_cckdiff[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + char txpwr_ht20diff[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + char txpwr_ht40diff[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + char txpwr_legacyhtdiff[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + + u8 txpwr_5g_bw40base[MAX_RF_PATH][CHANNEL_MAX_NUMBER]; + u8 txpwr_5g_bw80base[MAX_RF_PATH][CHANNEL_MAX_NUMBER_5G_80M]; + char txpwr_5g_ofdmdiff[MAX_RF_PATH][MAX_TX_COUNT]; + char txpwr_5g_bw20diff[MAX_RF_PATH][MAX_TX_COUNT]; + char txpwr_5g_bw40diff[MAX_RF_PATH][MAX_TX_COUNT]; + char txpwr_5g_bw80diff[MAX_RF_PATH][MAX_TX_COUNT]; + u8 txpwr_safetyflag; /* Band edge enable flag */ u16 eeprom_txpowerdiff; u8 legacy_httxpowerdiff; /* Legacy to HT rate power diff */ @@ -1571,7 +1802,9 @@ struct rtl_stats { bool rx_is40Mhzpacket; u32 rx_pwdb_all; u8 rx_mimo_signalstrength[4]; /*in 0~100 index */ - s8 rx_mimo_sig_qual[2]; + s8 rx_mimo_sig_qual[4]; + u8 rx_pwr[4]; /* per-path's pwdb */ + u8 rx_snr[4]; /* per-path's SNR */ bool packet_matchbssid; bool is_cck; bool is_ht; @@ -1644,6 +1877,8 @@ struct rtl_tcb_desc { bool btx_enable_sw_calc_duration; }; +struct rtl92c_firmware_header; + struct rtl_hal_ops { int (*init_sw_vars) (struct ieee80211_hw *hw); void (*deinit_sw_vars) (struct ieee80211_hw *hw); @@ -1673,9 +1908,17 @@ struct rtl_hal_ops { void (*set_hw_reg) (struct ieee80211_hw *hw, u8 variable, u8 *val); void (*update_rate_tbl) (struct ieee80211_hw *hw, struct ieee80211_sta *sta, u8 rssi_level); + void (*pre_fill_tx_bd_desc)(struct ieee80211_hw *hw, u8 *tx_bd_desc, + u8 *desc, u8 queue_index, + struct sk_buff *skb, dma_addr_t addr); void (*update_rate_mask) (struct ieee80211_hw *hw, u8 rssi_level); + u16 (*rx_desc_buff_remained_cnt)(struct ieee80211_hw *hw, + u8 queue_index); + void (*rx_check_dma_ok)(struct ieee80211_hw *hw, u8 *header_desc, + u8 queue_index); void (*fill_tx_desc) (struct ieee80211_hw *hw, struct ieee80211_hdr *hdr, u8 *pdesc_tx, + u8 *pbd_desc_tx, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, struct sk_buff *skb, u8 hw_queue, @@ -1698,8 +1941,11 @@ struct rtl_hal_ops { enum rf_pwrstate rfpwr_state); void (*led_control) (struct ieee80211_hw *hw, enum led_ctl_mode ledaction); - void (*set_desc) (u8 *pdesc, bool istx, u8 desc_name, u8 *val); + void (*set_desc)(struct ieee80211_hw *hw, u8 *pdesc, bool istx, + u8 desc_name, u8 *val); u32 (*get_desc) (u8 *pdesc, bool istx, u8 desc_name); + bool (*is_tx_desc_closed) (struct ieee80211_hw *hw, + u8 hw_queue, u16 index); void (*tx_polling) (struct ieee80211_hw *hw, u8 hw_queue); void (*enable_hw_sec) (struct ieee80211_hw *hw); void (*set_key) (struct ieee80211_hw *hw, u32 key_index, @@ -1714,8 +1960,6 @@ struct rtl_hal_ops { u32 regaddr, u32 bitmask); void (*set_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); - void (*allow_all_destaddr)(struct ieee80211_hw *hw, - bool allow_all_da, bool write_into_reg); void (*linked_set_reg) (struct ieee80211_hw *hw); void (*chk_switch_dmdp) (struct ieee80211_hw *hw); void (*dualmac_easy_concurrent) (struct ieee80211_hw *hw); @@ -1738,6 +1982,10 @@ struct rtl_hal_ops { void (*bt_coex_off_before_lps) (struct ieee80211_hw *hw); void (*fill_h2c_cmd) (struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); + bool (*get_btc_status) (void); + bool (*is_fw_header) (struct rtl92c_firmware_header *hdr); + u32 (*rx_command_packet)(struct ieee80211_hw *hw, + struct rtl_stats status, struct sk_buff *skb); }; struct rtl_intf_ops { @@ -1780,6 +2028,10 @@ struct rtl_mod_params { /* default: 1 = using linked fw power save */ bool fwctrl_lps; + + /* default: 0 = not using MSI interrupts mode */ + /* submodules should set their own defalut value */ + bool msi_support; }; struct rtl_hal_usbint_cfg { @@ -1810,6 +2062,7 @@ struct rtl_hal_cfg { bool write_readback; char *name; char *fw_name; + char *alt_fw_name; struct rtl_hal_ops *ops; struct rtl_mod_params *mod_params; struct rtl_hal_usbint_cfg *usb_interface_cfg; @@ -1846,6 +2099,8 @@ struct rtl_locks { /*Easy concurrent*/ spinlock_t check_sendpkt_lock; + + spinlock_t iqk_lock; }; struct rtl_works { @@ -1914,6 +2169,7 @@ struct ps_t { u8 cur_ccasate; u8 pre_rfstate; u8 cur_rfstate; + u8 initialize; long rssi_val_min; }; @@ -1938,6 +2194,7 @@ struct dig_t { u8 cursta_cstate; u8 presta_cstate; u8 curmultista_cstate; + u8 stop_dig; char back_val; char back_range_max; char back_range_min; @@ -1954,6 +2211,8 @@ struct dig_t { u8 pre_ccastate; u8 cur_ccasate; u8 large_fa_hit; + u8 dig_dynamic_min; + u8 dig_dynamic_min_1; u8 forbidden_igi; u8 dig_state; u8 dig_highpwrstate; @@ -1970,6 +2229,7 @@ struct dig_t { char backoffval_range_min; u8 dig_min_0; u8 dig_min_1; + u8 bt30_cur_igi; bool media_connect_0; bool media_connect_1; @@ -1984,6 +2244,96 @@ struct rtl_global_var { spinlock_t glb_list_lock; }; +struct rtl_btc_info { + u8 bt_type; + u8 btcoexist; + u8 ant_num; +}; + +struct bt_coexist_info { + struct rtl_btc_ops *btc_ops; + struct rtl_btc_info btc_info; + /* EEPROM BT info. */ + u8 eeprom_bt_coexist; + u8 eeprom_bt_type; + u8 eeprom_bt_ant_num; + u8 eeprom_bt_ant_isol; + u8 eeprom_bt_radio_shared; + + u8 bt_coexistence; + u8 bt_ant_num; + u8 bt_coexist_type; + u8 bt_state; + u8 bt_cur_state; /* 0:on, 1:off */ + u8 bt_ant_isolation; /* 0:good, 1:bad */ + u8 bt_pape_ctrl; /* 0:SW, 1:SW/HW dynamic */ + u8 bt_service; + u8 bt_radio_shared_type; + u8 bt_rfreg_origin_1e; + u8 bt_rfreg_origin_1f; + u8 bt_rssi_state; + u32 ratio_tx; + u32 ratio_pri; + u32 bt_edca_ul; + u32 bt_edca_dl; + + bool init_set; + bool bt_busy_traffic; + bool bt_traffic_mode_set; + bool bt_non_traffic_mode_set; + + bool fw_coexist_all_off; + bool sw_coexist_all_off; + bool hw_coexist_all_off; + u32 cstate; + u32 previous_state; + u32 cstate_h; + u32 previous_state_h; + + u8 bt_pre_rssi_state; + u8 bt_pre_rssi_state1; + + u8 reg_bt_iso; + u8 reg_bt_sco; + bool balance_on; + u8 bt_active_zero_cnt; + bool cur_bt_disabled; + bool pre_bt_disabled; + + u8 bt_profile_case; + u8 bt_profile_action; + bool bt_busy; + bool hold_for_bt_operation; + u8 lps_counter; +}; + +struct rtl_btc_ops { + void (*btc_init_variables) (struct rtl_priv *rtlpriv); + void (*btc_init_hal_vars) (struct rtl_priv *rtlpriv); + void (*btc_init_hw_config) (struct rtl_priv *rtlpriv); + void (*btc_ips_notify) (struct rtl_priv *rtlpriv, u8 type); + void (*btc_scan_notify) (struct rtl_priv *rtlpriv, u8 scantype); + void (*btc_connect_notify) (struct rtl_priv *rtlpriv, u8 action); + void (*btc_mediastatus_notify) (struct rtl_priv *rtlpriv, + enum _RT_MEDIA_STATUS mstatus); + void (*btc_periodical) (struct rtl_priv *rtlpriv); + void (*btc_halt_notify) (void); + void (*btc_btinfo_notify) (struct rtl_priv *rtlpriv, + u8 *tmp_buf, u8 length); + bool (*btc_is_limited_dig) (struct rtl_priv *rtlpriv); + bool (*btc_is_disable_edca_turbo) (struct rtl_priv *rtlpriv); + bool (*btc_is_bt_disabled) (struct rtl_priv *rtlpriv); +}; + +struct proxim { + bool proxim_on; + + void *proximity_priv; + int (*proxim_rx)(struct ieee80211_hw *hw, struct rtl_stats *status, + struct sk_buff *skb); + u8 (*proxim_get_var)(struct ieee80211_hw *hw, u8 type); +}; + struct rtl_priv { struct ieee80211_hw *hw; struct completion firmware_loading_complete; @@ -2006,6 +2356,7 @@ struct rtl_priv { struct rtl_ps_ctl psc; struct rate_adaptive ra; + struct dynamic_primary_cca primarycca; struct wireless_stats stats; struct rt_link_detect link_info; struct false_alarm_statistics falsealm_cnt; @@ -2034,25 +2385,32 @@ struct rtl_priv { struct dig_t dm_digtable; struct ps_t dm_pstable; - /* section shared by individual drivers */ - union { - struct { /* data buffer pointer for USB reads */ - __le32 *usb_data; - int usb_data_index; - bool initialized; - }; - struct { /* section for 8723ae */ - bool reg_init; /* true if regs saved */ - u32 reg_874; - u32 reg_c70; - u32 reg_85c; - u32 reg_a74; - bool bt_operation_on; - }; - }; + u32 reg_874; + u32 reg_c70; + u32 reg_85c; + u32 reg_a74; + bool reg_init; /* true if regs saved */ + bool bt_operation_on; + __le32 *usb_data; + int usb_data_index; + bool initialized; bool enter_ps; /* true when entering PS */ u8 rate_mask[5]; + /* intel Proximity, should be alloc mem + * in intel Proximity module and can only + * be used in intel Proximity mode + */ + struct proxim proximity; + + /*for bt coexist use*/ + struct bt_coexist_info btcoexist; + + /* separate 92ee from other ICs, + * 92ee use new trx flow. + */ + bool use_new_trx_flow; + /*This must be the last item so that it points to the data allocated beyond this structure like: @@ -2084,6 +2442,15 @@ enum bt_co_type { BT_CSR_BC8 = 4, BT_RTL8756 = 5, BT_RTL8723A = 6, + BT_RTL8821A = 7, + BT_RTL8723B = 8, + BT_RTL8192E = 9, + BT_RTL8812A = 11, +}; + +enum bt_total_ant_num { + ANT_TOTAL_X2 = 0, + ANT_TOTAL_X1 = 1 }; enum bt_cur_state { @@ -2109,62 +2476,6 @@ enum bt_radio_shared { BT_RADIO_INDIVIDUAL = 1, }; -struct bt_coexist_info { - - /* EEPROM BT info. */ - u8 eeprom_bt_coexist; - u8 eeprom_bt_type; - u8 eeprom_bt_ant_num; - u8 eeprom_bt_ant_isol; - u8 eeprom_bt_radio_shared; - - u8 bt_coexistence; - u8 bt_ant_num; - u8 bt_coexist_type; - u8 bt_state; - u8 bt_cur_state; /* 0:on, 1:off */ - u8 bt_ant_isolation; /* 0:good, 1:bad */ - u8 bt_pape_ctrl; /* 0:SW, 1:SW/HW dynamic */ - u8 bt_service; - u8 bt_radio_shared_type; - u8 bt_rfreg_origin_1e; - u8 bt_rfreg_origin_1f; - u8 bt_rssi_state; - u32 ratio_tx; - u32 ratio_pri; - u32 bt_edca_ul; - u32 bt_edca_dl; - - bool init_set; - bool bt_busy_traffic; - bool bt_traffic_mode_set; - bool bt_non_traffic_mode_set; - - bool fw_coexist_all_off; - bool sw_coexist_all_off; - bool hw_coexist_all_off; - u32 cstate; - u32 previous_state; - u32 cstate_h; - u32 previous_state_h; - - u8 bt_pre_rssi_state; - u8 bt_pre_rssi_state1; - - u8 reg_bt_iso; - u8 reg_bt_sco; - bool balance_on; - u8 bt_active_zero_cnt; - bool cur_bt_disabled; - bool pre_bt_disabled; - - u8 bt_profile_case; - u8 bt_profile_action; - bool bt_busy; - bool hold_for_bt_operation; - u8 lps_counter; -}; - /**************************************** mem access macro define start diff --git a/drivers/net/wireless/ti/wilink_platform_data.c b/drivers/net/wireless/ti/wilink_platform_data.c index 998e95895f9..a92bd3e8979 100644 --- a/drivers/net/wireless/ti/wilink_platform_data.c +++ b/drivers/net/wireless/ti/wilink_platform_data.c @@ -23,17 +23,17 @@ #include <linux/err.h> #include <linux/wl12xx.h> -static struct wl12xx_platform_data *platform_data; +static struct wl12xx_platform_data *wl12xx_platform_data; int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data) { - if (platform_data) + if (wl12xx_platform_data) return -EBUSY; if (!data) return -EINVAL; - platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); - if (!platform_data) + wl12xx_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); + if (!wl12xx_platform_data) return -ENOMEM; return 0; @@ -41,9 +41,34 @@ int __init wl12xx_set_platform_data(const struct wl12xx_platform_data *data) struct wl12xx_platform_data *wl12xx_get_platform_data(void) { - if (!platform_data) + if (!wl12xx_platform_data) return ERR_PTR(-ENODEV); - return platform_data; + return wl12xx_platform_data; } EXPORT_SYMBOL(wl12xx_get_platform_data); + +static struct wl1251_platform_data *wl1251_platform_data; + +int __init wl1251_set_platform_data(const struct wl1251_platform_data *data) +{ + if (wl1251_platform_data) + return -EBUSY; + if (!data) + return -EINVAL; + + wl1251_platform_data = kmemdup(data, sizeof(*data), GFP_KERNEL); + if (!wl1251_platform_data) + return -ENOMEM; + + return 0; +} + +struct wl1251_platform_data *wl1251_get_platform_data(void) +{ + if (!wl1251_platform_data) + return ERR_PTR(-ENODEV); + + return wl1251_platform_data; +} +EXPORT_SYMBOL(wl1251_get_platform_data); diff --git a/drivers/net/wireless/ti/wl1251/acx.c b/drivers/net/wireless/ti/wl1251/acx.c index db6430c1a08..5695628757e 100644 --- a/drivers/net/wireless/ti/wl1251/acx.c +++ b/drivers/net/wireless/ti/wl1251/acx.c @@ -2,7 +2,6 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/crc7.h> #include "wl1251.h" #include "reg.h" @@ -18,10 +17,8 @@ int wl1251_acx_frame_rates(struct wl1251 *wl, u8 ctrl_rate, u8 ctrl_mod, wl1251_debug(DEBUG_ACX, "acx frame rates"); rates = kzalloc(sizeof(*rates), GFP_KERNEL); - if (!rates) { - ret = -ENOMEM; - goto out; - } + if (!rates) + return -ENOMEM; rates->tx_ctrl_frame_rate = ctrl_rate; rates->tx_ctrl_frame_mod = ctrl_mod; @@ -49,10 +46,8 @@ int wl1251_acx_station_id(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx dot11_station_id"); mac = kzalloc(sizeof(*mac), GFP_KERNEL); - if (!mac) { - ret = -ENOMEM; - goto out; - } + if (!mac) + return -ENOMEM; for (i = 0; i < ETH_ALEN; i++) mac->mac[i] = wl->mac_addr[ETH_ALEN - 1 - i]; @@ -74,10 +69,8 @@ int wl1251_acx_default_key(struct wl1251 *wl, u8 key_id) wl1251_debug(DEBUG_ACX, "acx dot11_default_key (%d)", key_id); default_key = kzalloc(sizeof(*default_key), GFP_KERNEL); - if (!default_key) { - ret = -ENOMEM; - goto out; - } + if (!default_key) + return -ENOMEM; default_key->id = key_id; @@ -104,10 +97,8 @@ int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event, wl1251_debug(DEBUG_ACX, "acx wake up conditions"); wake_up = kzalloc(sizeof(*wake_up), GFP_KERNEL); - if (!wake_up) { - ret = -ENOMEM; - goto out; - } + if (!wake_up) + return -ENOMEM; wake_up->wake_up_event = wake_up_event; wake_up->listen_interval = listen_interval; @@ -132,16 +123,13 @@ int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth) wl1251_debug(DEBUG_ACX, "acx sleep auth"); auth = kzalloc(sizeof(*auth), GFP_KERNEL); - if (!auth) { - ret = -ENOMEM; - goto out; - } + if (!auth) + return -ENOMEM; auth->sleep_auth = sleep_auth; ret = wl1251_cmd_configure(wl, ACX_SLEEP_AUTH, auth, sizeof(*auth)); -out: kfree(auth); return ret; } @@ -154,10 +142,8 @@ int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len) wl1251_debug(DEBUG_ACX, "acx fw rev"); rev = kzalloc(sizeof(*rev), GFP_KERNEL); - if (!rev) { - ret = -ENOMEM; - goto out; - } + if (!rev) + return -ENOMEM; ret = wl1251_cmd_interrogate(wl, ACX_FW_REV, rev, sizeof(*rev)); if (ret < 0) { @@ -191,10 +177,8 @@ int wl1251_acx_tx_power(struct wl1251 *wl, int power) return -EINVAL; acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->current_tx_power = power * 10; @@ -209,7 +193,7 @@ out: return ret; } -int wl1251_acx_feature_cfg(struct wl1251 *wl) +int wl1251_acx_feature_cfg(struct wl1251 *wl, u32 data_flow_options) { struct acx_feature_config *feature; int ret; @@ -217,13 +201,11 @@ int wl1251_acx_feature_cfg(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx feature cfg"); feature = kzalloc(sizeof(*feature), GFP_KERNEL); - if (!feature) { - ret = -ENOMEM; - goto out; - } + if (!feature) + return -ENOMEM; - /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */ - feature->data_flow_options = 0; + /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE can be set */ + feature->data_flow_options = data_flow_options; feature->options = 0; ret = wl1251_cmd_configure(wl, ACX_FEATURE_CFG, @@ -261,10 +243,8 @@ int wl1251_acx_data_path_params(struct wl1251 *wl, wl1251_debug(DEBUG_ACX, "acx data path params"); params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - ret = -ENOMEM; - goto out; - } + if (!params) + return -ENOMEM; params->rx_packet_ring_chunk_size = DP_RX_PACKET_RING_CHUNK_SIZE; params->tx_packet_ring_chunk_size = DP_TX_PACKET_RING_CHUNK_SIZE; @@ -309,10 +289,8 @@ int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time) wl1251_debug(DEBUG_ACX, "acx rx msdu life time"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->lifetime = life_time; ret = wl1251_cmd_configure(wl, DOT11_RX_MSDU_LIFE_TIME, @@ -335,10 +313,8 @@ int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter) wl1251_debug(DEBUG_ACX, "acx rx config"); rx_config = kzalloc(sizeof(*rx_config), GFP_KERNEL); - if (!rx_config) { - ret = -ENOMEM; - goto out; - } + if (!rx_config) + return -ENOMEM; rx_config->config_options = config; rx_config->filter_options = filter; @@ -363,10 +339,8 @@ int wl1251_acx_pd_threshold(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx data pd threshold"); pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - ret = -ENOMEM; - goto out; - } + if (!pd) + return -ENOMEM; /* FIXME: threshold value not set */ @@ -389,10 +363,8 @@ int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time) wl1251_debug(DEBUG_ACX, "acx slot"); slot = kzalloc(sizeof(*slot), GFP_KERNEL); - if (!slot) { - ret = -ENOMEM; - goto out; - } + if (!slot) + return -ENOMEM; slot->wone_index = STATION_WONE_INDEX; slot->slot_time = slot_time; @@ -408,7 +380,8 @@ out: return ret; } -int wl1251_acx_group_address_tbl(struct wl1251 *wl) +int wl1251_acx_group_address_tbl(struct wl1251 *wl, bool enable, + void *mc_list, u32 mc_list_len) { struct acx_dot11_grp_addr_tbl *acx; int ret; @@ -416,15 +389,13 @@ int wl1251_acx_group_address_tbl(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx group address tbl"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; /* MAC filtering */ - acx->enabled = 0; - acx->num_groups = 0; - memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN); + acx->enabled = enable; + acx->num_groups = mc_list_len; + memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN); ret = wl1251_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL, acx, sizeof(*acx)); @@ -444,10 +415,8 @@ int wl1251_acx_service_period_timeout(struct wl1251 *wl) int ret; rx_timeout = kzalloc(sizeof(*rx_timeout), GFP_KERNEL); - if (!rx_timeout) { - ret = -ENOMEM; - goto out; - } + if (!rx_timeout) + return -ENOMEM; wl1251_debug(DEBUG_ACX, "acx service period timeout"); @@ -475,10 +444,8 @@ int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold) wl1251_debug(DEBUG_ACX, "acx rts threshold"); rts = kzalloc(sizeof(*rts), GFP_KERNEL); - if (!rts) { - ret = -ENOMEM; - goto out; - } + if (!rts) + return -ENOMEM; rts->threshold = rts_threshold; @@ -501,10 +468,8 @@ int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter) wl1251_debug(DEBUG_ACX, "acx beacon filter opt"); beacon_filter = kzalloc(sizeof(*beacon_filter), GFP_KERNEL); - if (!beacon_filter) { - ret = -ENOMEM; - goto out; - } + if (!beacon_filter) + return -ENOMEM; beacon_filter->enable = enable_filter; beacon_filter->max_num_beacons = 0; @@ -530,10 +495,8 @@ int wl1251_acx_beacon_filter_table(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx beacon filter table"); ie_table = kzalloc(sizeof(*ie_table), GFP_KERNEL); - if (!ie_table) { - ret = -ENOMEM; - goto out; - } + if (!ie_table) + return -ENOMEM; /* configure default beacon pass-through rules */ ie_table->num_ie = 1; @@ -560,10 +523,8 @@ int wl1251_acx_conn_monit_params(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx connection monitor parameters"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->synch_fail_thold = SYNCH_FAIL_DEFAULT_THRESHOLD; acx->bss_lose_timeout = NO_BEACON_DEFAULT_TIMEOUT; @@ -589,10 +550,8 @@ int wl1251_acx_sg_enable(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx sg enable"); pta = kzalloc(sizeof(*pta), GFP_KERNEL); - if (!pta) { - ret = -ENOMEM; - goto out; - } + if (!pta) + return -ENOMEM; pta->enable = SG_ENABLE; @@ -615,10 +574,8 @@ int wl1251_acx_sg_cfg(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx sg cfg"); param = kzalloc(sizeof(*param), GFP_KERNEL); - if (!param) { - ret = -ENOMEM; - goto out; - } + if (!param) + return -ENOMEM; /* BT-WLAN coext parameters */ param->min_rate = RATE_INDEX_24MBPS; @@ -669,10 +626,8 @@ int wl1251_acx_cca_threshold(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx cca threshold"); detection = kzalloc(sizeof(*detection), GFP_KERNEL); - if (!detection) { - ret = -ENOMEM; - goto out; - } + if (!detection) + return -ENOMEM; detection->rx_cca_threshold = CCA_THRSH_DISABLE_ENERGY_D; detection->tx_energy_detection = 0; @@ -682,7 +637,6 @@ int wl1251_acx_cca_threshold(struct wl1251 *wl) if (ret < 0) wl1251_warning("failed to set cca threshold: %d", ret); -out: kfree(detection); return ret; } @@ -695,10 +649,8 @@ int wl1251_acx_bcn_dtim_options(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx bcn dtim options"); bb = kzalloc(sizeof(*bb), GFP_KERNEL); - if (!bb) { - ret = -ENOMEM; - goto out; - } + if (!bb) + return -ENOMEM; bb->beacon_rx_timeout = BCN_RX_TIMEOUT_DEF_VALUE; bb->broadcast_timeout = BROADCAST_RX_TIMEOUT_DEF_VALUE; @@ -724,10 +676,8 @@ int wl1251_acx_aid(struct wl1251 *wl, u16 aid) wl1251_debug(DEBUG_ACX, "acx aid"); acx_aid = kzalloc(sizeof(*acx_aid), GFP_KERNEL); - if (!acx_aid) { - ret = -ENOMEM; - goto out; - } + if (!acx_aid) + return -ENOMEM; acx_aid->aid = aid; @@ -750,10 +700,8 @@ int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask) wl1251_debug(DEBUG_ACX, "acx event mbox mask"); mask = kzalloc(sizeof(*mask), GFP_KERNEL); - if (!mask) { - ret = -ENOMEM; - goto out; - } + if (!mask) + return -ENOMEM; /* high event mask is unused */ mask->high_event_mask = 0xffffffff; @@ -805,10 +753,8 @@ int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble) wl1251_debug(DEBUG_ACX, "acx_set_preamble"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->preamble = preamble; @@ -832,10 +778,8 @@ int wl1251_acx_cts_protect(struct wl1251 *wl, wl1251_debug(DEBUG_ACX, "acx_set_ctsprotect"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->ctsprotect = ctsprotect; @@ -856,10 +800,8 @@ int wl1251_acx_tsf_info(struct wl1251 *wl, u64 *mactime) int ret; tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL); - if (!tsf_info) { - ret = -ENOMEM; - goto out; - } + if (!tsf_info) + return -ENOMEM; ret = wl1251_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info, sizeof(*tsf_info)); @@ -900,19 +842,22 @@ int wl1251_acx_rate_policies(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx rate policies"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; /* configure one default (one-size-fits-all) rate class */ - acx->rate_class_cnt = 1; + acx->rate_class_cnt = 2; acx->rate_class[0].enabled_rates = ACX_RATE_MASK_UNSPECIFIED; acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT; acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT; acx->rate_class[0].aflags = 0; + /* no-retry rate class */ + acx->rate_class[1].enabled_rates = ACX_RATE_MASK_UNSPECIFIED; + acx->rate_class[1].short_retry_limit = 0; + acx->rate_class[1].long_retry_limit = 0; + acx->rate_class[1].aflags = 0; + ret = wl1251_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx)); if (ret < 0) { wl1251_warning("Setting of rate policies failed: %d", ret); @@ -932,10 +877,8 @@ int wl1251_acx_mem_cfg(struct wl1251 *wl) wl1251_debug(DEBUG_ACX, "acx mem cfg"); mem_conf = kzalloc(sizeof(*mem_conf), GFP_KERNEL); - if (!mem_conf) { - ret = -ENOMEM; - goto out; - } + if (!mem_conf) + return -ENOMEM; /* memory config */ mem_conf->mem_config.num_stations = cpu_to_le16(DEFAULT_NUM_STATIONS); @@ -979,10 +922,8 @@ int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim) wl1251_debug(DEBUG_ACX, "acx tbtt and dtim"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->tbtt = tbtt; acx->dtim = dtim; @@ -1008,10 +949,8 @@ int wl1251_acx_bet_enable(struct wl1251 *wl, enum wl1251_acx_bet_mode mode, wl1251_debug(DEBUG_ACX, "acx bet enable"); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->enable = mode; acx->max_consecutive = max_consecutive; @@ -1027,6 +966,32 @@ out: return ret; } +int wl1251_acx_arp_ip_filter(struct wl1251 *wl, bool enable, __be32 address) +{ + struct wl1251_acx_arp_filter *acx; + int ret; + + wl1251_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + acx->version = ACX_IPV4_VERSION; + acx->enable = enable; + + if (enable) + memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE); + + ret = wl1251_cmd_configure(wl, ACX_ARP_IP_FILTER, + acx, sizeof(*acx)); + if (ret < 0) + wl1251_warning("failed to set arp ip filter: %d", ret); + + kfree(acx); + return ret; +} + int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, u8 aifs, u16 txop) { @@ -1037,11 +1002,8 @@ int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, "aifs %d txop %d", ac, cw_min, cw_max, aifs, txop); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->ac = ac; acx->cw_min = cw_min; @@ -1073,11 +1035,8 @@ int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, ps_scheme, ack_policy); acx = kzalloc(sizeof(*acx), GFP_KERNEL); - - if (!acx) { - ret = -ENOMEM; - goto out; - } + if (!acx) + return -ENOMEM; acx->queue = queue; acx->type = type; diff --git a/drivers/net/wireless/ti/wl1251/acx.h b/drivers/net/wireless/ti/wl1251/acx.h index c2ba100f9b1..2bdec38699f 100644 --- a/drivers/net/wireless/ti/wl1251/acx.h +++ b/drivers/net/wireless/ti/wl1251/acx.h @@ -350,8 +350,8 @@ struct acx_slot { } __packed; -#define ADDRESS_GROUP_MAX (8) -#define ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ADDRESS_GROUP_MAX) +#define ACX_MC_ADDRESS_GROUP_MAX (8) +#define ACX_MC_ADDRESS_GROUP_MAX_LEN (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX) struct acx_dot11_grp_addr_tbl { struct acx_header header; @@ -359,7 +359,7 @@ struct acx_dot11_grp_addr_tbl { u8 enabled; u8 num_groups; u8 pad[2]; - u8 mac_table[ADDRESS_GROUP_MAX_LEN]; + u8 mac_table[ACX_MC_ADDRESS_GROUP_MAX_LEN]; } __packed; @@ -1232,6 +1232,20 @@ struct wl1251_acx_bet_enable { u8 padding[2]; } __packed; +#define ACX_IPV4_VERSION 4 +#define ACX_IPV6_VERSION 6 +#define ACX_IPV4_ADDR_SIZE 4 +struct wl1251_acx_arp_filter { + struct acx_header header; + u8 version; /* The IP version: 4 - IPv4, 6 - IPv6.*/ + u8 enable; /* 1 - ARP filtering is enabled, 0 - disabled */ + u8 padding[2]; + u8 address[16]; /* The IP address used to filter ARP packets. + ARP packets that do not match this address are + dropped. When the IP Version is 4, the last 12 + bytes of the the address are ignored. */ +} __attribute__((packed)); + struct wl1251_acx_ac_cfg { struct acx_header header; @@ -1440,7 +1454,7 @@ int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event, int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth); int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len); int wl1251_acx_tx_power(struct wl1251 *wl, int power); -int wl1251_acx_feature_cfg(struct wl1251 *wl); +int wl1251_acx_feature_cfg(struct wl1251 *wl, u32 data_flow_options); int wl1251_acx_mem_map(struct wl1251 *wl, struct acx_header *mem_map, size_t len); int wl1251_acx_data_path_params(struct wl1251 *wl, @@ -1449,7 +1463,8 @@ int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time); int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter); int wl1251_acx_pd_threshold(struct wl1251 *wl); int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time); -int wl1251_acx_group_address_tbl(struct wl1251 *wl); +int wl1251_acx_group_address_tbl(struct wl1251 *wl, bool enable, + void *mc_list, u32 mc_list_len); int wl1251_acx_service_period_timeout(struct wl1251 *wl); int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold); int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter); @@ -1473,6 +1488,7 @@ int wl1251_acx_mem_cfg(struct wl1251 *wl); int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim); int wl1251_acx_bet_enable(struct wl1251 *wl, enum wl1251_acx_bet_mode mode, u8 max_consecutive); +int wl1251_acx_arp_ip_filter(struct wl1251 *wl, bool enable, __be32 address); int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max, u8 aifs, u16 txop); int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue, diff --git a/drivers/net/wireless/ti/wl1251/boot.c b/drivers/net/wireless/ti/wl1251/boot.c index a2e5241382d..2000cd53607 100644 --- a/drivers/net/wireless/ti/wl1251/boot.c +++ b/drivers/net/wireless/ti/wl1251/boot.c @@ -299,7 +299,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) ROAMING_TRIGGER_LOW_RSSI_EVENT_ID | ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID | REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID | - BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID; + BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID | + PS_REPORT_EVENT_ID; ret = wl1251_event_unmask(wl); if (ret < 0) { diff --git a/drivers/net/wireless/ti/wl1251/cmd.c b/drivers/net/wireless/ti/wl1251/cmd.c index 6822b845efc..ede31f048ef 100644 --- a/drivers/net/wireless/ti/wl1251/cmd.c +++ b/drivers/net/wireless/ti/wl1251/cmd.c @@ -2,7 +2,7 @@ #include <linux/module.h> #include <linux/slab.h> -#include <linux/crc7.h> +#include <linux/etherdevice.h> #include "wl1251.h" #include "reg.h" @@ -203,11 +203,11 @@ out: return ret; } -int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable) +int wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable) { struct cmd_enabledisable_path *cmd; int ret; - u16 cmd_rx, cmd_tx; + u16 cmd_rx; wl1251_debug(DEBUG_CMD, "cmd data path"); @@ -219,13 +219,10 @@ int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable) cmd->channel = channel; - if (enable) { + if (enable) cmd_rx = CMD_ENABLE_RX; - cmd_tx = CMD_ENABLE_TX; - } else { + else cmd_rx = CMD_DISABLE_RX; - cmd_tx = CMD_DISABLE_TX; - } ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd)); if (ret < 0) { @@ -237,17 +234,38 @@ int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable) wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d", enable ? "start" : "stop", channel); +out: + kfree(cmd); + return ret; +} + +int wl1251_cmd_data_path_tx(struct wl1251 *wl, u8 channel, bool enable) +{ + struct cmd_enabledisable_path *cmd; + int ret; + u16 cmd_tx; + + wl1251_debug(DEBUG_CMD, "cmd data path"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->channel = channel; + + if (enable) + cmd_tx = CMD_ENABLE_TX; + else + cmd_tx = CMD_DISABLE_TX; + ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd)); - if (ret < 0) { + if (ret < 0) wl1251_error("tx %s cmd for channel %d failed", enable ? "start" : "stop", channel); - goto out; - } - - wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", - enable ? "start" : "stop", channel); + else + wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d", + enable ? "start" : "stop", channel); -out: kfree(cmd); return ret; } @@ -410,7 +428,9 @@ int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, struct wl1251_cmd_scan *cmd; int i, ret = 0; - wl1251_debug(DEBUG_CMD, "cmd scan"); + wl1251_debug(DEBUG_CMD, "cmd scan channels %d", n_channels); + + WARN_ON(n_channels > SCAN_MAX_NUM_OF_CHANNELS); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) @@ -421,6 +441,13 @@ int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, CFG_RX_MGMT_EN | CFG_RX_BCN_EN); cmd->params.scan_options = 0; + /* + * Use high priority scan when not associated to prevent fw issue + * causing never-ending scans (sometimes 20+ minutes). + * Note: This bug may be caused by the fw's DTIM handling. + */ + if (is_zero_ether_addr(wl->bssid)) + cmd->params.scan_options |= cpu_to_le16(WL1251_SCAN_OPT_PRIORITY_HIGH); cmd->params.num_channels = n_channels; cmd->params.num_probe_requests = n_probes; cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ diff --git a/drivers/net/wireless/ti/wl1251/cmd.h b/drivers/net/wireless/ti/wl1251/cmd.h index ee4f2b39182..d824ff97831 100644 --- a/drivers/net/wireless/ti/wl1251/cmd.h +++ b/drivers/net/wireless/ti/wl1251/cmd.h @@ -35,7 +35,8 @@ int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len); int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len); int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity, void *bitmap, u16 bitmap_len, u8 bitmap_control); -int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable); +int wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable); +int wl1251_cmd_data_path_tx(struct wl1251 *wl, u8 channel, bool enable); int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel, u16 beacon_interval, u8 dtim_interval); int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode); @@ -167,6 +168,11 @@ struct cmd_read_write_memory { #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 +#define WL1251_SCAN_OPT_PASSIVE 1 +#define WL1251_SCAN_OPT_5GHZ_BAND 2 +#define WL1251_SCAN_OPT_TRIGGERD_SCAN 4 +#define WL1251_SCAN_OPT_PRIORITY_HIGH 8 + #define WL1251_SCAN_MIN_DURATION 30000 #define WL1251_SCAN_MAX_DURATION 60000 diff --git a/drivers/net/wireless/ti/wl1251/event.c b/drivers/net/wireless/ti/wl1251/event.c index 74ae8e1c2e3..c98630394a1 100644 --- a/drivers/net/wireless/ti/wl1251/event.c +++ b/drivers/net/wireless/ti/wl1251/event.c @@ -46,6 +46,43 @@ static int wl1251_event_scan_complete(struct wl1251 *wl, return ret; } +#define WL1251_PSM_ENTRY_RETRIES 3 +static int wl1251_event_ps_report(struct wl1251 *wl, + struct event_mailbox *mbox) +{ + int ret = 0; + + wl1251_debug(DEBUG_EVENT, "ps status: %x", mbox->ps_status); + + switch (mbox->ps_status) { + case EVENT_ENTER_POWER_SAVE_FAIL: + wl1251_debug(DEBUG_PSM, "PSM entry failed"); + + if (wl->station_mode != STATION_POWER_SAVE_MODE) { + /* remain in active mode */ + wl->psm_entry_retry = 0; + break; + } + + if (wl->psm_entry_retry < WL1251_PSM_ENTRY_RETRIES) { + wl->psm_entry_retry++; + ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); + } else { + wl1251_error("Power save entry failed, giving up"); + wl->psm_entry_retry = 0; + } + break; + case EVENT_ENTER_POWER_SAVE_SUCCESS: + case EVENT_EXIT_POWER_SAVE_FAIL: + case EVENT_EXIT_POWER_SAVE_SUCCESS: + default: + wl->psm_entry_retry = 0; + break; + } + + return 0; +} + static void wl1251_event_mbox_dump(struct event_mailbox *mbox) { wl1251_debug(DEBUG_EVENT, "MBOX DUMP:"); @@ -80,11 +117,19 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox) } } + if (vector & PS_REPORT_EVENT_ID) { + wl1251_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); + ret = wl1251_event_ps_report(wl, mbox); + if (ret < 0) + return ret; + } + if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) { wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT"); /* indicate to the stack, that beacons have been lost */ - ieee80211_beacon_loss(wl->vif); + if (wl->vif && wl->vif->type == NL80211_IFTYPE_STATION) + ieee80211_beacon_loss(wl->vif); } if (vector & REGAINED_BSS_EVENT_ID) { diff --git a/drivers/net/wireless/ti/wl1251/event.h b/drivers/net/wireless/ti/wl1251/event.h index 30eb5d150bf..88570a5cd04 100644 --- a/drivers/net/wireless/ti/wl1251/event.h +++ b/drivers/net/wireless/ti/wl1251/event.h @@ -112,6 +112,13 @@ struct event_mailbox { u8 padding[19]; } __packed; +enum { + EVENT_ENTER_POWER_SAVE_FAIL = 0, + EVENT_ENTER_POWER_SAVE_SUCCESS, + EVENT_EXIT_POWER_SAVE_FAIL, + EVENT_EXIT_POWER_SAVE_SUCCESS, +}; + int wl1251_event_unmask(struct wl1251 *wl); void wl1251_event_mbox_config(struct wl1251 *wl); int wl1251_event_handle(struct wl1251 *wl, u8 mbox); diff --git a/drivers/net/wireless/ti/wl1251/init.c b/drivers/net/wireless/ti/wl1251/init.c index 89b43d35473..1d799bffaa9 100644 --- a/drivers/net/wireless/ti/wl1251/init.c +++ b/drivers/net/wireless/ti/wl1251/init.c @@ -33,7 +33,7 @@ int wl1251_hw_init_hwenc_config(struct wl1251 *wl) { int ret; - ret = wl1251_acx_feature_cfg(wl); + ret = wl1251_acx_feature_cfg(wl, 0); if (ret < 0) { wl1251_warning("couldn't set feature config"); return ret; @@ -127,7 +127,7 @@ int wl1251_hw_init_phy_config(struct wl1251 *wl) if (ret < 0) return ret; - ret = wl1251_acx_group_address_tbl(wl); + ret = wl1251_acx_group_address_tbl(wl, true, NULL, 0); if (ret < 0) return ret; @@ -394,8 +394,13 @@ int wl1251_hw_init(struct wl1251 *wl) if (ret < 0) goto out_free_data_path; - /* Enable data path */ - ret = wl1251_cmd_data_path(wl, wl->channel, 1); + /* Enable rx data path */ + ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1); + if (ret < 0) + goto out_free_data_path; + + /* Enable tx data path */ + ret = wl1251_cmd_data_path_tx(wl, wl->channel, 1); if (ret < 0) goto out_free_data_path; diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 3291ffa9527..4e782f18ae3 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -28,6 +28,7 @@ #include <linux/etherdevice.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/netdevice.h> #include "wl1251.h" #include "wl12xx_80211.h" @@ -479,10 +480,13 @@ static void wl1251_op_stop(struct ieee80211_hw *hw) wl->next_tx_complete = 0; wl->elp = false; wl->station_mode = STATION_ACTIVE_MODE; + wl->psm_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->rssi_thold = 0; wl->channel = WL1251_DEFAULT_CHANNEL; + wl->monitor_present = false; + wl->joined = false; wl1251_debugfs_reset(wl); @@ -521,7 +525,7 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, goto out; } - if (memcmp(wl->mac_addr, vif->addr, ETH_ALEN)) { + if (!ether_addr_equal_unaligned(wl->mac_addr, vif->addr)) { memcpy(wl->mac_addr, vif->addr, ETH_ALEN); SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); ret = wl1251_acx_station_id(wl); @@ -542,9 +546,38 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw, mutex_lock(&wl->mutex); wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface"); wl->vif = NULL; + memset(wl->bssid, 0, ETH_ALEN); mutex_unlock(&wl->mutex); } +static int wl1251_build_null_data(struct wl1251 *wl) +{ + struct sk_buff *skb = NULL; + int size; + void *ptr; + int ret = -ENOMEM; + + if (wl->bss_type == BSS_TYPE_IBSS) { + size = sizeof(struct wl12xx_null_data_template); + ptr = NULL; + } else { + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; + size = skb->len; + ptr = skb->data; + } + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, ptr, size); + +out: + dev_kfree_skb(skb); + if (ret) + wl1251_warning("cmd buld null data failed: %d", ret); + + return ret; +} + static int wl1251_build_qos_null_data(struct wl1251 *wl) { struct ieee80211_qos_hdr template; @@ -566,6 +599,11 @@ static int wl1251_build_qos_null_data(struct wl1251 *wl) sizeof(template)); } +static bool wl1251_can_do_pm(struct ieee80211_conf *conf, struct wl1251 *wl) +{ + return (conf->flags & IEEE80211_CONF_PS) && !wl->monitor_present; +} + static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1251 *wl = hw->priv; @@ -575,8 +613,10 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) channel = ieee80211_frequency_to_channel( conf->chandef.chan->center_freq); - wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", + wl1251_debug(DEBUG_MAC80211, + "mac80211 config ch %d monitor %s psm %s power %d", channel, + conf->flags & IEEE80211_CONF_MONITOR ? "on" : "off", conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); @@ -586,16 +626,44 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) if (ret < 0) goto out; + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + u32 mode; + + if (conf->flags & IEEE80211_CONF_MONITOR) { + wl->monitor_present = true; + mode = DF_SNIFF_MODE_ENABLE | DF_ENCRYPTION_DISABLE; + } else { + wl->monitor_present = false; + mode = 0; + } + + ret = wl1251_acx_feature_cfg(wl, mode); + if (ret < 0) + goto out_sleep; + } + if (channel != wl->channel) { wl->channel = channel; - ret = wl1251_join(wl, wl->bss_type, wl->channel, - wl->beacon_int, wl->dtim_period); + /* + * Use ENABLE_RX command for channel switching when no + * interface is present (monitor mode only). + * This leaves the tx path disabled in firmware, whereas + * the usual JOIN command seems to transmit some frames + * at firmware level. + */ + if (wl->vif == NULL) { + wl->joined = false; + ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1); + } else { + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); + } if (ret < 0) goto out_sleep; } - if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { + if (wl1251_can_do_pm(conf, wl) && !wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm enabled"); wl->psm_requested = true; @@ -611,8 +679,7 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE); if (ret < 0) goto out_sleep; - } else if (!(conf->flags & IEEE80211_CONF_PS) && - wl->psm_requested) { + } else if (!wl1251_can_do_pm(conf, wl) && wl->psm_requested) { wl1251_debug(DEBUG_PSM, "psm disabled"); wl->psm_requested = false; @@ -657,6 +724,44 @@ out: return ret; } +struct wl1251_filter_params { + bool enabled; + int mc_list_length; + u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN]; +}; + +static u64 wl1251_op_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct wl1251_filter_params *fp; + struct netdev_hw_addr *ha; + struct wl1251 *wl = hw->priv; + + if (unlikely(wl->state == WL1251_STATE_OFF)) + return 0; + + fp = kzalloc(sizeof(*fp), GFP_ATOMIC); + if (!fp) { + wl1251_error("Out of memory setting filters."); + return 0; + } + + /* update multicast filtering parameters */ + fp->mc_list_length = 0; + if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) { + fp->enabled = false; + } else { + fp->enabled = true; + netdev_hw_addr_list_for_each(ha, mc_list) { + memcpy(fp->mc_list[fp->mc_list_length], + ha->addr, ETH_ALEN); + fp->mc_list_length++; + } + } + + return (u64)(unsigned long)fp; +} + #define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \ FIF_ALLMULTI | \ FIF_FCSFAIL | \ @@ -667,8 +772,9 @@ out: static void wl1251_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed, - unsigned int *total,u64 multicast) + unsigned int *total, u64 multicast) { + struct wl1251_filter_params *fp = (void *)(unsigned long)multicast; struct wl1251 *wl = hw->priv; int ret; @@ -677,9 +783,11 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, *total &= WL1251_SUPPORTED_FILTERS; changed &= WL1251_SUPPORTED_FILTERS; - if (changed == 0) + if (changed == 0) { /* no filters which we support changed */ + kfree(fp); return; + } mutex_lock(&wl->mutex); @@ -716,6 +824,15 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, if (ret < 0) goto out; + if (*total & FIF_ALLMULTI || *total & FIF_PROMISC_IN_BSS) + ret = wl1251_acx_group_address_tbl(wl, false, NULL, 0); + else if (fp) + ret = wl1251_acx_group_address_tbl(wl, fp->enabled, + fp->mc_list, + fp->mc_list_length); + if (ret < 0) + goto out; + /* send filters to firmware */ wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter); @@ -723,6 +840,7 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw, out: mutex_unlock(&wl->mutex); + kfree(fp); } /* HW encryption */ @@ -802,12 +920,12 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, mutex_lock(&wl->mutex); - ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) - goto out_unlock; - switch (cmd) { case SET_KEY: + if (wl->monitor_present) { + ret = -EOPNOTSUPP; + goto out_unlock; + } wl_cmd->key_action = KEY_ADD_OR_REPLACE; break; case DISABLE_KEY: @@ -818,6 +936,10 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, break; } + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) + goto out_unlock; + ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr); if (ret < 0) { wl1251_error("Set KEY type failed"); @@ -930,6 +1052,7 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels, req->n_channels, WL1251_SCAN_NUM_PROBES); if (ret < 0) { + wl1251_debug(DEBUG_SCAN, "scan failed %d", ret); wl->scanning = false; goto out_idle; } @@ -977,6 +1100,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, { struct wl1251 *wl = hw->priv; struct sk_buff *beacon, *skb; + bool enable; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -997,24 +1121,19 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->rssi_thold = bss_conf->cqm_rssi_thold; } - if (changed & BSS_CHANGED_BSSID) { + if ((changed & BSS_CHANGED_BSSID) && + memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - skb = ieee80211_nullfunc_get(wl->hw, wl->vif); - if (!skb) - goto out_sleep; - - ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, - skb->data, skb->len); - dev_kfree_skb(skb); - if (ret < 0) - goto out_sleep; + if (!is_zero_ether_addr(wl->bssid)) { + ret = wl1251_build_null_data(wl); + if (ret < 0) + goto out_sleep; - ret = wl1251_build_qos_null_data(wl); - if (ret < 0) - goto out; + ret = wl1251_build_qos_null_data(wl); + if (ret < 0) + goto out_sleep; - if (wl->bss_type != BSS_TYPE_IBSS) { ret = wl1251_join(wl, wl->bss_type, wl->channel, wl->beacon_int, wl->dtim_period); if (ret < 0) @@ -1075,6 +1194,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, } } + if (changed & BSS_CHANGED_ARP_FILTER) { + __be32 addr = bss_conf->arp_addr_list[0]; + WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS); + + enable = bss_conf->arp_addr_cnt == 1 && bss_conf->assoc; + wl1251_acx_arp_ip_filter(wl, enable, addr); + + if (ret < 0) + goto out_sleep; + } + if (changed & BSS_CHANGED_BEACON) { beacon = ieee80211_beacon_get(hw, vif); if (!beacon) @@ -1096,8 +1226,8 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, - wl->channel, wl->dtim_period); + ret = wl1251_join(wl, wl->bss_type, wl->channel, + wl->beacon_int, wl->dtim_period); if (ret < 0) goto out_sleep; @@ -1245,6 +1375,7 @@ static const struct ieee80211_ops wl1251_ops = { .add_interface = wl1251_op_add_interface, .remove_interface = wl1251_op_remove_interface, .config = wl1251_op_config, + .prepare_multicast = wl1251_op_prepare_multicast, .configure_filter = wl1251_op_configure_filter, .tx = wl1251_op_tx, .set_key = wl1251_op_set_key, @@ -1347,7 +1478,6 @@ int wl1251_init_ieee80211(struct wl1251 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->channel_change_time = 10000; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | @@ -1401,7 +1531,10 @@ struct ieee80211_hw *wl1251_alloc_hw(void) INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work); wl->channel = WL1251_DEFAULT_CHANNEL; + wl->monitor_present = false; + wl->joined = false; wl->scanning = false; + wl->bss_type = MAX_BSS_TYPE; wl->default_key = 0; wl->listen_int = 1; wl->rx_counter = 0; @@ -1413,6 +1546,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void) wl->elp = false; wl->station_mode = STATION_ACTIVE_MODE; wl->psm_requested = false; + wl->psm_entry_retry = 0; wl->tx_queue_stopped = false; wl->power_level = WL1251_DEFAULT_POWER_LEVEL; wl->rssi_thold = 0; @@ -1478,3 +1612,4 @@ MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>"); MODULE_FIRMWARE(WL1251_FW_NAME); +MODULE_FIRMWARE(WL1251_NVS_NAME); diff --git a/drivers/net/wireless/ti/wl1251/rx.c b/drivers/net/wireless/ti/wl1251/rx.c index 23289d49dd3..cde0eaf9971 100644 --- a/drivers/net/wireless/ti/wl1251/rx.c +++ b/drivers/net/wireless/ti/wl1251/rx.c @@ -83,7 +83,7 @@ static void wl1251_rx_status(struct wl1251 *wl, status->flag |= RX_FLAG_MACTIME_START; - if (desc->flags & RX_DESC_ENCRYPTION_MASK) { + if (!wl->monitor_present && (desc->flags & RX_DESC_ENCRYPTION_MASK)) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; if (likely(!(desc->flags & RX_DESC_DECRYPT_FAIL))) @@ -180,7 +180,7 @@ static void wl1251_rx_body(struct wl1251 *wl, wl1251_mem_read(wl, rx_packet_ring_addr, rx_buffer, length); /* The actual length doesn't include the target's alignment */ - skb->len = desc->length - PLCP_HEADER_LENGTH; + skb_trim(skb, desc->length - PLCP_HEADER_LENGTH); fc = (u16 *)skb->data; diff --git a/drivers/net/wireless/ti/wl1251/sdio.c b/drivers/net/wireless/ti/wl1251/sdio.c index e2b3d9c541e..b661f896e9f 100644 --- a/drivers/net/wireless/ti/wl1251/sdio.c +++ b/drivers/net/wireless/ti/wl1251/sdio.c @@ -28,6 +28,7 @@ #include <linux/wl12xx.h> #include <linux/irq.h> #include <linux/pm_runtime.h> +#include <linux/gpio.h> #include "wl1251.h" @@ -182,8 +183,9 @@ static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable) * callback in case it wants to do any additional setup, * for example enabling clock buffer for the module. */ - if (wl->set_power) - wl->set_power(true); + if (gpio_is_valid(wl->power_gpio)) + gpio_set_value(wl->power_gpio, true); + ret = pm_runtime_get_sync(&func->dev); if (ret < 0) { @@ -203,8 +205,8 @@ static int wl1251_sdio_set_power(struct wl1251 *wl, bool enable) if (ret < 0) goto out; - if (wl->set_power) - wl->set_power(false); + if (gpio_is_valid(wl->power_gpio)) + gpio_set_value(wl->power_gpio, false); } out: @@ -227,7 +229,7 @@ static int wl1251_sdio_probe(struct sdio_func *func, struct wl1251 *wl; struct ieee80211_hw *hw; struct wl1251_sdio *wl_sdio; - const struct wl12xx_platform_data *wl12xx_board_data; + const struct wl1251_platform_data *wl1251_board_data; hw = wl1251_alloc_hw(); if (IS_ERR(hw)) @@ -254,11 +256,20 @@ static int wl1251_sdio_probe(struct sdio_func *func, wl->if_priv = wl_sdio; wl->if_ops = &wl1251_sdio_ops; - wl12xx_board_data = wl12xx_get_platform_data(); - if (!IS_ERR(wl12xx_board_data)) { - wl->set_power = wl12xx_board_data->set_power; - wl->irq = wl12xx_board_data->irq; - wl->use_eeprom = wl12xx_board_data->use_eeprom; + wl1251_board_data = wl1251_get_platform_data(); + if (!IS_ERR(wl1251_board_data)) { + wl->power_gpio = wl1251_board_data->power_gpio; + wl->irq = wl1251_board_data->irq; + wl->use_eeprom = wl1251_board_data->use_eeprom; + } + + if (gpio_is_valid(wl->power_gpio)) { + ret = devm_gpio_request(&func->dev, wl->power_gpio, + "wl1251 power"); + if (ret) { + wl1251_error("Failed to request gpio: %d\n", ret); + goto disable; + } } if (wl->irq) { diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index c7dc6feab2f..a0aa8fa7239 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -23,9 +23,14 @@ #include <linux/irq.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/swab.h> #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/wl12xx.h> +#include <linux/gpio.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/regulator/consumer.h> #include "wl1251.h" #include "reg.h" @@ -79,47 +84,44 @@ static void wl1251_spi_reset(struct wl1251 *wl) static void wl1251_spi_wake(struct wl1251 *wl) { - u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; struct spi_message m; + u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); - cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { wl1251_error("could not allocate cmd for spi init"); return; } - memset(crc, 0, sizeof(crc)); memset(&t, 0, sizeof(t)); spi_message_init(&m); /* Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ - cmd[2] = 0xff; - cmd[3] = 0xff; - cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; - cmd[0] = 0; - cmd[7] = 0; - cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; - cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + cmd[0] = 0xff; + cmd[1] = 0xff; + cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; + cmd[3] = 0; + cmd[4] = 0; + cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3; + cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + + cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS + | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) - cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; + cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; else - cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - - cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS - | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; - - crc[0] = cmd[1]; - crc[1] = cmd[0]; - crc[2] = cmd[7]; - crc[3] = cmd[6]; - crc[4] = cmd[5]; + cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; - cmd[4] |= WSPI_INIT_CMD_END; + cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; + /* + * The above is the logical order; it must actually be stored + * in the buffer byte-swapped. + */ + __swab32s((u32 *)cmd); + __swab32s((u32 *)cmd+1); t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; @@ -221,8 +223,8 @@ static void wl1251_spi_disable_irq(struct wl1251 *wl) static int wl1251_spi_set_power(struct wl1251 *wl, bool enable) { - if (wl->set_power) - wl->set_power(enable); + if (gpio_is_valid(wl->power_gpio)) + gpio_set_value(wl->power_gpio, enable); return 0; } @@ -238,13 +240,13 @@ static const struct wl1251_if_operations wl1251_spi_ops = { static int wl1251_spi_probe(struct spi_device *spi) { - struct wl12xx_platform_data *pdata; + struct wl1251_platform_data *pdata = dev_get_platdata(&spi->dev); + struct device_node *np = spi->dev.of_node; struct ieee80211_hw *hw; struct wl1251 *wl; int ret; - pdata = spi->dev.platform_data; - if (!pdata) { + if (!np && !pdata) { wl1251_error("no platform data"); return -ENODEV; } @@ -271,22 +273,42 @@ static int wl1251_spi_probe(struct spi_device *spi) goto out_free; } - wl->set_power = pdata->set_power; - if (!wl->set_power) { - wl1251_error("set power function missing in platform data"); - return -ENODEV; + if (np) { + wl->use_eeprom = of_property_read_bool(np, "ti,wl1251-has-eeprom"); + wl->power_gpio = of_get_named_gpio(np, "ti,power-gpio", 0); + } else if (pdata) { + wl->power_gpio = pdata->power_gpio; + wl->use_eeprom = pdata->use_eeprom; + } + + if (wl->power_gpio == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto out_free; + } + + if (gpio_is_valid(wl->power_gpio)) { + ret = devm_gpio_request_one(&spi->dev, wl->power_gpio, + GPIOF_OUT_INIT_LOW, "wl1251 power"); + if (ret) { + wl1251_error("Failed to request gpio: %d\n", ret); + goto out_free; + } + } else { + wl1251_error("set power gpio missing in platform data"); + ret = -ENODEV; + goto out_free; } wl->irq = spi->irq; if (wl->irq < 0) { wl1251_error("irq missing in platform data"); - return -ENODEV; + ret = -ENODEV; + goto out_free; } - wl->use_eeprom = pdata->use_eeprom; - irq_set_status_flags(wl->irq, IRQ_NOAUTOEN); - ret = request_irq(wl->irq, wl1251_irq, 0, DRIVER_NAME, wl); + ret = devm_request_irq(&spi->dev, wl->irq, wl1251_irq, 0, + DRIVER_NAME, wl); if (ret < 0) { wl1251_error("request_irq() failed: %d", ret); goto out_free; @@ -294,16 +316,26 @@ static int wl1251_spi_probe(struct spi_device *spi) irq_set_irq_type(wl->irq, IRQ_TYPE_EDGE_RISING); + wl->vio = devm_regulator_get(&spi->dev, "vio"); + if (IS_ERR(wl->vio)) { + ret = PTR_ERR(wl->vio); + wl1251_error("vio regulator missing: %d", ret); + goto out_free; + } + + ret = regulator_enable(wl->vio); + if (ret) + goto out_free; + ret = wl1251_init_ieee80211(wl); if (ret) - goto out_irq; + goto disable_regulator; return 0; - out_irq: - free_irq(wl->irq, wl); - - out_free: +disable_regulator: + regulator_disable(wl->vio); +out_free: ieee80211_free_hw(hw); return ret; @@ -315,6 +347,7 @@ static int wl1251_spi_remove(struct spi_device *spi) free_irq(wl->irq, wl); wl1251_free_hw(wl); + regulator_disable(wl->vio); return 0; } diff --git a/drivers/net/wireless/ti/wl1251/tx.c b/drivers/net/wireless/ti/wl1251/tx.c index 28121c590a2..81de83c6fcf 100644 --- a/drivers/net/wireless/ti/wl1251/tx.c +++ b/drivers/net/wireless/ti/wl1251/tx.c @@ -28,6 +28,7 @@ #include "tx.h" #include "ps.h" #include "io.h" +#include "event.h" static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count) { @@ -89,8 +90,12 @@ static void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr, /* 802.11 packets */ tx_hdr->control.packet_type = 0; - if (control->flags & IEEE80211_TX_CTL_NO_ACK) + /* Also disable retry and ACK policy for injected packets */ + if ((control->flags & IEEE80211_TX_CTL_NO_ACK) || + (control->flags & IEEE80211_TX_CTL_INJECTED)) { + tx_hdr->control.rate_policy = 1; tx_hdr->control.ack_policy = 1; + } tx_hdr->control.tx_complete = 1; @@ -277,6 +282,26 @@ static void wl1251_tx_trigger(struct wl1251 *wl) TX_STATUS_DATA_OUT_COUNT_MASK; } +static void enable_tx_for_packet_injection(struct wl1251 *wl) +{ + int ret; + + ret = wl1251_cmd_join(wl, BSS_TYPE_STA_BSS, wl->channel, + wl->beacon_int, wl->dtim_period); + if (ret < 0) { + wl1251_warning("join failed"); + return; + } + + ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100); + if (ret < 0) { + wl1251_warning("join timeout"); + return; + } + + wl->joined = true; +} + /* caller must hold wl->mutex */ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) { @@ -287,6 +312,9 @@ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) info = IEEE80211_SKB_CB(skb); if (info->control.hw_key) { + if (unlikely(wl->monitor_present)) + return -EINVAL; + idx = info->control.hw_key->hw_key_idx; if (unlikely(wl->default_key != idx)) { ret = wl1251_acx_default_key(wl, idx); @@ -295,6 +323,10 @@ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) } } + /* Enable tx path in monitor mode for packet injection */ + if ((wl->vif == NULL) && !wl->joined) + enable_tx_for_packet_injection(wl); + ret = wl1251_tx_path_status(wl); if (ret < 0) return ret; @@ -394,6 +426,7 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl, info = IEEE80211_SKB_CB(skb); if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && + !(info->flags & IEEE80211_TX_CTL_INJECTED) && (result->status == TX_SUCCESS)) info->flags |= IEEE80211_TX_STAT_ACK; diff --git a/drivers/net/wireless/ti/wl1251/wl1251.h b/drivers/net/wireless/ti/wl1251/wl1251.h index fd02060038d..16dae526917 100644 --- a/drivers/net/wireless/ti/wl1251/wl1251.h +++ b/drivers/net/wireless/ti/wl1251/wl1251.h @@ -93,6 +93,7 @@ enum { } while (0) #define WL1251_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN | \ + CFG_MC_FILTER_EN | \ CFG_BSSID_FILTER_EN) #define WL1251_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN | \ @@ -275,10 +276,12 @@ struct wl1251 { void *if_priv; const struct wl1251_if_operations *if_ops; - void (*set_power)(bool enable); + int power_gpio; int irq; bool use_eeprom; + struct regulator *vio; + spinlock_t wl_lock; enum wl1251_state state; @@ -303,6 +306,8 @@ struct wl1251 { u8 bss_type; u8 listen_int; int channel; + bool monitor_present; + bool joined; void *target_mem_map; struct acx_data_path_params_resp *data_path; @@ -368,6 +373,9 @@ struct wl1251 { /* PSM mode requested */ bool psm_requested; + /* retry counter for PSM entries */ + u8 psm_entry_retry; + u16 beacon_int; u8 dtim_period; @@ -424,8 +432,8 @@ void wl1251_disable_interrupts(struct wl1251 *wl); #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) -#define WL1251_FW_NAME "wl1251-fw.bin" -#define WL1251_NVS_NAME "wl1251-nvs.bin" +#define WL1251_FW_NAME "ti-connectivity/wl1251-fw.bin" +#define WL1251_NVS_NAME "ti-connectivity/wl1251-nvs.bin" #define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */ diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 1c627da8508..d50dfac9163 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -333,11 +333,11 @@ static struct wlcore_conf wl12xx_conf = { .always = 0, }, .fwlog = { - .mode = WL12XX_FWLOG_ON_DEMAND, + .mode = WL12XX_FWLOG_CONTINUOUS, .mem_blocks = 2, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, - .output = WL12XX_FWLOG_OUTPUT_HOST, + .output = WL12XX_FWLOG_OUTPUT_DBG_PINS, .threshold = 0, }, .rate = { @@ -717,6 +717,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl) goto out; } + wl->fw_mem_block_size = 256; + wl->fwlog_end = 0x2000000; + /* common settings */ wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY; wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY; @@ -1262,9 +1265,10 @@ static int wl12xx_boot(struct wl1271 *wl) BA_SESSION_RX_CONSTRAINT_EVENT_ID | REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | - MAX_TX_RETRY_EVENT_ID | CHANNEL_SWITCH_COMPLETE_EVENT_ID; + wl->ap_event_mask = MAX_TX_RETRY_EVENT_ID; + ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; @@ -1374,7 +1378,7 @@ static u32 wl12xx_get_rx_packet_len(struct wl1271 *wl, void *rx_data, static int wl12xx_tx_delayed_compl(struct wl1271 *wl) { - if (wl->fw_status_1->tx_results_counter == + if (wl->fw_status->tx_results_counter == (wl->tx_results_count & 0xff)) return 0; @@ -1434,6 +1438,37 @@ out: return ret; } +static void wl12xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, + struct wl_fw_status *fw_status) +{ + struct wl12xx_fw_status *int_fw_status = raw_fw_status; + + fw_status->intr = le32_to_cpu(int_fw_status->intr); + fw_status->fw_rx_counter = int_fw_status->fw_rx_counter; + fw_status->drv_rx_counter = int_fw_status->drv_rx_counter; + fw_status->tx_results_counter = int_fw_status->tx_results_counter; + fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs; + + fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime); + fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap); + fw_status->link_fast_bitmap = + le32_to_cpu(int_fw_status->link_fast_bitmap); + fw_status->total_released_blks = + le32_to_cpu(int_fw_status->total_released_blks); + fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total); + + fw_status->counters.tx_released_pkts = + int_fw_status->counters.tx_released_pkts; + fw_status->counters.tx_lnk_free_pkts = + int_fw_status->counters.tx_lnk_free_pkts; + fw_status->counters.tx_voice_released_blks = + int_fw_status->counters.tx_voice_released_blks; + fw_status->counters.tx_last_rate = + int_fw_status->counters.tx_last_rate; + + fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr); +} + static u32 wl12xx_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { @@ -1648,6 +1683,11 @@ static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, return true; } +static u32 wl12xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr) +{ + return hwaddr << 5; +} + static int wl12xx_setup(struct wl1271 *wl); static struct wlcore_ops wl12xx_ops = { @@ -1668,6 +1708,7 @@ static struct wlcore_ops wl12xx_ops = { .tx_delayed_compl = wl12xx_tx_delayed_compl, .hw_init = wl12xx_hw_init, .init_vif = NULL, + .convert_fw_status = wl12xx_convert_fw_status, .sta_get_ap_rate_mask = wl12xx_sta_get_ap_rate_mask, .get_pg_ver = wl12xx_get_pg_ver, .get_mac = wl12xx_get_mac, @@ -1684,6 +1725,7 @@ static struct wlcore_ops wl12xx_ops = { .channel_switch = wl12xx_cmd_channel_switch, .pre_pkt_send = NULL, .set_peer_cap = wl12xx_set_peer_cap, + .convert_hwaddr = wl12xx_convert_hwaddr, .lnk_high_prio = wl12xx_lnk_high_prio, .lnk_low_prio = wl12xx_lnk_low_prio, }; @@ -1701,22 +1743,53 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { }, }; +static const struct ieee80211_iface_limit wl12xx_iface_limits[] = { + { + .max = 3, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +}; + +static const struct ieee80211_iface_combination +wl12xx_iface_combinations[] = { + { + .max_interfaces = 3, + .limits = wl12xx_iface_limits, + .n_limits = ARRAY_SIZE(wl12xx_iface_limits), + .num_different_channels = 1, + }, +}; + static int wl12xx_setup(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; - struct wlcore_platdev_data *pdev_data = wl->pdev->dev.platform_data; + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev); struct wl12xx_platform_data *pdata = pdev_data->pdata; + BUILD_BUG_ON(WL12XX_MAX_LINKS > WLCORE_MAX_LINKS); + BUILD_BUG_ON(WL12XX_MAX_AP_STATIONS > WL12XX_MAX_LINKS); + wl->rtable = wl12xx_rtable; wl->num_tx_desc = WL12XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL12XX_NUM_RX_DESCRIPTORS; - wl->num_channels = 1; + wl->num_links = WL12XX_MAX_LINKS; + wl->max_ap_stations = WL12XX_MAX_AP_STATIONS; + wl->iface_combinations = wl12xx_iface_combinations; + wl->n_iface_combinations = ARRAY_SIZE(wl12xx_iface_combinations); wl->num_mac_addr = WL12XX_NUM_MAC_ADDRESSES; wl->band_rate_to_idx = wl12xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL12XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL12XX_CONF_HW_RXTX_RATE_MCS0; + wl->fw_status_len = sizeof(struct wl12xx_fw_status); wl->fw_status_priv_len = 0; wl->stats.fw_stats_len = sizeof(struct wl12xx_acx_statistics); + wl->ofdm_only_ap = true; wlcore_set_ht_cap(wl, IEEE80211_BAND_2GHZ, &wl12xx_ht_cap); wlcore_set_ht_cap(wl, IEEE80211_BAND_5GHZ, &wl12xx_ht_cap); wl12xx_conf_init(wl); diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c index 4a0bbb13806..7541bd1a4a4 100644 --- a/drivers/net/wireless/ti/wl12xx/scan.c +++ b/drivers/net/wireless/ti/wl12xx/scan.c @@ -47,7 +47,7 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, * In active scans, we only scan channels not * marked as passive. */ - (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { + (passive || !(flags & IEEE80211_CHAN_NO_IR))) { wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", req->channels[i]->band, req->channels[i]->center_freq); diff --git a/drivers/net/wireless/ti/wl12xx/wl12xx.h b/drivers/net/wireless/ti/wl12xx/wl12xx.h index 9e5484a7366..75c92658bfe 100644 --- a/drivers/net/wireless/ti/wl12xx/wl12xx.h +++ b/drivers/net/wireless/ti/wl12xx/wl12xx.h @@ -65,6 +65,9 @@ #define WL12XX_RX_BA_MAX_SESSIONS 3 +#define WL12XX_MAX_AP_STATIONS 8 +#define WL12XX_MAX_LINKS 12 + struct wl127x_rx_mem_pool_addr { u32 addr; u32 addr_extra; @@ -79,4 +82,54 @@ struct wl12xx_priv { struct wl127x_rx_mem_pool_addr *rx_mem_addr; }; +struct wl12xx_fw_packet_counters { + /* Cumulative counter of released packets per AC */ + u8 tx_released_pkts[NUM_TX_QUEUES]; + + /* Cumulative counter of freed packets per HLID */ + u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; + + /* Cumulative counter of released Voice memory blocks */ + u8 tx_voice_released_blks; + + /* Tx rate of the last transmitted packet */ + u8 tx_last_rate; + + u8 padding[2]; +} __packed; + +/* FW status registers */ +struct wl12xx_fw_status { + __le32 intr; + u8 fw_rx_counter; + u8 drv_rx_counter; + u8 reserved; + u8 tx_results_counter; + __le32 rx_pkt_descs[WL12XX_NUM_RX_DESCRIPTORS]; + + __le32 fw_localtime; + + /* + * A bitmap (where each bit represents a single HLID) + * to indicate if the station is in PS mode. + */ + __le32 link_ps_bitmap; + + /* + * A bitmap (where each bit represents a single HLID) to indicate + * if the station is in Fast mode + */ + __le32 link_fast_bitmap; + + /* Cumulative counter of total released mem blocks since FW-reset */ + __le32 total_released_blks; + + /* Size (in Memory Blocks) of TX pool */ + __le32 tx_total; + + struct wl12xx_fw_packet_counters counters; + + __le32 log_start_addr; +} __packed; + #endif /* __WL12XX_PRIV_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index 398f3d2c0a6..a76e98eb837 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -68,6 +68,26 @@ struct wl18xx_event_mailbox { /* bitmap of inactive stations (by HLID) */ __le32 inactive_sta_bitmap; + + /* rx BA win size indicated by RX_BA_WIN_SIZE_CHANGE_EVENT_ID */ + u8 rx_ba_role_id; + u8 rx_ba_link_id; + u8 rx_ba_win_size; + u8 padding; + + /* smart config */ + u8 sc_ssid_len; + u8 sc_pwd_len; + u8 sc_token_len; + u8 padding1; + u8 sc_ssid[32]; + u8 sc_pwd[32]; + u8 sc_token[32]; + + /* smart config sync channel */ + u8 sc_sync_channel; + u8 sc_sync_band; + u8 padding2[2]; } __packed; int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 7aa0eb848c5..de5b4fa5d16 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -456,11 +456,11 @@ static struct wlcore_conf wl18xx_conf = { .always = 0, }, .fwlog = { - .mode = WL12XX_FWLOG_ON_DEMAND, + .mode = WL12XX_FWLOG_CONTINUOUS, .mem_blocks = 2, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, - .output = WL12XX_FWLOG_OUTPUT_HOST, + .output = WL12XX_FWLOG_OUTPUT_DBG_PINS, .threshold = 0, }, .rate = { @@ -505,7 +505,7 @@ static struct wlcore_conf wl18xx_conf = { static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .ht = { - .mode = HT_MODE_DEFAULT, + .mode = HT_MODE_WIDE, }, .phy = { .phy_standalone = 0x00, @@ -516,7 +516,7 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .auto_detect = 0x00, .dedicated_fem = FEM_NONE, .low_band_component = COMPONENT_3_WAY_SWITCH, - .low_band_component_type = 0x04, + .low_band_component_type = 0x05, .high_band_component = COMPONENT_2_WAY_SWITCH, .high_band_component_type = 0x09, .tcxo_ldo_voltage = 0x00, @@ -556,15 +556,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .per_chan_pwr_limit_arr_11p = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, .psat = 0, - .low_power_val = 0x08, - .med_power_val = 0x12, - .high_power_val = 0x18, - .low_power_val_2nd = 0x05, - .med_power_val_2nd = 0x0a, - .high_power_val_2nd = 0x14, .external_pa_dc2dc = 0, .number_of_assembled_ant2_4 = 2, .number_of_assembled_ant5 = 1, + .low_power_val = 0xff, + .med_power_val = 0xff, + .high_power_val = 0xff, + .low_power_val_2nd = 0xff, + .med_power_val_2nd = 0xff, + .high_power_val_2nd = 0xff, .tx_rf_margin = 1, }, }; @@ -623,6 +623,18 @@ static const int wl18xx_rtable[REG_TABLE_LEN] = { [REG_RAW_FW_STATUS_ADDR] = WL18XX_FW_STATUS_ADDR, }; +static const struct wl18xx_clk_cfg wl18xx_clk_table_coex[NUM_CLOCK_CONFIGS] = { + [CLOCK_CONFIG_16_2_M] = { 8, 121, 0, 0, false }, + [CLOCK_CONFIG_16_368_M] = { 8, 120, 0, 0, false }, + [CLOCK_CONFIG_16_8_M] = { 8, 117, 0, 0, false }, + [CLOCK_CONFIG_19_2_M] = { 10, 128, 0, 0, false }, + [CLOCK_CONFIG_26_M] = { 11, 104, 0, 0, false }, + [CLOCK_CONFIG_32_736_M] = { 8, 120, 0, 0, false }, + [CLOCK_CONFIG_33_6_M] = { 8, 117, 0, 0, false }, + [CLOCK_CONFIG_38_468_M] = { 10, 128, 0, 0, false }, + [CLOCK_CONFIG_52_M] = { 11, 104, 0, 0, false }, +}; + static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { [CLOCK_CONFIG_16_2_M] = { 7, 104, 801, 4, true }, [CLOCK_CONFIG_16_368_M] = { 9, 132, 3751, 4, true }, @@ -636,7 +648,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { }; /* TODO: maybe move to a new header file? */ -#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-2.bin" +#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin" static int wl18xx_identify_chip(struct wl1271 *wl) { @@ -674,6 +686,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl) goto out; } + wl->fw_mem_block_size = 272; + wl->fwlog_end = 0x40000000; + wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC; @@ -704,6 +719,23 @@ static int wl18xx_set_clk(struct wl1271 *wl) wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q, wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit"); + /* coex PLL configuration */ + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_N, + wl18xx_clk_table_coex[clk_freq].n); + if (ret < 0) + goto out; + + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_M, + wl18xx_clk_table_coex[clk_freq].m); + if (ret < 0) + goto out; + + /* bypass the swallowing logic */ + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_SWALLOW_EN, + PLLSH_COEX_PLL_SWALLOW_EN_VAL1); + if (ret < 0) + goto out; + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n); if (ret < 0) @@ -745,6 +777,30 @@ static int wl18xx_set_clk(struct wl1271 *wl) PLLSH_WCS_PLL_SWALLOW_EN_VAL2); } + /* choose WCS PLL */ + ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_SEL, + PLLSH_WL_PLL_SEL_WCS_PLL); + if (ret < 0) + goto out; + + /* enable both PLLs */ + ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_EN, PLLSH_WL_PLL_EN_VAL1); + if (ret < 0) + goto out; + + udelay(1000); + + /* disable coex PLL */ + ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_EN, PLLSH_WL_PLL_EN_VAL2); + if (ret < 0) + goto out; + + /* reset the swallowing logic */ + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_SWALLOW_EN, + PLLSH_COEX_PLL_SWALLOW_EN_VAL2); + if (ret < 0) + goto out; + out: return ret; } @@ -935,10 +991,11 @@ static int wl18xx_boot(struct wl1271 *wl) BA_SESSION_RX_CONSTRAINT_EVENT_ID | REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | - MAX_TX_FAILURE_EVENT_ID | CHANNEL_SWITCH_COMPLETE_EVENT_ID | DFS_CHANNELS_CONFIG_COMPLETE_EVENT; + wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; + ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; @@ -1076,6 +1133,39 @@ static int wl18xx_hw_init(struct wl1271 *wl) return ret; } +static void wl18xx_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, + struct wl_fw_status *fw_status) +{ + struct wl18xx_fw_status *int_fw_status = raw_fw_status; + + fw_status->intr = le32_to_cpu(int_fw_status->intr); + fw_status->fw_rx_counter = int_fw_status->fw_rx_counter; + fw_status->drv_rx_counter = int_fw_status->drv_rx_counter; + fw_status->tx_results_counter = int_fw_status->tx_results_counter; + fw_status->rx_pkt_descs = int_fw_status->rx_pkt_descs; + + fw_status->fw_localtime = le32_to_cpu(int_fw_status->fw_localtime); + fw_status->link_ps_bitmap = le32_to_cpu(int_fw_status->link_ps_bitmap); + fw_status->link_fast_bitmap = + le32_to_cpu(int_fw_status->link_fast_bitmap); + fw_status->total_released_blks = + le32_to_cpu(int_fw_status->total_released_blks); + fw_status->tx_total = le32_to_cpu(int_fw_status->tx_total); + + fw_status->counters.tx_released_pkts = + int_fw_status->counters.tx_released_pkts; + fw_status->counters.tx_lnk_free_pkts = + int_fw_status->counters.tx_lnk_free_pkts; + fw_status->counters.tx_voice_released_blks = + int_fw_status->counters.tx_voice_released_blks; + fw_status->counters.tx_last_rate = + int_fw_status->counters.tx_last_rate; + + fw_status->log_start_addr = le32_to_cpu(int_fw_status->log_start_addr); + + fw_status->priv = &int_fw_status->priv; +} + static void wl18xx_set_tx_desc_csum(struct wl1271 *wl, struct wl1271_tx_hw_descr *desc, struct sk_buff *skb) @@ -1175,16 +1265,48 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, } } +static const char *wl18xx_rdl_name(enum wl18xx_rdl_num rdl_num) +{ + switch (rdl_num) { + case RDL_1_HP: + return "183xH"; + case RDL_2_SP: + return "183x or 180x"; + case RDL_3_HP: + return "187xH"; + case RDL_4_SP: + return "187x"; + case RDL_5_SP: + return "RDL11 - Not Supported"; + case RDL_6_SP: + return "180xD"; + case RDL_7_SP: + return "RDL13 - Not Supported (1893Q)"; + case RDL_8_SP: + return "18xxQ"; + case RDL_NONE: + return "UNTRIMMED"; + default: + return "UNKNOWN"; + } +} + static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) { u32 fuse; - s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0; + s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0, package_type = 0; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); if (ret < 0) goto out; + ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_2_3, &fuse); + if (ret < 0) + goto out; + + package_type = (fuse >> WL18XX_PACKAGE_TYPE_OFFSET) & 1; + ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_1_3, &fuse); if (ret < 0) goto out; @@ -1192,7 +1314,7 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) pg_ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; rom = (fuse & WL18XX_ROM_VER_MASK) >> WL18XX_ROM_VER_OFFSET; - if (rom <= 0xE) + if ((rom <= 0xE) && (package_type == WL18XX_PACKAGE_TYPE_WSP)) metal = (fuse & WL18XX_METAL_VER_MASK) >> WL18XX_METAL_VER_OFFSET; else @@ -1204,11 +1326,9 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) goto out; rdl_ver = (fuse & WL18XX_RDL_VER_MASK) >> WL18XX_RDL_VER_OFFSET; - if (rdl_ver > RDL_MAX) - rdl_ver = RDL_NONE; - wl1271_info("wl18xx HW: RDL %d, %s, PG %x.%x (ROM %x)", - rdl_ver, rdl_names[rdl_ver], pg_ver, metal, rom); + wl1271_info("wl18xx HW: %s, PG %d.%d (ROM 0x%x)", + wl18xx_rdl_name(rdl_ver), pg_ver, metal, rom); if (ver) *ver = pg_ver; @@ -1485,7 +1605,7 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, { u8 thold; struct wl18xx_fw_status_priv *status_priv = - (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; + (struct wl18xx_fw_status_priv *)wl->fw_status->priv; u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); /* suspended links are never high priority */ @@ -1507,7 +1627,7 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, { u8 thold; struct wl18xx_fw_status_priv *status_priv = - (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; + (struct wl18xx_fw_status_priv *)wl->fw_status->priv; u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) @@ -1521,6 +1641,11 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, return lnk->allocated_pkts < thold; } +static u32 wl18xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr) +{ + return hwaddr & ~0x80000000; +} + static int wl18xx_setup(struct wl1271 *wl); static struct wlcore_ops wl18xx_ops = { @@ -1540,6 +1665,7 @@ static struct wlcore_ops wl18xx_ops = { .tx_immediate_compl = wl18xx_tx_immediate_completion, .tx_delayed_compl = NULL, .hw_init = wl18xx_hw_init, + .convert_fw_status = wl18xx_convert_fw_status, .set_tx_desc_csum = wl18xx_set_tx_desc_csum, .get_pg_ver = wl18xx_get_pg_ver, .set_rx_csum = wl18xx_set_rx_csum, @@ -1558,6 +1684,7 @@ static struct wlcore_ops wl18xx_ops = { .pre_pkt_send = wl18xx_pre_pkt_send, .sta_rc_update = wl18xx_sta_rc_update, .set_peer_cap = wl18xx_set_peer_cap, + .convert_hwaddr = wl18xx_convert_hwaddr, .lnk_high_prio = wl18xx_lnk_high_prio, .lnk_low_prio = wl18xx_lnk_low_prio, }; @@ -1620,19 +1747,62 @@ static struct ieee80211_sta_ht_cap wl18xx_mimo_ht_cap_2ghz = { }, }; +static const struct ieee80211_iface_limit wl18xx_iface_limits[] = { + { + .max = 3, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +}; + +static const struct ieee80211_iface_limit wl18xx_iface_ap_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_combination +wl18xx_iface_combinations[] = { + { + .max_interfaces = 3, + .limits = wl18xx_iface_limits, + .n_limits = ARRAY_SIZE(wl18xx_iface_limits), + .num_different_channels = 2, + }, + { + .max_interfaces = 2, + .limits = wl18xx_iface_ap_limits, + .n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits), + .num_different_channels = 1, + } +}; + static int wl18xx_setup(struct wl1271 *wl) { struct wl18xx_priv *priv = wl->priv; int ret; + BUILD_BUG_ON(WL18XX_MAX_LINKS > WLCORE_MAX_LINKS); + BUILD_BUG_ON(WL18XX_MAX_AP_STATIONS > WL18XX_MAX_LINKS); + wl->rtable = wl18xx_rtable; wl->num_tx_desc = WL18XX_NUM_TX_DESCRIPTORS; wl->num_rx_desc = WL18XX_NUM_RX_DESCRIPTORS; - wl->num_channels = 2; + wl->num_links = WL18XX_MAX_LINKS; + wl->max_ap_stations = WL18XX_MAX_AP_STATIONS; + wl->iface_combinations = wl18xx_iface_combinations; + wl->n_iface_combinations = ARRAY_SIZE(wl18xx_iface_combinations); wl->num_mac_addr = WL18XX_NUM_MAC_ADDRESSES; wl->band_rate_to_idx = wl18xx_band_rate_to_idx; wl->hw_tx_rate_tbl_size = WL18XX_CONF_HW_RXTX_RATE_MAX; wl->hw_min_ht_rate = WL18XX_CONF_HW_RXTX_RATE_MCS0; + wl->fw_status_len = sizeof(struct wl18xx_fw_status); wl->fw_status_priv_len = sizeof(struct wl18xx_fw_status_priv); wl->stats.fw_stats_len = sizeof(struct wl18xx_acx_statistics); wl->static_data_priv_len = sizeof(struct wl18xx_static_data_priv); diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 05dd8bad274..a433a75f3cd 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -114,6 +114,11 @@ #define PLATFORM_DETECTION 0xA0E3E0 #define OCS_EN 0xA02080 #define PRIMARY_CLK_DETECT 0xA020A6 +#define PLLSH_COEX_PLL_N 0xA02384 +#define PLLSH_COEX_PLL_M 0xA02382 +#define PLLSH_COEX_PLL_SWALLOW_EN 0xA0238E +#define PLLSH_WL_PLL_SEL 0xA02398 + #define PLLSH_WCS_PLL_N 0xA02362 #define PLLSH_WCS_PLL_M 0xA02360 #define PLLSH_WCS_PLL_Q_FACTOR_CFG_1 0xA02364 @@ -128,19 +133,30 @@ #define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK 0xFFFF #define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK 0x000F +#define PLLSH_WL_PLL_EN_VAL1 0x7 +#define PLLSH_WL_PLL_EN_VAL2 0x2 +#define PLLSH_COEX_PLL_SWALLOW_EN_VAL1 0x2 +#define PLLSH_COEX_PLL_SWALLOW_EN_VAL2 0x11 + #define PLLSH_WCS_PLL_SWALLOW_EN_VAL1 0x1 #define PLLSH_WCS_PLL_SWALLOW_EN_VAL2 0x12 +#define PLLSH_WL_PLL_SEL_WCS_PLL 0x0 +#define PLLSH_WL_PLL_SEL_COEX_PLL 0x1 + #define WL18XX_REG_FUSE_DATA_1_3 0xA0260C #define WL18XX_PG_VER_MASK 0x70 #define WL18XX_PG_VER_OFFSET 4 -#define WL18XX_ROM_VER_MASK 0x3 -#define WL18XX_ROM_VER_OFFSET 0 +#define WL18XX_ROM_VER_MASK 0x3e00 +#define WL18XX_ROM_VER_OFFSET 9 #define WL18XX_METAL_VER_MASK 0xC #define WL18XX_METAL_VER_OFFSET 2 #define WL18XX_NEW_METAL_VER_MASK 0x180 #define WL18XX_NEW_METAL_VER_OFFSET 7 +#define WL18XX_PACKAGE_TYPE_OFFSET 13 +#define WL18XX_PACKAGE_TYPE_WSP 0 + #define WL18XX_REG_FUSE_DATA_2_3 0xA02614 #define WL18XX_RDL_VER_MASK 0x1f00 #define WL18XX_RDL_VER_OFFSET 8 @@ -201,24 +217,21 @@ enum { NUM_BOARD_TYPES, }; -enum { +enum wl18xx_rdl_num { RDL_NONE = 0, RDL_1_HP = 1, RDL_2_SP = 2, RDL_3_HP = 3, RDL_4_SP = 4, + RDL_5_SP = 0x11, + RDL_6_SP = 0x12, + RDL_7_SP = 0x13, + RDL_8_SP = 0x14, _RDL_LAST, RDL_MAX = _RDL_LAST - 1, }; -static const char * const rdl_names[] = { - [RDL_NONE] = "", - [RDL_1_HP] = "1853 SISO", - [RDL_2_SP] = "1857 MIMO", - [RDL_3_HP] = "1893 SISO", - [RDL_4_SP] = "1897 MIMO", -}; /* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */ #define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40 diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c index 57c69439664..be1ebd55ac8 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.c +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -32,7 +32,7 @@ static void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif, struct ieee80211_tx_rate *rate) { - u8 fw_rate = wl->fw_status_2->counters.tx_last_rate; + u8 fw_rate = wl->fw_status->counters.tx_last_rate; if (fw_rate > CONF_HW_RATE_INDEX_MAX) { wl1271_error("last Tx rate invalid: %d", fw_rate); @@ -139,7 +139,7 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) void wl18xx_tx_immediate_complete(struct wl1271 *wl) { struct wl18xx_fw_status_priv *status_priv = - (struct wl18xx_fw_status_priv *)wl->fw_status_2->priv; + (struct wl18xx_fw_status_priv *)wl->fw_status->priv; struct wl18xx_priv *priv = wl->priv; u8 i; diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 9204e07ee43..eb7cfe81701 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -26,10 +26,10 @@ /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 -#define WL18XX_IFTYPE_VER 5 +#define WL18XX_IFTYPE_VER 8 #define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE #define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE -#define WL18XX_MINOR_VER 39 +#define WL18XX_MINOR_VER 13 #define WL18XX_CMD_MAX_SIZE 740 @@ -40,7 +40,10 @@ #define WL18XX_NUM_MAC_ADDRESSES 3 -#define WL18XX_RX_BA_MAX_SESSIONS 5 +#define WL18XX_RX_BA_MAX_SESSIONS 13 + +#define WL18XX_MAX_AP_STATIONS 10 +#define WL18XX_MAX_LINKS 16 struct wl18xx_priv { /* buffer for sending commands to FW */ @@ -109,6 +112,59 @@ struct wl18xx_fw_status_priv { u8 padding[3]; }; +struct wl18xx_fw_packet_counters { + /* Cumulative counter of released packets per AC */ + u8 tx_released_pkts[NUM_TX_QUEUES]; + + /* Cumulative counter of freed packets per HLID */ + u8 tx_lnk_free_pkts[WL18XX_MAX_LINKS]; + + /* Cumulative counter of released Voice memory blocks */ + u8 tx_voice_released_blks; + + /* Tx rate of the last transmitted packet */ + u8 tx_last_rate; + + u8 padding[2]; +} __packed; + +/* FW status registers */ +struct wl18xx_fw_status { + __le32 intr; + u8 fw_rx_counter; + u8 drv_rx_counter; + u8 reserved; + u8 tx_results_counter; + __le32 rx_pkt_descs[WL18XX_NUM_RX_DESCRIPTORS]; + + __le32 fw_localtime; + + /* + * A bitmap (where each bit represents a single HLID) + * to indicate if the station is in PS mode. + */ + __le32 link_ps_bitmap; + + /* + * A bitmap (where each bit represents a single HLID) to indicate + * if the station is in Fast mode + */ + __le32 link_fast_bitmap; + + /* Cumulative counter of total released mem blocks since FW-reset */ + __le32 total_released_blks; + + /* Size (in Memory Blocks) of TX pool */ + __le32 tx_total; + + struct wl18xx_fw_packet_counters counters; + + __le32 log_start_addr; + + /* Private status to be used by the lower drivers */ + struct wl18xx_fw_status_priv priv; +} __packed; + #define WL18XX_PHY_VERSION_MAX_LEN 20 struct wl18xx_static_data_priv { diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index 7a970cd9c55..b924ceadc02 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -162,7 +162,8 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, wl1271_debug(DEBUG_ACX, "acx mem map"); - ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); + ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, + sizeof(struct acx_header), len); if (ret < 0) return ret; @@ -357,7 +358,8 @@ int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct acx_beacon_filter_option *beacon_filter = NULL; int ret = 0; - wl1271_debug(DEBUG_ACX, "acx beacon filter opt"); + wl1271_debug(DEBUG_ACX, "acx beacon filter opt enable=%d", + enable_filter); if (enable_filter && wl->conf.conn.bcn_filt_mode == CONF_BCN_FILT_MODE_DISABLED) @@ -722,6 +724,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, void *stats) wl1271_debug(DEBUG_ACX, "acx statistics"); ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats, + sizeof(struct acx_header), wl->stats.fw_stats_len); if (ret < 0) { wl1271_warning("acx statistics failed: %d", ret); @@ -1470,8 +1473,8 @@ int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, tsf_info->role_id = wlvif->role_id; - ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, - tsf_info, sizeof(*tsf_info)); + ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info, + sizeof(struct acx_header), sizeof(*tsf_info)); if (ret < 0) { wl1271_warning("acx tsf info interrogate failed"); goto out; @@ -1589,7 +1592,8 @@ out: return ret; } -int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) +int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 *addr) { struct wl1271_acx_inconnection_sta *acx = NULL; int ret; @@ -1601,6 +1605,7 @@ int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) return -ENOMEM; memcpy(acx->addr, addr, ETH_ALEN); + acx->role_id = wlvif->role_id; ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST, acx, sizeof(*acx)); @@ -1752,7 +1757,7 @@ int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif, acx->role_id = wlvif->role_id; ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL, - acx, sizeof(*acx)); + acx, sizeof(*acx), sizeof(*acx)); if (ret < 0) { wl1271_warning("acx roaming statistics failed: %d", ret); ret = -ENOMEM; diff --git a/drivers/net/wireless/ti/wlcore/acx.h b/drivers/net/wireless/ti/wlcore/acx.h index 6dcfad9b047..954d57ec98f 100644 --- a/drivers/net/wireless/ti/wlcore/acx.h +++ b/drivers/net/wireless/ti/wlcore/acx.h @@ -824,7 +824,8 @@ struct wl1271_acx_inconnection_sta { struct acx_header header; u8 addr[ETH_ALEN]; - u8 padding1[2]; + u8 role_id; + u8 padding; } __packed; /* @@ -1118,7 +1119,8 @@ int wl1271_acx_ps_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool enable); int wl1271_acx_ap_max_tx_retry(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl12xx_acx_config_ps(struct wl1271 *wl, struct wl12xx_vif *wlvif); -int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); +int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif, u8 *addr); int wl1271_acx_fm_coex(struct wl1271 *wl); int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl); int wl12xx_acx_config_hangover(struct wl1271 *wl); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index c9e060795d1..40dc30f4faa 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -60,7 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf, u16 status; u16 poll_count = 0; - if (WARN_ON(unlikely(wl->state == WLCORE_STATE_RESTARTING))) + if (unlikely(wl->state == WLCORE_STATE_RESTARTING && + id != CMD_STOP_FWLOGGER)) return -EIO; cmd = buf; @@ -311,8 +312,8 @@ static int wlcore_get_new_session_id(struct wl1271 *wl, u8 hlid) int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) { unsigned long flags; - u8 link = find_first_zero_bit(wl->links_map, WL12XX_MAX_LINKS); - if (link >= WL12XX_MAX_LINKS) + u8 link = find_first_zero_bit(wl->links_map, wl->num_links); + if (link >= wl->num_links) return -EBUSY; wl->session_ids[link] = wlcore_get_new_session_id(wl, link); @@ -323,9 +324,14 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) __set_bit(link, wlvif->links_map); spin_unlock_irqrestore(&wl->wl_lock, flags); - /* take the last "freed packets" value from the current FW status */ - wl->links[link].prev_freed_pkts = - wl->fw_status_2->counters.tx_lnk_free_pkts[link]; + /* + * take the last "freed packets" value from the current FW status. + * on recovery, we might not have fw_status yet, and + * tx_lnk_free_pkts will be NULL. check for it. + */ + if (wl->fw_status->counters.tx_lnk_free_pkts) + wl->links[link].prev_freed_pkts = + wl->fw_status->counters.tx_lnk_free_pkts[link]; wl->links[link].wlvif = wlvif; /* @@ -845,7 +851,8 @@ EXPORT_SYMBOL_GPL(wl1271_cmd_test); * @buf: buffer for the response, including all headers, must work with dma * @len: length of buf */ -int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) +int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, + size_t cmd_len, size_t res_len) { struct acx_header *acx = buf; int ret; @@ -854,10 +861,10 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) acx->id = cpu_to_le16(id); - /* payload length, does not include any headers */ - acx->len = cpu_to_le16(len - sizeof(*acx)); + /* response payload length, does not include any headers */ + acx->len = cpu_to_le16(res_len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len); + ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len); if (ret < 0) wl1271_error("INTERROGATE command failed"); @@ -1126,6 +1133,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 template_id_2_4 = wl->scan_templ_id_2_4; u16 template_id_5 = wl->scan_templ_id_5; + wl1271_debug(DEBUG_SCAN, "build probe request band %d", band); + skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, ie_len); if (!skb) { @@ -1135,8 +1144,6 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ie_len) memcpy(skb_put(skb, ie_len), ie, ie_len); - wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); - if (sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { template_id_2_4 = wl->sched_scan_templ_id_2_4; @@ -1172,7 +1179,7 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, if (!skb) goto out; - wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len); + wl1271_debug(DEBUG_SCAN, "set ap probe request template"); rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]); if (wlvif->band == IEEE80211_BAND_2GHZ) @@ -1525,6 +1532,7 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, cmd->sp_len = sta->max_sp; cmd->wmm = sta->wme ? 1 : 0; cmd->session_id = wl->session_ids[hlid]; + cmd->role_id = wlvif->role_id; for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) if (sta->wme && (sta->uapsd_queues & BIT(i))) @@ -1561,7 +1569,8 @@ out: return ret; } -int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) +int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid) { struct wl12xx_cmd_remove_peer *cmd; int ret; @@ -1579,6 +1588,7 @@ int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid) /* We never send a deauth, mac80211 is in charge of this */ cmd->reason_opcode = 0; cmd->send_deauth_flag = 0; + cmd->role_id = wlvif->role_id; ret = wl1271_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0); if (ret < 0) { @@ -1607,33 +1617,43 @@ out: static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch) { - int idx = -1; - + /* + * map the given band/channel to the respective predefined + * bit expected by the fw + */ switch (band) { - case IEEE80211_BAND_5GHZ: - if (ch >= 8 && ch <= 16) - idx = ((ch-8)/4 + 18); - else if (ch >= 34 && ch <= 64) - idx = ((ch-34)/2 + 3 + 18); - else if (ch >= 100 && ch <= 140) - idx = ((ch-100)/4 + 15 + 18); - else if (ch >= 149 && ch <= 165) - idx = ((ch-149)/4 + 26 + 18); - else - idx = -1; - break; case IEEE80211_BAND_2GHZ: + /* channels 1..14 are mapped to 0..13 */ if (ch >= 1 && ch <= 14) - idx = ch - 1; - else - idx = -1; + return ch - 1; + break; + case IEEE80211_BAND_5GHZ: + switch (ch) { + case 8 ... 16: + /* channels 8,12,16 are mapped to 18,19,20 */ + return 18 + (ch-8)/4; + case 34 ... 48: + /* channels 34,36..48 are mapped to 21..28 */ + return 21 + (ch-34)/2; + case 52 ... 64: + /* channels 52,56..64 are mapped to 29..32 */ + return 29 + (ch-52)/4; + case 100 ... 140: + /* channels 100,104..140 are mapped to 33..43 */ + return 33 + (ch-100)/4; + case 149 ... 165: + /* channels 149,153..165 are mapped to 44..48 */ + return 44 + (ch-149)/4; + default: + break; + } break; default: - wl1271_error("get reg conf ch idx - unknown band: %d", - (int)band); + break; } - return idx; + wl1271_error("%s: unknown band/channel: %d/%d", __func__, band, ch); + return -1; } void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, @@ -1646,7 +1666,7 @@ void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel); - if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS) + if (ch_bit_idx >= 0 && ch_bit_idx <= WL1271_MAX_CHANNELS) set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending); } @@ -1676,7 +1696,7 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) if (channel->flags & (IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_PASSIVE_SCAN)) + IEEE80211_CHAN_NO_IR)) continue; ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch); diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index fd34123047c..b084830a61c 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -45,7 +45,8 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum ieee80211_band band, int channel); int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); -int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); +int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, + size_t cmd_len, size_t res_len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len, unsigned long valid_rets); @@ -87,7 +88,8 @@ int wl12xx_roc(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 role_id, int wl12xx_croc(struct wl1271 *wl, u8 role_id); int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid); -int wl12xx_cmd_remove_peer(struct wl1271 *wl, u8 hlid); +int wl12xx_cmd_remove_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid); void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, enum ieee80211_band band); int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl); @@ -205,7 +207,7 @@ enum cmd_templ { #define WL1271_COMMAND_TIMEOUT 2000 #define WL1271_CMD_TEMPL_DFLT_SIZE 252 #define WL1271_CMD_TEMPL_MAX_SIZE 512 -#define WL1271_EVENT_TIMEOUT 1500 +#define WL1271_EVENT_TIMEOUT 5000 struct wl1271_cmd_header { __le16 id; @@ -593,6 +595,8 @@ struct wl12xx_cmd_add_peer { u8 sp_len; u8 wmm; u8 session_id; + u8 role_id; + u8 padding[3]; } __packed; struct wl12xx_cmd_remove_peer { @@ -601,7 +605,7 @@ struct wl12xx_cmd_remove_peer { u8 hlid; u8 reason_opcode; u8 send_deauth_flag; - u8 padding1; + u8 role_id; } __packed; /* diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 2b96ff82134..40995c42bef 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -1274,6 +1274,9 @@ struct conf_rx_streaming_settings { u8 always; } __packed; +#define CONF_FWLOG_MIN_MEM_BLOCKS 2 +#define CONF_FWLOG_MAX_MEM_BLOCKS 16 + struct conf_fwlog { /* Continuous or on-demand */ u8 mode; @@ -1281,7 +1284,7 @@ struct conf_fwlog { /* * Number of memory blocks dedicated for the FW logger * - * Range: 1-3, or 0 to disable the FW logger + * Range: 2-16, or 0 to disable the FW logger */ u8 mem_blocks; diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index e17630c2a84..89893c71702 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -437,6 +437,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, int res = 0; ssize_t ret; char *buf; + struct wl12xx_vif *wlvif; #define DRIVER_STATE_BUF_LEN 1024 @@ -450,12 +451,28 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ #x " = " fmt "\n", wl->x)) +#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...) \ + (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ + #x " = " fmt "\n", args)) + #define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld") #define DRIVER_STATE_PRINT_INT(x) DRIVER_STATE_PRINT(x, "%d") #define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") #define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + continue; + + DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, + wlvif->p2p ? "P2P-CL" : "STA"); + } + + wl12xx_for_each_wlvif_ap(wl, wlvif) + DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, + wlvif->p2p ? "P2P-GO" : "AP"); + DRIVER_STATE_PRINT_INT(tx_blocks_available); DRIVER_STATE_PRINT_INT(tx_allocated_blocks); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]); @@ -474,7 +491,6 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_INT(tx_blocks_freed); DRIVER_STATE_PRINT_INT(rx_counter); DRIVER_STATE_PRINT_INT(state); - DRIVER_STATE_PRINT_INT(channel); DRIVER_STATE_PRINT_INT(band); DRIVER_STATE_PRINT_INT(power_level); DRIVER_STATE_PRINT_INT(sg_enabled); diff --git a/drivers/net/wireless/ti/wlcore/debugfs.h b/drivers/net/wireless/ti/wlcore/debugfs.h index f7381dd6900..0f2cfb0d2a9 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.h +++ b/drivers/net/wireless/ti/wlcore/debugfs.h @@ -57,7 +57,7 @@ static const struct file_operations name## _ops = { \ wl, &name## _ops); \ if (!entry || IS_ERR(entry)) \ goto err; \ - } while (0); + } while (0) #define DEBUGFS_ADD_PREFIX(prefix, name, parent) \ @@ -66,7 +66,7 @@ static const struct file_operations name## _ops = { \ wl, &prefix## _## name## _ops); \ if (!entry || IS_ERR(entry)) \ goto err; \ - } while (0); + } while (0) #define DEBUGFS_FWSTATS_FILE(sub, name, fmt, struct_type) \ static ssize_t sub## _ ##name## _read(struct file *file, \ diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 67f61689b49..16d10281798 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -67,7 +67,7 @@ static void wl1271_stop_ba_event(struct wl1271 *wl, struct wl12xx_vif *wlvif) u8 hlid; struct wl1271_link *lnk; for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, - WL12XX_MAX_LINKS) { + wl->num_links) { lnk = &wl->links[hlid]; if (!lnk->ba_bitmap) continue; @@ -158,6 +158,11 @@ EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); void wlcore_event_dummy_packet(struct wl1271 *wl) { + if (wl->plt) { + wl1271_info("Got DUMMY_PACKET event in PLT mode. FW bug, ignoring."); + return; + } + wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); wl1271_tx_dummy_packet(wl); } @@ -172,7 +177,7 @@ static void wlcore_disconnect_sta(struct wl1271 *wl, unsigned long sta_bitmap) const u8 *addr; int h; - for_each_set_bit(h, &sta_bitmap, WL12XX_MAX_LINKS) { + for_each_set_bit(h, &sta_bitmap, wl->num_links) { bool found = false; /* find the ap vif connected to this sta */ wl12xx_for_each_wlvif_ap(wl, wlvif) { @@ -266,6 +271,7 @@ int wl1271_event_unmask(struct wl1271 *wl) { int ret; + wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask); ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask)); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 7fd260c02a0..1555ff97005 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -106,6 +106,15 @@ wlcore_hw_init_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif) return 0; } +static inline void +wlcore_hw_convert_fw_status(struct wl1271 *wl, void *raw_fw_status, + struct wl_fw_status *fw_status) +{ + BUG_ON(!wl->ops->convert_fw_status); + + wl->ops->convert_fw_status(wl, raw_fw_status, fw_status); +} + static inline u32 wlcore_hw_sta_get_ap_rate_mask(struct wl1271 *wl, struct wl12xx_vif *wlvif) { @@ -222,6 +231,15 @@ wlcore_hw_set_peer_cap(struct wl1271 *wl, return 0; } +static inline u32 +wlcore_hw_convert_hwaddr(struct wl1271 *wl, u32 hwaddr) +{ + if (!wl->ops->convert_hwaddr) + BUG_ON(1); + + return wl->ops->convert_hwaddr(wl, hwaddr); +} + static inline bool wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk) diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 5c6f11e157d..199e9412086 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -287,8 +287,8 @@ static int wl1271_init_sta_beacon_filter(struct wl1271 *wl, if (ret < 0) return ret; - /* enable beacon filtering */ - ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); + /* disable beacon filtering until we get the first beacon */ + ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); if (ret < 0) return ret; @@ -462,7 +462,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl, struct wl12xx_vif *wlvif) * If the basic rates contain OFDM rates, use OFDM only * rates for unicast TX as well. Else use all supported rates. */ - if ((wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) + if (wl->ofdm_only_ap && (wlvif->basic_rate_set & CONF_TX_OFDM_RATES)) supported_rates = CONF_TX_OFDM_RATES; else supported_rates = CONF_TX_ENABLED_RATES; @@ -571,6 +571,12 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); if (ret < 0) return ret; + + /* unmask ap events */ + wl->event_mask |= wl->ap_event_mask; + ret = wl1271_event_unmask(wl); + if (ret < 0) + return ret; /* first STA, no APs */ } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) { u8 sta_auth = wl->conf.conn.sta_sleep_auth; diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index af7d9f9b3b4..0305729d098 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -60,7 +60,9 @@ static inline int __must_check wlcore_raw_write(struct wl1271 *wl, int addr, { int ret; - if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags)) + if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) || + WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) && + addr != HW_ACCESS_ELP_CTRL_REG))) return -EIO; ret = wl->if_ops->write(wl->dev, addr, buf, len, fixed); @@ -76,7 +78,9 @@ static inline int __must_check wlcore_raw_read(struct wl1271 *wl, int addr, { int ret; - if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags)) + if (test_bit(WL1271_FLAG_IO_FAILED, &wl->flags) || + WARN_ON((test_bit(WL1271_FLAG_IN_ELP, &wl->flags) && + addr != HW_ACCESS_ELP_CTRL_REG))) return -EIO; ret = wl->if_ops->read(wl->dev, addr, buf, len, fixed); @@ -165,8 +169,8 @@ static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, int physical; int addr; - /* Addresses are stored internally as addresses to 32 bytes blocks */ - addr = hwaddr << 5; + /* Convert from FW internal address which is chip arch dependent */ + addr = wl->ops->convert_hwaddr(wl, hwaddr); physical = wlcore_translate_addr(wl, addr); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 38995f90040..3d6028e6275 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -44,6 +44,7 @@ #define WL1271_BOOT_RETRIES 3 static char *fwlog_param; +static int fwlog_mem_blocks = -1; static int bug_on_recovery = -1; static int no_recovery = -1; @@ -90,8 +91,7 @@ static void wl1271_reg_notify(struct wiphy *wiphy, continue; if (ch->flags & IEEE80211_CHAN_RADAR) - ch->flags |= IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_PASSIVE_SCAN; + ch->flags |= IEEE80211_CHAN_NO_IR; } @@ -291,6 +291,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl) { /* Adjust settings according to optional module parameters */ + /* Firmware Logger params */ + if (fwlog_mem_blocks != -1) { + if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS && + fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) { + wl->conf.fwlog.mem_blocks = fwlog_mem_blocks; + } else { + wl1271_error( + "Illegal fwlog_mem_blocks=%d using default %d", + fwlog_mem_blocks, wl->conf.fwlog.mem_blocks); + } + } + if (fwlog_param) { if (!strcmp(fwlog_param, "continuous")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; @@ -333,24 +345,24 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, * Start high-level PS if the STA is asleep with enough blocks in FW. * Make an exception if this is the only connected link. In this * case FW-memory congestion is less of a problem. - * Note that a single connected STA means 3 active links, since we must - * account for the global and broadcast AP links. The "fw_ps" check - * assures us the third link is a STA connected to the AP. Otherwise - * the FW would not set the PSM bit. + * Note that a single connected STA means 2*ap_count + 1 active links, + * since we must account for the global and broadcast AP links + * for each AP. The "fw_ps" check assures us the other link is a STA + * connected to the AP. Otherwise the FW would not set the PSM bit. */ - else if (wl->active_link_count > 3 && fw_ps && + else if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } static void wl12xx_irq_update_links_status(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct wl_fw_status_2 *status) + struct wl_fw_status *status) { u32 cur_fw_ps_map; u8 hlid; - cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); + cur_fw_ps_map = status->link_ps_bitmap; if (wl->ap_fw_ps_map != cur_fw_ps_map) { wl1271_debug(DEBUG_PSM, "link ps prev 0x%x cur 0x%x changed 0x%x", @@ -360,77 +372,73 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, wl->ap_fw_ps_map = cur_fw_ps_map; } - for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, WL12XX_MAX_LINKS) + for_each_set_bit(hlid, wlvif->ap.sta_hlid_map, wl->num_links) wl12xx_irq_ps_regulate_link(wl, wlvif, hlid, wl->links[hlid].allocated_pkts); } -static int wlcore_fw_status(struct wl1271 *wl, - struct wl_fw_status_1 *status_1, - struct wl_fw_status_2 *status_2) +static int wlcore_fw_status(struct wl1271 *wl, struct wl_fw_status *status) { struct wl12xx_vif *wlvif; struct timespec ts; u32 old_tx_blk_count = wl->tx_blocks_available; int avail, freed_blocks; int i; - size_t status_len; int ret; struct wl1271_link *lnk; - status_len = WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + - sizeof(*status_2) + wl->fw_status_priv_len; - - ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, status_1, - status_len, false); + ret = wlcore_raw_read_data(wl, REG_RAW_FW_STATUS_ADDR, + wl->raw_fw_status, + wl->fw_status_len, false); if (ret < 0) return ret; + wlcore_hw_convert_fw_status(wl, wl->raw_fw_status, wl->fw_status); + wl1271_debug(DEBUG_IRQ, "intr: 0x%x (fw_rx_counter = %d, " "drv_rx_counter = %d, tx_results_counter = %d)", - status_1->intr, - status_1->fw_rx_counter, - status_1->drv_rx_counter, - status_1->tx_results_counter); + status->intr, + status->fw_rx_counter, + status->drv_rx_counter, + status->tx_results_counter); for (i = 0; i < NUM_TX_QUEUES; i++) { /* prevent wrap-around in freed-packets counter */ wl->tx_allocated_pkts[i] -= - (status_2->counters.tx_released_pkts[i] - + (status->counters.tx_released_pkts[i] - wl->tx_pkts_freed[i]) & 0xff; - wl->tx_pkts_freed[i] = status_2->counters.tx_released_pkts[i]; + wl->tx_pkts_freed[i] = status->counters.tx_released_pkts[i]; } - for_each_set_bit(i, wl->links_map, WL12XX_MAX_LINKS) { + for_each_set_bit(i, wl->links_map, wl->num_links) { u8 diff; lnk = &wl->links[i]; /* prevent wrap-around in freed-packets counter */ - diff = (status_2->counters.tx_lnk_free_pkts[i] - + diff = (status->counters.tx_lnk_free_pkts[i] - lnk->prev_freed_pkts) & 0xff; if (diff == 0) continue; lnk->allocated_pkts -= diff; - lnk->prev_freed_pkts = status_2->counters.tx_lnk_free_pkts[i]; + lnk->prev_freed_pkts = status->counters.tx_lnk_free_pkts[i]; /* accumulate the prev_freed_pkts counter */ lnk->total_freed_pkts += diff; } /* prevent wrap-around in total blocks counter */ - if (likely(wl->tx_blocks_freed <= - le32_to_cpu(status_2->total_released_blks))) - freed_blocks = le32_to_cpu(status_2->total_released_blks) - + if (likely(wl->tx_blocks_freed <= status->total_released_blks)) + freed_blocks = status->total_released_blks - wl->tx_blocks_freed; else freed_blocks = 0x100000000LL - wl->tx_blocks_freed + - le32_to_cpu(status_2->total_released_blks); + status->total_released_blks; - wl->tx_blocks_freed = le32_to_cpu(status_2->total_released_blks); + wl->tx_blocks_freed = status->total_released_blks; wl->tx_allocated_blocks -= freed_blocks; @@ -446,7 +454,7 @@ static int wlcore_fw_status(struct wl1271 *wl, cancel_delayed_work(&wl->tx_watchdog_work); } - avail = le32_to_cpu(status_2->tx_total) - wl->tx_allocated_blocks; + avail = status->tx_total - wl->tx_allocated_blocks; /* * The FW might change the total number of TX memblocks before @@ -465,15 +473,15 @@ static int wlcore_fw_status(struct wl1271 *wl, /* for AP update num of allocated TX blocks per link and ps status */ wl12xx_for_each_wlvif_ap(wl, wlvif) { - wl12xx_irq_update_links_status(wl, wlvif, status_2); + wl12xx_irq_update_links_status(wl, wlvif, status); } /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - - (s64)le32_to_cpu(status_2->fw_localtime); + (s64)(status->fw_localtime); - wl->fw_fast_lnk_map = le32_to_cpu(status_2->link_fast_bitmap); + wl->fw_fast_lnk_map = status->link_fast_bitmap; return 0; } @@ -535,15 +543,15 @@ static int wlcore_irq_locked(struct wl1271 *wl) * wl1271_ps_elp_wakeup cannot be called concurrently. */ clear_bit(WL1271_FLAG_IRQ_RUNNING, &wl->flags); - smp_mb__after_clear_bit(); + smp_mb__after_atomic(); - ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + ret = wlcore_fw_status(wl, wl->fw_status); if (ret < 0) goto out; wlcore_hw_tx_immediate_compl(wl); - intr = le32_to_cpu(wl->fw_status_1->intr); + intr = wl->fw_status->intr; intr &= WLCORE_ALL_INTR_MASK; if (!intr) { done = true; @@ -572,7 +580,7 @@ static int wlcore_irq_locked(struct wl1271 *wl) if (likely(intr & WL1271_ACX_INTR_DATA)) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); - ret = wlcore_rx(wl, wl->fw_status_1); + ret = wlcore_rx(wl, wl->fw_status); if (ret < 0) goto out; @@ -774,12 +782,14 @@ out: void wl12xx_queue_recovery_work(struct wl1271 *wl) { - WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)); - /* Avoid a recursive recovery */ if (wl->state == WLCORE_STATE_ON) { + WARN_ON(!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, + &wl->flags)); + wl->state = WLCORE_STATE_RESTARTING; set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); + wl1271_ps_elp_wakeup(wl); wlcore_disable_interrupts_nosync(wl); ieee80211_queue_work(wl->hw, &wl->recovery_work); } @@ -787,19 +797,10 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl) size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) { - size_t len = 0; - - /* The FW log is a length-value list, find where the log end */ - while (len < maxlen) { - if (memblock[len] == 0) - break; - if (len + memblock[len] + 1 > maxlen) - break; - len += memblock[len] + 1; - } + size_t len; /* Make sure we have enough room */ - len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size)); + len = min_t(size_t, maxlen, PAGE_SIZE - wl->fwlog_size); /* Fill the FW log file, consumed by the sysfs fwlog entry */ memcpy(wl->fwlog + wl->fwlog_size, memblock, len); @@ -808,10 +809,9 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) return len; } -#define WLCORE_FW_LOG_END 0x2000000 - static void wl12xx_read_fwlog_panic(struct wl1271 *wl) { + struct wlcore_partition_set part, old_part; u32 addr; u32 offset; u32 end_of_log; @@ -824,7 +824,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) wl1271_info("Reading FW panic log"); - block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL); + block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL); if (!block) return; @@ -840,27 +840,41 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) wl12xx_cmd_stop_fwlog(wl); /* Read the first memory block address */ - ret = wlcore_fw_status(wl, wl->fw_status_1, wl->fw_status_2); + ret = wlcore_fw_status(wl, wl->fw_status); if (ret < 0) goto out; - addr = le32_to_cpu(wl->fw_status_2->log_start_addr); + addr = wl->fw_status->log_start_addr; if (!addr) goto out; if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) { offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor); - end_of_log = WLCORE_FW_LOG_END; + end_of_log = wl->fwlog_end; } else { offset = sizeof(addr); end_of_log = addr; } + old_part = wl->curr_part; + memset(&part, 0, sizeof(part)); + /* Traverse the memory blocks linked list */ do { - memset(block, 0, WL12XX_HW_BLOCK_SIZE); - ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, - false); + part.mem.start = wlcore_hw_convert_hwaddr(wl, addr); + part.mem.size = PAGE_SIZE; + + ret = wlcore_set_partition(wl, &part); + if (ret < 0) { + wl1271_error("%s: set_partition start=0x%X size=%d", + __func__, part.mem.start, part.mem.size); + goto out; + } + + memset(block, 0, wl->fw_mem_block_size); + ret = wlcore_read_hwaddr(wl, addr, block, + wl->fw_mem_block_size, false); + if (ret < 0) goto out; @@ -871,8 +885,9 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) * on demand mode and is equal to 0x2000000 in continuous mode. */ addr = le32_to_cpup((__le32 *)block); + if (!wl12xx_copy_fwlog(wl, block + offset, - WL12XX_HW_BLOCK_SIZE - offset)) + wl->fw_mem_block_size - offset)) break; } while (addr && (addr != end_of_log)); @@ -880,6 +895,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) out: kfree(block); + wlcore_set_partition(wl, &old_part); } static void wlcore_print_recovery(struct wl1271 *wl) @@ -924,7 +940,8 @@ static void wl1271_recovery_work(struct work_struct *work) goto out_unlock; if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) { - wl12xx_read_fwlog_panic(wl); + if (wl->conf.fwlog.output == WL12XX_FWLOG_OUTPUT_HOST) + wl12xx_read_fwlog_panic(wl); wlcore_print_recovery(wl); } @@ -970,23 +987,23 @@ static int wlcore_fw_wakeup(struct wl1271 *wl) static int wl1271_setup(struct wl1271 *wl) { - wl->fw_status_1 = kzalloc(WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc) + - sizeof(*wl->fw_status_2) + - wl->fw_status_priv_len, GFP_KERNEL); - if (!wl->fw_status_1) - return -ENOMEM; + wl->raw_fw_status = kzalloc(wl->fw_status_len, GFP_KERNEL); + if (!wl->raw_fw_status) + goto err; - wl->fw_status_2 = (struct wl_fw_status_2 *) - (((u8 *) wl->fw_status_1) + - WLCORE_FW_STATUS_1_LEN(wl->num_rx_desc)); + wl->fw_status = kzalloc(sizeof(*wl->fw_status), GFP_KERNEL); + if (!wl->fw_status) + goto err; wl->tx_res_if = kzalloc(sizeof(*wl->tx_res_if), GFP_KERNEL); - if (!wl->tx_res_if) { - kfree(wl->fw_status_1); - return -ENOMEM; - } + if (!wl->tx_res_if) + goto err; return 0; +err: + kfree(wl->fw_status); + kfree(wl->raw_fw_status); + return -ENOMEM; } static int wl12xx_set_power_on(struct wl1271 *wl) @@ -1062,7 +1079,8 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode) static const char* const PLT_MODE[] = { "PLT_OFF", "PLT_ON", - "PLT_FEM_DETECT" + "PLT_FEM_DETECT", + "PLT_CHIP_AWAKE" }; int ret; @@ -1088,9 +1106,11 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode) if (ret < 0) goto power_off; - ret = wl->ops->plt_init(wl); - if (ret < 0) - goto power_off; + if (plt_mode != PLT_CHIP_AWAKE) { + ret = wl->ops->plt_init(wl); + if (ret < 0) + goto power_off; + } wl->state = WLCORE_STATE_ON; wl1271_notice("firmware booted in PLT mode %s (%s)", @@ -1396,7 +1416,7 @@ void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter) int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, u16 offset, u8 flags, - u8 *pattern, u8 len) + const u8 *pattern, u8 len) { struct wl12xx_rx_filter_field *field; @@ -1744,6 +1764,12 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, flush_work(&wl->tx_work); flush_delayed_work(&wl->elp_work); + /* + * Cancel the watchdog even if above tx_flush failed. We will detect + * it on resume anyway. + */ + cancel_delayed_work(&wl->tx_watchdog_work); + return 0; } @@ -1801,6 +1827,13 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) out: wl->wow_enabled = false; + + /* + * Set a flag to re-init the watchdog on the first Tx after resume. + * That way we avoid possible conditions where Tx-complete interrupts + * fail to arrive and we perform a spurious recovery. + */ + set_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags); mutex_unlock(&wl->mutex); return 0; @@ -1891,6 +1924,7 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); memset(wl->session_ids, 0, sizeof(wl->session_ids)); + memset(wl->rx_filter_enabled, 0, sizeof(wl->rx_filter_enabled)); wl->active_sta_count = 0; wl->active_link_count = 0; @@ -1915,9 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) wl1271_debugfs_reset(wl); - kfree(wl->fw_status_1); - wl->fw_status_1 = NULL; - wl->fw_status_2 = NULL; + kfree(wl->raw_fw_status); + wl->raw_fw_status = NULL; + kfree(wl->fw_status); + wl->fw_status = NULL; kfree(wl->tx_res_if); wl->tx_res_if = NULL; kfree(wl->target_mem_map); @@ -1925,8 +1960,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) /* * FW channels must be re-calibrated after recovery, - * clear the last Reg-Domain channel configuration. + * save current Reg-Domain channel configuration and clear it. */ + memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last, + sizeof(wl->reg_ch_conf_pending)); memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last)); } @@ -2008,6 +2045,47 @@ out: mutex_unlock(&wl->mutex); } +static void wlcore_pending_auth_complete_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct wl12xx_vif *wlvif; + unsigned long time_spare; + int ret; + + dwork = container_of(work, struct delayed_work, work); + wlvif = container_of(dwork, struct wl12xx_vif, + pending_auth_complete_work); + wl = wlvif->wl; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* + * Make sure a second really passed since the last auth reply. Maybe + * a second auth reply arrived while we were stuck on the mutex. + * Check for a little less than the timeout to protect from scheduler + * irregularities. + */ + time_spare = jiffies + + msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50); + if (!time_after(time_spare, wlvif->pending_auth_reply_time)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* cancel the ROC if active */ + wlcore_update_inconn_sta(wl, wlvif, NULL, false); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx) { u8 policy = find_first_zero_bit(wl->rate_policies_map, @@ -2159,6 +2237,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wlcore_channel_switch_work); INIT_DELAYED_WORK(&wlvif->connection_loss_work, wlcore_connection_loss_work); + INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work, + wlcore_pending_auth_complete_work); INIT_LIST_HEAD(&wlvif->list); setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer, @@ -2376,6 +2456,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, int ret = 0; u8 role_type; + if (wl->plt) { + wl1271_error("Adding Interface not allowed while in PLT mode"); + return -EBUSY; + } + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; @@ -2498,10 +2583,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl, ieee80211_scan_completed(wl->hw, true); } - if (wl->sched_vif == wlvif) { - ieee80211_sched_scan_stopped(wl->hw); + if (wl->sched_vif == wlvif) wl->sched_vif = NULL; - } if (wl->roc_vif == vif) { wl->roc_vif = NULL; @@ -2572,6 +2655,12 @@ deinit: !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) goto unlock; + if (wl->ap_count == 0 && is_ap) { + /* mask ap events */ + wl->event_mask &= ~wl->ap_event_mask; + wl1271_event_unmask(wl); + } + if (wl->ap_count == 0 && is_ap && wl->sta_count) { u8 sta_auth = wl->conf.conn.sta_sleep_auth; /* Configure for power according to debugfs */ @@ -2590,6 +2679,7 @@ unlock: cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->channel_switch_work); + cancel_delayed_work_sync(&wlvif->pending_auth_complete_work); mutex_lock(&wl->mutex); } @@ -2851,6 +2941,11 @@ static int wlcore_unset_assoc(struct wl1271 *wl, struct wl12xx_vif *wlvif) ret = wl1271_acx_keep_alive_mode(wl, wlvif, false); if (ret < 0) return ret; + + /* disable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); + if (ret < 0) + return ret; } if (test_and_clear_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags)) { @@ -2875,6 +2970,25 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) wlvif->rate_set = wlvif->basic_rate_set; } +static void wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif, + bool idle) +{ + bool cur_idle = !test_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags); + + if (idle == cur_idle) + return; + + if (idle) { + clear_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags); + } else { + /* The current firmware only supports sched_scan in idle */ + if (wl->sched_vif == wlvif) + wl->ops->sched_scan_stop(wl, wlvif); + + set_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags); + } +} + static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { @@ -3364,6 +3478,10 @@ static void wl1271_op_set_default_key_idx(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 set default key idx %d", key_idx); + /* we don't handle unsetting of default key */ + if (key_idx == -1) + return; + mutex_lock(&wl->mutex); if (unlikely(wl->state != WLCORE_STATE_ON)) { @@ -3550,8 +3668,8 @@ out: return ret; } -static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static int wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); @@ -3573,6 +3691,8 @@ static void wl1271_op_sched_scan_stop(struct ieee80211_hw *hw, wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); + + return 0; } static int wl1271_op_set_frag_threshold(struct ieee80211_hw *hw, u32 value) @@ -3969,6 +4089,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, } } else { if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { + /* + * AP might be in ROC in case we have just + * sent auth reply. handle it. + */ + if (test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); + ret = wl12xx_cmd_role_stop_ap(wl, wlvif); if (ret < 0) goto out; @@ -4120,6 +4247,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, do_join = true; } + if (changed & BSS_CHANGED_IDLE && !is_ibss) + wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle); + if (changed & BSS_CHANGED_CQM) { bool enable = false; if (bss_conf->cqm_rssi_thold) @@ -4189,6 +4319,13 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, } } + if ((changed & BSS_CHANGED_BEACON_INFO) && bss_conf->dtim_period) { + /* enable beacon filtering */ + ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); + if (ret < 0) + goto out; + } + ret = wl1271_bss_erp_info_changed(wl, vif, bss_conf, changed); if (ret < 0) goto out; @@ -4348,6 +4485,16 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) goto out; + if ((changed & BSS_CHANGED_TXPOWER) && + bss_conf->txpower != wlvif->power_level) { + + ret = wl1271_acx_tx_power(wl, wlvif, bss_conf->txpower); + if (ret < 0) + goto out; + + wlvif->power_level = bss_conf->txpower; + } + if (is_ap) wl1271_bss_info_changed_ap(wl, vif, bss_conf, changed); else @@ -4532,7 +4679,7 @@ static int wl1271_allocate_sta(struct wl1271 *wl, int ret; - if (wl->active_sta_count >= AP_MAX_STATIONS) { + if (wl->active_sta_count >= wl->max_ap_stations) { wl1271_warning("could not allocate HLID - too much stations"); return -EBUSY; } @@ -4635,7 +4782,7 @@ static int wl12xx_sta_remove(struct wl1271 *wl, if (WARN_ON(!test_bit(id, wlvif->ap.sta_hlid_map))) return -EINVAL; - ret = wl12xx_cmd_remove_peer(wl, wl_sta->hlid); + ret = wl12xx_cmd_remove_peer(wl, wlvif, wl_sta->hlid); if (ret < 0) return ret; @@ -4656,29 +4803,49 @@ static void wlcore_roc_if_possible(struct wl1271 *wl, wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel); } -static void wlcore_update_inconn_sta(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct wl1271_station *wl_sta, - bool in_connection) +/* + * when wl_sta is NULL, we treat this call as if coming from a + * pending auth reply. + * wl->mutex must be taken and the FW must be awake when the call + * takes place. + */ +void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct wl1271_station *wl_sta, bool in_conn) { - if (in_connection) { - if (WARN_ON(wl_sta->in_connection)) + if (in_conn) { + if (WARN_ON(wl_sta && wl_sta->in_connection)) return; - wl_sta->in_connection = true; - if (!wlvif->inconn_count++) + + if (!wlvif->ap_pending_auth_reply && + !wlvif->inconn_count) wlcore_roc_if_possible(wl, wlvif); + + if (wl_sta) { + wl_sta->in_connection = true; + wlvif->inconn_count++; + } else { + wlvif->ap_pending_auth_reply = true; + } } else { - if (!wl_sta->in_connection) + if (wl_sta && !wl_sta->in_connection) + return; + + if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply)) return; - wl_sta->in_connection = false; - wlvif->inconn_count--; - if (WARN_ON(wlvif->inconn_count < 0)) + if (WARN_ON(wl_sta && !wlvif->inconn_count)) return; - if (!wlvif->inconn_count) - if (test_bit(wlvif->role_id, wl->roc_map)) - wl12xx_croc(wl, wlvif->role_id); + if (wl_sta) { + wl_sta->in_connection = false; + wlvif->inconn_count--; + } else { + wlvif->ap_pending_auth_reply = false; + } + + if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply && + test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); } } @@ -5017,7 +5184,8 @@ out: mutex_unlock(&wl->mutex); } -static void wlcore_op_flush(struct ieee80211_hw *hw, u32 queues, bool drop) +static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) { struct wl1271 *wl = hw->priv; @@ -5313,10 +5481,7 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = { /* 5 GHz band channels for WL1273 */ static struct ieee80211_channel wl1271_channels_5ghz[] = { - { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR }, - { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR }, - { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR }, @@ -5543,28 +5708,6 @@ static void wl1271_unregister_hw(struct wl1271 *wl) } -static const struct ieee80211_iface_limit wlcore_iface_limits[] = { - { - .max = 3, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT), - }, -}; - -static struct ieee80211_iface_combination -wlcore_iface_combinations[] = { - { - .max_interfaces = 3, - .limits = wlcore_iface_limits, - .n_limits = ARRAY_SIZE(wlcore_iface_limits), - }, -}; - static int wl1271_init_ieee80211(struct wl1271 *wl) { int i; @@ -5584,7 +5727,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->channel_change_time = 10000; wl->hw->max_listen_interval = wl->conf.conn.max_listen_interval; wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | @@ -5598,7 +5740,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_AP_LINK_PS | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | - IEEE80211_HW_QUEUE_CONTROL; + IEEE80211_HW_QUEUE_CONTROL | + IEEE80211_HW_CHANCTX_STA_CSA; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); @@ -5686,10 +5829,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; /* allowed interface combinations */ - wlcore_iface_combinations[0].num_different_channels = wl->num_channels; - wl->hw->wiphy->iface_combinations = wlcore_iface_combinations; - wl->hw->wiphy->n_iface_combinations = - ARRAY_SIZE(wlcore_iface_combinations); + wl->hw->wiphy->iface_combinations = wl->iface_combinations; + wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations; SET_IEEE80211_DEV(wl->hw, wl->dev); @@ -5709,8 +5850,6 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, int i, j, ret; unsigned int order; - BUILD_BUG_ON(AP_MAX_STATIONS > WL12XX_MAX_LINKS); - hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); @@ -5732,8 +5871,12 @@ struct ieee80211_hw *wlcore_alloc_hw(size_t priv_size, u32 aggr_buf_size, wl->hw = hw; + /* + * wl->num_links is not configured yet, so just use WLCORE_MAX_LINKS. + * we don't allocate any additional resource here, so that's fine. + */ for (i = 0; i < NUM_TX_QUEUES; i++) - for (j = 0; j < WL12XX_MAX_LINKS; j++) + for (j = 0; j < WLCORE_MAX_LINKS; j++) skb_queue_head_init(&wl->links[j].tx_queue[i]); skb_queue_head_init(&wl->deferred_rx_queue); @@ -5876,7 +6019,8 @@ int wlcore_free_hw(struct wl1271 *wl) kfree(wl->nvs); wl->nvs = NULL; - kfree(wl->fw_status_1); + kfree(wl->raw_fw_status); + kfree(wl->fw_status); kfree(wl->tx_res_if); destroy_workqueue(wl->freezable_wq); @@ -5896,14 +6040,20 @@ static const struct wiphy_wowlan_support wlcore_wowlan_support = { }; #endif +static irqreturn_t wlcore_hardirq(int irq, void *cookie) +{ + return IRQ_WAKE_THREAD; +} + static void wlcore_nvs_cb(const struct firmware *fw, void *context) { struct wl1271 *wl = context; struct platform_device *pdev = wl->pdev; - struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data; + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev); struct wl12xx_platform_data *pdata = pdev_data->pdata; unsigned long irqflags; int ret; + irq_handler_t hardirq_fn = NULL; if (fw) { wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); @@ -5932,12 +6082,14 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) wl->platform_quirks = pdata->platform_quirks; wl->if_ops = pdev_data->if_ops; - if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) + if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) { irqflags = IRQF_TRIGGER_RISING; - else + hardirq_fn = wlcore_hardirq; + } else { irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + } - ret = request_threaded_irq(wl->irq, NULL, wlcore_irq, + ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq, irqflags, pdev->name, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); @@ -6046,6 +6198,9 @@ module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(fwlog, "FW logger options: continuous, ondemand, dbgpins or disable"); +module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks"); + module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 98066d40c2a..b52516eed7b 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -83,6 +83,10 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) struct wl12xx_vif *wlvif; u32 timeout; + /* We do not enter elp sleep in PLT mode */ + if (wl->plt) + return; + if (wl->sleep_auth != WL1271_PSM_ELP) return; @@ -276,7 +280,11 @@ void wl12xx_ps_link_start(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta; struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); - if (test_bit(hlid, &wl->ap_ps_map)) + if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS)) + return; + + if (!test_bit(hlid, wlvif->ap.sta_hlid_map) || + test_bit(hlid, &wl->ap_ps_map)) return; wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d pkts %d " diff --git a/drivers/net/wireless/ti/wlcore/rx.c b/drivers/net/wireless/ti/wlcore/rx.c index 6791a1a6afb..e125974285c 100644 --- a/drivers/net/wireless/ti/wlcore/rx.c +++ b/drivers/net/wireless/ti/wlcore/rx.c @@ -203,9 +203,9 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, return is_data; } -int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) +int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status) { - unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; + unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0}; u32 buf_size; u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc; u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc; @@ -263,12 +263,12 @@ int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status) wl->aggr_buf + pkt_offset, pkt_len, rx_align, &hlid) == 1) { - if (hlid < WL12XX_MAX_LINKS) + if (hlid < wl->num_links) __set_bit(hlid, active_hlids); else WARN(1, - "hlid exceeded WL12XX_MAX_LINKS " - "(%d)\n", hlid); + "hlid (%d) exceeded MAX_LINKS\n", + hlid); } wl->rx_counter++; @@ -302,7 +302,7 @@ int wl1271_rx_filter_enable(struct wl1271 *wl, { int ret; - if (wl->rx_filter_enabled[index] == enable) { + if (!!test_bit(index, wl->rx_filter_enabled) == enable) { wl1271_warning("Request to enable an already " "enabled rx filter %d", index); return 0; @@ -316,7 +316,10 @@ int wl1271_rx_filter_enable(struct wl1271 *wl, return ret; } - wl->rx_filter_enabled[index] = enable; + if (enable) + __set_bit(index, wl->rx_filter_enabled); + else + __clear_bit(index, wl->rx_filter_enabled); return 0; } @@ -326,7 +329,7 @@ int wl1271_rx_filter_clear_all(struct wl1271 *wl) int i, ret = 0; for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) { - if (!wl->rx_filter_enabled[i]) + if (!test_bit(i, wl->rx_filter_enabled)) continue; ret = wl1271_rx_filter_enable(wl, i, 0, NULL); if (ret) diff --git a/drivers/net/wireless/ti/wlcore/rx.h b/drivers/net/wireless/ti/wlcore/rx.h index 3363f60fb7d..a3b1618db27 100644 --- a/drivers/net/wireless/ti/wlcore/rx.h +++ b/drivers/net/wireless/ti/wlcore/rx.h @@ -142,7 +142,7 @@ struct wl1271_rx_descriptor { u8 reserved; } __packed; -int wlcore_rx(struct wl1271 *wl, struct wl_fw_status_1 *status); +int wlcore_rx(struct wl1271 *wl, struct wl_fw_status *status); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); int wl1271_rx_filter_enable(struct wl1271 *wl, int index, bool enable, diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index f407101e525..1e3d51cd673 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -92,9 +92,31 @@ out: static void wlcore_started_vifs_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + bool active = false; int *count = (int *)data; - if (!vif->bss_conf.idle) + /* + * count active interfaces according to interface type. + * checking only bss_conf.idle is bad for some cases, e.g. + * we don't want to count sta in p2p_find as active interface. + */ + switch (wlvif->bss_type) { + case BSS_TYPE_STA_BSS: + if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + active = true; + break; + + case BSS_TYPE_AP_BSS: + if (wlvif->wl->active_sta_count > 0) + active = true; + break; + + default: + break; + } + + if (active) (*count)++; } @@ -166,26 +188,14 @@ wlcore_scan_get_channels(struct wl1271 *wl, flags = req_channels[i]->flags; if (force_passive) - flags |= IEEE80211_CHAN_PASSIVE_SCAN; + flags |= IEEE80211_CHAN_NO_IR; if ((req_channels[i]->band == band) && !(flags & IEEE80211_CHAN_DISABLED) && (!!(flags & IEEE80211_CHAN_RADAR) == radar) && /* if radar is set, we ignore the passive flag */ (radar || - !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) { - wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", - req_channels[i]->band, - req_channels[i]->center_freq); - wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", - req_channels[i]->hw_value, - req_channels[i]->flags); - wl1271_debug(DEBUG_SCAN, "max_power %d", - req_channels[i]->max_power); - wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d", - min_dwell_time_active, - max_dwell_time_active); - + !!(flags & IEEE80211_CHAN_NO_IR) == passive)) { if (flags & IEEE80211_CHAN_RADAR) { channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS; @@ -208,7 +218,7 @@ wlcore_scan_get_channels(struct wl1271 *wl, (band == IEEE80211_BAND_2GHZ) && (channels[j].channel >= 12) && (channels[j].channel <= 14) && - (flags & IEEE80211_CHAN_PASSIVE_SCAN) && + (flags & IEEE80211_CHAN_NO_IR) && !force_passive) { /* pactive channels treated as DFS */ channels[j].flags = SCAN_CHANNEL_FLAGS_DFS; @@ -222,6 +232,17 @@ wlcore_scan_get_channels(struct wl1271 *wl, *n_pactive_ch); } + wl1271_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s", + req_channels[i]->center_freq, + req_channels[i]->hw_value, + req_channels[i]->flags, + req_channels[i]->max_power, + min_dwell_time_active, + max_dwell_time_active, + flags & IEEE80211_CHAN_RADAR ? + ", DFS" : "", + flags & IEEE80211_CHAN_NO_IR ? + ", NO-IR" : ""); j++; } } @@ -364,7 +385,7 @@ wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, struct cfg80211_ssid *ssids = req->ssids; int ret = 0, type, i, j, n_match_ssids = 0; - wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list"); + wl1271_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list"); /* count the match sets that contain SSIDs */ for (i = 0; i < req->n_match_sets; i++) @@ -442,8 +463,6 @@ wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, } } - wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd)); - ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd, sizeof(*cmd), 0); if (ret < 0) { diff --git a/drivers/net/wireless/ti/wlcore/sdio.c b/drivers/net/wireless/ti/wlcore/sdio.c index 29ef2492951..d3dd7bfdf3f 100644 --- a/drivers/net/wireless/ti/wlcore/sdio.c +++ b/drivers/net/wireless/ti/wlcore/sdio.c @@ -217,7 +217,7 @@ static struct wl1271_if_operations sdio_ops = { static int wl1271_probe(struct sdio_func *func, const struct sdio_device_id *id) { - struct wlcore_platdev_data *pdev_data; + struct wlcore_platdev_data pdev_data; struct wl12xx_sdio_glue *glue; struct resource res[1]; mmc_pm_flag_t mmcflags; @@ -228,16 +228,13 @@ static int wl1271_probe(struct sdio_func *func, if (func->num != 0x02) return -ENODEV; - pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL); - if (!pdev_data) - goto out; - - pdev_data->if_ops = &sdio_ops; + memset(&pdev_data, 0x00, sizeof(pdev_data)); + pdev_data.if_ops = &sdio_ops; glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&func->dev, "can't allocate glue\n"); - goto out_free_pdev_data; + goto out; } glue->dev = &func->dev; @@ -248,9 +245,9 @@ static int wl1271_probe(struct sdio_func *func, /* Use block mode for transferring over one block size of data */ func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; - pdev_data->pdata = wl12xx_get_platform_data(); - if (IS_ERR(pdev_data->pdata)) { - ret = PTR_ERR(pdev_data->pdata); + pdev_data.pdata = wl12xx_get_platform_data(); + if (IS_ERR(pdev_data.pdata)) { + ret = PTR_ERR(pdev_data.pdata); dev_err(glue->dev, "missing wlan platform data: %d\n", ret); goto out_free_glue; } @@ -260,7 +257,7 @@ static int wl1271_probe(struct sdio_func *func, dev_dbg(glue->dev, "sdio PM caps = 0x%x\n", mmcflags); if (mmcflags & MMC_PM_KEEP_POWER) - pdev_data->pdata->pwr_in_suspend = true; + pdev_data.pdata->pwr_in_suspend = true; sdio_set_drvdata(func, glue); @@ -289,7 +286,7 @@ static int wl1271_probe(struct sdio_func *func, memset(res, 0x00, sizeof(res)); - res[0].start = pdev_data->pdata->irq; + res[0].start = pdev_data.pdata->irq; res[0].flags = IORESOURCE_IRQ; res[0].name = "irq"; @@ -299,8 +296,8 @@ static int wl1271_probe(struct sdio_func *func, goto out_dev_put; } - ret = platform_device_add_data(glue->core, pdev_data, - sizeof(*pdev_data)); + ret = platform_device_add_data(glue->core, &pdev_data, + sizeof(pdev_data)); if (ret) { dev_err(glue->dev, "can't add platform data\n"); goto out_dev_put; @@ -319,9 +316,6 @@ out_dev_put: out_free_glue: kfree(glue); -out_free_pdev_data: - kfree(pdev_data); - out: return ret; } diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 1b0cd98e35f..392c882b28f 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -24,11 +24,12 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/module.h> +#include <linux/slab.h> +#include <linux/swab.h> #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/wl12xx.h> #include <linux/platform_device.h> -#include <linux/slab.h> #include "wlcore.h" #include "wl12xx_80211.h" @@ -110,18 +111,16 @@ static void wl12xx_spi_reset(struct device *child) static void wl12xx_spi_init(struct device *child) { struct wl12xx_spi_glue *glue = dev_get_drvdata(child->parent); - u8 crc[WSPI_INIT_CMD_CRC_LEN], *cmd; struct spi_transfer t; struct spi_message m; + u8 *cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); - cmd = kzalloc(WSPI_INIT_CMD_LEN, GFP_KERNEL); if (!cmd) { dev_err(child->parent, "could not allocate cmd for spi init\n"); return; } - memset(crc, 0, sizeof(crc)); memset(&t, 0, sizeof(t)); spi_message_init(&m); @@ -129,30 +128,29 @@ static void wl12xx_spi_init(struct device *child) * Set WSPI_INIT_COMMAND * the data is being send from the MSB to LSB */ - cmd[2] = 0xff; - cmd[3] = 0xff; - cmd[1] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; - cmd[0] = 0; - cmd[7] = 0; - cmd[6] |= HW_ACCESS_WSPI_INIT_CMD_MASK << 3; - cmd[6] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + cmd[0] = 0xff; + cmd[1] = 0xff; + cmd[2] = WSPI_INIT_CMD_START | WSPI_INIT_CMD_TX; + cmd[3] = 0; + cmd[4] = 0; + cmd[5] = HW_ACCESS_WSPI_INIT_CMD_MASK << 3; + cmd[5] |= HW_ACCESS_WSPI_FIXED_BUSY_LEN & WSPI_INIT_CMD_FIXEDBUSY_LEN; + + cmd[6] = WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS + | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; if (HW_ACCESS_WSPI_FIXED_BUSY_LEN == 0) - cmd[5] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; + cmd[6] |= WSPI_INIT_CMD_DIS_FIXEDBUSY; else - cmd[5] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - - cmd[5] |= WSPI_INIT_CMD_IOD | WSPI_INIT_CMD_IP | WSPI_INIT_CMD_CS - | WSPI_INIT_CMD_WSPI | WSPI_INIT_CMD_WS; - - crc[0] = cmd[1]; - crc[1] = cmd[0]; - crc[2] = cmd[7]; - crc[3] = cmd[6]; - crc[4] = cmd[5]; + cmd[6] |= WSPI_INIT_CMD_EN_FIXEDBUSY; - cmd[4] |= crc7(0, crc, WSPI_INIT_CMD_CRC_LEN) << 1; - cmd[4] |= WSPI_INIT_CMD_END; + cmd[7] = crc7_be(0, cmd+2, WSPI_INIT_CMD_CRC_LEN) | WSPI_INIT_CMD_END; + /* + * The above is the logical order; it must actually be stored + * in the buffer byte-swapped. + */ + __swab32s((u32 *)cmd); + __swab32s((u32 *)cmd+1); t.tx_buf = cmd; t.len = WSPI_INIT_CMD_LEN; @@ -211,7 +209,7 @@ static int __must_check wl12xx_spi_raw_read(struct device *child, int addr, u32 chunk_len; while (len > 0) { - chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len); + chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len); cmd = &wl->buffer_cmd; busy_buf = wl->buffer_busyword; @@ -285,7 +283,7 @@ static int __must_check wl12xx_spi_raw_write(struct device *child, int addr, cmd = &commands[0]; i = 0; while (len > 0) { - chunk_len = min((size_t)WSPI_MAX_CHUNK_SIZE, len); + chunk_len = min_t(size_t, WSPI_MAX_CHUNK_SIZE, len); *cmd = 0; *cmd |= WSPI_CMD_WRITE; @@ -327,27 +325,25 @@ static struct wl1271_if_operations spi_ops = { static int wl1271_probe(struct spi_device *spi) { struct wl12xx_spi_glue *glue; - struct wlcore_platdev_data *pdev_data; + struct wlcore_platdev_data pdev_data; struct resource res[1]; int ret = -ENOMEM; - pdev_data = kzalloc(sizeof(*pdev_data), GFP_KERNEL); - if (!pdev_data) - goto out; + memset(&pdev_data, 0x00, sizeof(pdev_data)); - pdev_data->pdata = spi->dev.platform_data; - if (!pdev_data->pdata) { + pdev_data.pdata = dev_get_platdata(&spi->dev); + if (!pdev_data.pdata) { dev_err(&spi->dev, "no platform data\n"); ret = -ENODEV; - goto out_free_pdev_data; + goto out; } - pdev_data->if_ops = &spi_ops; + pdev_data.if_ops = &spi_ops; glue = kzalloc(sizeof(*glue), GFP_KERNEL); if (!glue) { dev_err(&spi->dev, "can't allocate glue\n"); - goto out_free_pdev_data; + goto out; } glue->dev = &spi->dev; @@ -385,8 +381,8 @@ static int wl1271_probe(struct spi_device *spi) goto out_dev_put; } - ret = platform_device_add_data(glue->core, pdev_data, - sizeof(*pdev_data)); + ret = platform_device_add_data(glue->core, &pdev_data, + sizeof(pdev_data)); if (ret) { dev_err(glue->dev, "can't add platform data\n"); goto out_dev_put; @@ -406,9 +402,6 @@ out_dev_put: out_free_glue: kfree(glue); -out_free_pdev_data: - kfree(pdev_data); - out: return ret; } diff --git a/drivers/net/wireless/ti/wlcore/sysfs.c b/drivers/net/wireless/ti/wlcore/sysfs.c index 8e583497940..24dd288d680 100644 --- a/drivers/net/wireless/ti/wlcore/sysfs.c +++ b/drivers/net/wireless/ti/wlcore/sysfs.c @@ -152,7 +152,7 @@ static ssize_t wl1271_sysfs_read_fwlog(struct file *filp, struct kobject *kobj, } /* Seeking is not supported - old logs are not kept. Disregard pos. */ - len = min(count, (size_t)wl->fwlog_size); + len = min_t(size_t, count, wl->fwlog_size); wl->fwlog_size -= len; memcpy(buffer, wl->fwlog, len); diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c index 527590f2adf..ddad58f614d 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.c +++ b/drivers/net/wireless/ti/wlcore/testmode.c @@ -179,7 +179,8 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) goto out_sleep; } - ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); + ret = wl1271_cmd_interrogate(wl, ie_id, cmd, + sizeof(struct acx_header), sizeof(*cmd)); if (ret < 0) { wl1271_warning("testmode cmd interrogate failed: %d", ret); goto out_free; @@ -297,7 +298,8 @@ static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) ret = wl1271_plt_stop(wl); break; case PLT_ON: - ret = wl1271_plt_start(wl, PLT_ON); + case PLT_CHIP_AWAKE: + ret = wl1271_plt_start(wl, val); break; case PLT_FEM_DETECT: ret = wl1271_tm_detect_fem(wl, tb); @@ -361,6 +363,7 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct wl1271 *wl = hw->priv; struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; + u32 nla_cmd; int err; err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); @@ -370,7 +373,14 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!tb[WL1271_TM_ATTR_CMD_ID]) return -EINVAL; - switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { + nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]); + + /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */ + if (wl->plt_mode == PLT_CHIP_AWAKE && + nla_cmd != WL1271_TM_CMD_SET_PLT_MODE) + return -EOPNOTSUPP; + + switch (nla_cmd) { case WL1271_TM_CMD_TEST: return wl1271_tm_cmd_test(wl, tb); case WL1271_TM_CMD_INTERROGATE: diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 7e93fe63a2c..40b43115f83 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -86,19 +86,34 @@ void wl1271_free_tx_id(struct wl1271 *wl, int id) EXPORT_SYMBOL(wl1271_free_tx_id); static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct sk_buff *skb) { struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *)(skb->data + + sizeof(struct wl1271_tx_hw_descr)); + if (!ieee80211_is_auth(hdr->frame_control)) + return; + /* * add the station to the known list before transmitting the * authentication response. this way it won't get de-authed by FW * when transmitting too soon. */ - hdr = (struct ieee80211_hdr *)(skb->data + - sizeof(struct wl1271_tx_hw_descr)); - if (ieee80211_is_auth(hdr->frame_control)) - wl1271_acx_set_inconnection_sta(wl, hdr->addr1); + wl1271_acx_set_inconnection_sta(wl, wlvif, hdr->addr1); + + /* + * ROC for 1 second on the AP channel for completing the connection. + * Note the ROC will be continued by the update_sta_state callbacks + * once the station reaches the associated state. + */ + wlcore_update_inconn_sta(wl, wlvif, NULL, true); + wlvif->pending_auth_reply_time = jiffies; + cancel_delayed_work(&wlvif->pending_auth_complete_work); + ieee80211_queue_delayed_work(wl->hw, + &wlvif->pending_auth_complete_work, + msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT)); } static void wl1271_tx_regulate_link(struct wl1271 *wl, @@ -119,12 +134,12 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, * into high-level PS and clean out its TX queues. * Make an exception if this is the only connected link. In this * case FW-memory congestion is less of a problem. - * Note that a single connected STA means 3 active links, since we must - * account for the global and broadcast AP links. The "fw_ps" check - * assures us the third link is a STA connected to the AP. Otherwise - * the FW would not set the PSM bit. + * Note that a single connected STA means 2*ap_count + 1 active links, + * since we must account for the global and broadcast AP links + * for each AP. The "fw_ps" check assures us the other link is a STA + * connected to the AP. Otherwise the FW would not set the PSM bit. */ - if (wl->active_link_count > 3 && fw_ps && + if (wl->active_link_count > (wl->ap_count*2 + 1) && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl12xx_ps_link_start(wl, wlvif, hlid, true); } @@ -219,8 +234,13 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl->tx_blocks_available -= total_blocks; wl->tx_allocated_blocks += total_blocks; - /* If the FW was empty before, arm the Tx watchdog */ - if (wl->tx_allocated_blocks == total_blocks) + /* + * If the FW was empty before, arm the Tx watchdog. Also do + * this on the first Tx after resume, as we always cancel the + * watchdog on suspend. + */ + if (wl->tx_allocated_blocks == total_blocks || + test_and_clear_bit(WL1271_FLAG_REINIT_TX_WDOG, &wl->flags)) wl12xx_rearm_tx_watchdog_locked(wl); ac = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); @@ -342,6 +362,10 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif, ieee80211_has_protected(frame_control)) tx_attr |= TX_HW_ATTR_HOST_ENCRYPT; + /* send EAPOL frames as voice */ + if (control->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) + tx_attr |= TX_HW_ATTR_EAPOL_FRAME; + desc->tx_attr = cpu_to_le16(tx_attr); wlcore_hw_set_tx_desc_csum(wl, desc, skb); @@ -386,7 +410,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104); - if (WARN_ON(is_wep && wlvif->default_key != idx)) { + if (WARN_ON(is_wep && wlvif && wlvif->default_key != idx)) { ret = wl1271_set_default_wep_key(wl, wlvif, idx); if (ret < 0) return ret; @@ -404,7 +428,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid); if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) { - wl1271_tx_ap_update_inconnection_sta(wl, skb); + wl1271_tx_ap_update_inconnection_sta(wl, wlvif, skb); wl1271_tx_regulate_link(wl, wlvif, hlid); } @@ -545,11 +569,11 @@ static struct sk_buff *wlcore_vif_dequeue_high_prio(struct wl1271 *wl, int i, h, start_hlid; /* start from the link after the last one */ - start_hlid = (wlvif->last_tx_hlid + 1) % WL12XX_MAX_LINKS; + start_hlid = (wlvif->last_tx_hlid + 1) % wl->num_links; /* dequeue according to AC, round robin on each link */ - for (i = 0; i < WL12XX_MAX_LINKS; i++) { - h = (start_hlid + i) % WL12XX_MAX_LINKS; + for (i = 0; i < wl->num_links; i++) { + h = (start_hlid + i) % wl->num_links; /* only consider connected stations */ if (!test_bit(h, wlvif->links_map)) @@ -673,8 +697,8 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif, skb_queue_head(&wl->links[hlid].tx_queue[q], skb); /* make sure we dequeue the same packet next time */ - wlvif->last_tx_hlid = (hlid + WL12XX_MAX_LINKS - 1) % - WL12XX_MAX_LINKS; + wlvif->last_tx_hlid = (hlid + wl->num_links - 1) % + wl->num_links; } spin_lock_irqsave(&wl->wl_lock, flags); @@ -707,7 +731,7 @@ void wl12xx_rearm_rx_streaming(struct wl1271 *wl, unsigned long *active_hlids) timeout = wl->conf.rx_streaming.duration; wl12xx_for_each_wlvif_sta(wl, wlvif) { bool found = false; - for_each_set_bit(hlid, active_hlids, WL12XX_MAX_LINKS) { + for_each_set_bit(hlid, active_hlids, wl->num_links) { if (test_bit(hlid, wlvif->links_map)) { found = true; break; @@ -744,7 +768,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl) struct wl1271_tx_hw_descr *desc; u32 buf_offset = 0, last_len = 0; bool sent_packets = false; - unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0}; + unsigned long active_hlids[BITS_TO_LONGS(WLCORE_MAX_LINKS)] = {0}; int ret = 0; int bus_ret = 0; u8 hlid; @@ -1046,7 +1070,7 @@ void wl12xx_tx_reset_wlvif(struct wl1271 *wl, struct wl12xx_vif *wlvif) int i; /* TX failure */ - for_each_set_bit(i, wlvif->links_map, WL12XX_MAX_LINKS) { + for_each_set_bit(i, wlvif->links_map, wl->num_links) { if (wlvif->bss_type == BSS_TYPE_AP_BSS && i != wlvif->ap.bcast_hlid && i != wlvif->ap.global_hlid) { /* this calls wl12xx_free_link */ @@ -1070,7 +1094,7 @@ void wl12xx_tx_reset(struct wl1271 *wl) /* only reset the queues if something bad happened */ if (wl1271_tx_total_queue_count(wl) != 0) { - for (i = 0; i < WL12XX_MAX_LINKS; i++) + for (i = 0; i < wl->num_links; i++) wl1271_tx_reset_link_queues(wl, i); for (i = 0; i < NUM_TX_QUEUES; i++) @@ -1163,7 +1187,7 @@ void wl1271_tx_flush(struct wl1271 *wl) WL1271_TX_FLUSH_TIMEOUT / 1000); /* forcibly flush all Tx buffers on our queues */ - for (i = 0; i < WL12XX_MAX_LINKS; i++) + for (i = 0; i < wl->num_links; i++) wl1271_tx_reset_link_queues(wl, i); out_wake: diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 55aa4acf910..79cb3ff8b71 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -37,6 +37,7 @@ #define TX_HW_ATTR_TX_CMPLT_REQ BIT(12) #define TX_HW_ATTR_TX_DUMMY_REQ BIT(13) #define TX_HW_ATTR_HOST_ENCRYPT BIT(14) +#define TX_HW_ATTR_EAPOL_FRAME BIT(15) #define TX_HW_ATTR_OFST_SAVE_RETRIES 0 #define TX_HW_ATTR_OFST_HEADER_PAD 1 @@ -56,6 +57,9 @@ /* Used for management frames and dummy packets */ #define WL1271_TID_MGMT 7 +/* stop a ROC for pending authentication reply after this time (ms) */ +#define WLCORE_PEND_AUTH_ROC_TIMEOUT 1000 + struct wl127x_tx_mem { /* * Number of extra memory blocks to allocate for this packet diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 0034979e97c..95a54504f0c 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -73,6 +73,8 @@ struct wlcore_ops { void (*tx_immediate_compl)(struct wl1271 *wl); int (*hw_init)(struct wl1271 *wl); int (*init_vif)(struct wl1271 *wl, struct wl12xx_vif *wlvif); + void (*convert_fw_status)(struct wl1271 *wl, void *raw_fw_status, + struct wl_fw_status *fw_status); u32 (*sta_get_ap_rate_mask)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*get_pg_ver)(struct wl1271 *wl, s8 *ver); @@ -110,6 +112,7 @@ struct wlcore_ops { struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u32 rate_set, u8 hlid); + u32 (*convert_hwaddr)(struct wl1271 *wl, u32 hwaddr); bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk); bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, @@ -219,7 +222,7 @@ struct wl1271 { int channel; u8 system_hlid; - unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; + unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)]; unsigned long roles_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)]; unsigned long roc_map[BITS_TO_LONGS(WL12XX_MAX_ROLES)]; unsigned long rate_policies_map[ @@ -227,7 +230,7 @@ struct wl1271 { unsigned long klv_templates_map[ BITS_TO_LONGS(WLCORE_MAX_KLV_TEMPLATES)]; - u8 session_ids[WL12XX_MAX_LINKS]; + u8 session_ids[WLCORE_MAX_LINKS]; struct list_head wlvif_list; @@ -290,6 +293,12 @@ struct wl1271 { /* Number of valid bytes in the FW log buffer */ ssize_t fwlog_size; + /* FW log end marker */ + u32 fwlog_end; + + /* FW memory block size */ + u32 fw_mem_block_size; + /* Sysfs FW log entry readers wait queue */ wait_queue_head_t fwlog_waitq; @@ -307,6 +316,8 @@ struct wl1271 { /* The mbox event mask */ u32 event_mask; + /* events to unmask only when ap interface is up */ + u32 ap_event_mask; /* Mailbox pointers */ u32 mbox_size; @@ -337,8 +348,8 @@ struct wl1271 { u32 buffer_cmd; u32 buffer_busyword[WL1271_BUSY_WORD_CNT]; - struct wl_fw_status_1 *fw_status_1; - struct wl_fw_status_2 *fw_status_2; + void *raw_fw_status; + struct wl_fw_status *fw_status; struct wl1271_tx_hw_res_if *tx_res_if; /* Current chipset configuration */ @@ -367,7 +378,7 @@ struct wl1271 { * AP-mode - links indexed by HLID. The global and broadcast links * are always active. */ - struct wl1271_link links[WL12XX_MAX_LINKS]; + struct wl1271_link links[WLCORE_MAX_LINKS]; /* number of currently active links */ int active_link_count; @@ -396,6 +407,9 @@ struct wl1271 { /* AP-mode - number of currently connected stations */ int active_sta_count; + /* Flag determining whether AP should broadcast OFDM-only rates */ + bool ofdm_only_ap; + /* last wlvif we transmitted from */ struct wl12xx_vif *last_wlvif; @@ -425,6 +439,10 @@ struct wl1271 { u32 num_tx_desc; /* number of RX descriptors the HW supports. */ u32 num_rx_desc; + /* number of links the HW supports */ + u8 num_links; + /* max stations a single AP can support */ + u8 max_ap_stations; /* translate HW Tx rates to standard rate-indices */ const u8 **band_rate_to_idx; @@ -439,10 +457,11 @@ struct wl1271 { struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS]; /* size of the private FW status data */ + size_t fw_status_len; size_t fw_status_priv_len; /* RX Data filter rule state - enabled/disabled */ - bool rx_filter_enabled[WL1271_MAX_RX_FILTERS]; + unsigned long rx_filter_enabled[BITS_TO_LONGS(WL1271_MAX_RX_FILTERS)]; /* size of the private static data */ size_t static_data_priv_len; @@ -467,8 +486,9 @@ struct wl1271 { struct completion nvs_loading_complete; - /* number of concurrent channels the HW supports */ - u32 num_channels; + /* interface combinations supported by the hw */ + const struct ieee80211_iface_combination *iface_combinations; + u8 n_iface_combinations; }; int wlcore_probe(struct wl1271 *wl, struct platform_device *pdev); @@ -481,6 +501,8 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); void wlcore_regdomain_config(struct wl1271 *wl); +void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct wl1271_station *wl_sta, bool in_conn); static inline void wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index e5e146435fe..c2c34a84ff3 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -58,10 +58,15 @@ #define WL1271_DEFAULT_DTIM_PERIOD 1 #define WL12XX_MAX_ROLES 4 -#define WL12XX_MAX_LINKS 12 #define WL12XX_INVALID_ROLE_ID 0xff #define WL12XX_INVALID_LINK_ID 0xff +/* + * max number of links allowed by all HWs. + * this is NOT the actual max links supported by the current hw. + */ +#define WLCORE_MAX_LINKS 16 + /* the driver supports the 2.4Ghz and 5Ghz bands */ #define WLCORE_NUM_BANDS 2 @@ -118,72 +123,58 @@ struct wl1271_chip { #define NUM_TX_QUEUES 4 -#define AP_MAX_STATIONS 8 - -struct wl_fw_packet_counters { - /* Cumulative counter of released packets per AC */ - u8 tx_released_pkts[NUM_TX_QUEUES]; - - /* Cumulative counter of freed packets per HLID */ - u8 tx_lnk_free_pkts[WL12XX_MAX_LINKS]; - - /* Cumulative counter of released Voice memory blocks */ - u8 tx_voice_released_blks; - - /* Tx rate of the last transmitted packet */ - u8 tx_last_rate; - - u8 padding[2]; -} __packed; - -/* FW status registers */ -struct wl_fw_status_1 { - __le32 intr; +struct wl_fw_status { + u32 intr; u8 fw_rx_counter; u8 drv_rx_counter; - u8 reserved; u8 tx_results_counter; - __le32 rx_pkt_descs[0]; -} __packed; - -/* - * Each HW arch has a different number of Rx descriptors. - * The length of the status depends on it, since it holds an array - * of descriptors. - */ -#define WLCORE_FW_STATUS_1_LEN(num_rx_desc) \ - (sizeof(struct wl_fw_status_1) + \ - (sizeof(((struct wl_fw_status_1 *)0)->rx_pkt_descs[0])) * \ - num_rx_desc) + __le32 *rx_pkt_descs; -struct wl_fw_status_2 { - __le32 fw_localtime; + u32 fw_localtime; /* * A bitmap (where each bit represents a single HLID) * to indicate if the station is in PS mode. */ - __le32 link_ps_bitmap; + u32 link_ps_bitmap; /* * A bitmap (where each bit represents a single HLID) to indicate * if the station is in Fast mode */ - __le32 link_fast_bitmap; + u32 link_fast_bitmap; /* Cumulative counter of total released mem blocks since FW-reset */ - __le32 total_released_blks; + u32 total_released_blks; /* Size (in Memory Blocks) of TX pool */ - __le32 tx_total; + u32 tx_total; - struct wl_fw_packet_counters counters; + struct { + /* + * Cumulative counter of released packets per AC + * (length of the array is NUM_TX_QUEUES) + */ + u8 *tx_released_pkts; + + /* + * Cumulative counter of freed packets per HLID + * (length of the array is wl->num_links) + */ + u8 *tx_lnk_free_pkts; + + /* Cumulative counter of released Voice memory blocks */ + u8 tx_voice_released_blks; - __le32 log_start_addr; + /* Tx rate of the last transmitted packet */ + u8 tx_last_rate; + } counters; + + u32 log_start_addr; /* Private status to be used by the lower drivers */ - u8 priv[0]; -} __packed; + void *priv; +}; #define WL1271_MAX_CHANNELS 64 struct wl1271_scan { @@ -240,6 +231,7 @@ enum wl12xx_flags { WL1271_FLAG_VIF_CHANGE_IN_PROGRESS, WL1271_FLAG_INTENDED_FW_RECOVERY, WL1271_FLAG_IO_FAILED, + WL1271_FLAG_REINIT_TX_WDOG, }; enum wl12xx_vif_flags { @@ -255,6 +247,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_CS_PROGRESS, WLVIF_FLAG_AP_PROBE_RESP_SET, WLVIF_FLAG_IN_USE, + WLVIF_FLAG_ACTIVE, }; struct wl12xx_vif; @@ -307,6 +300,7 @@ enum plt_mode { PLT_OFF = 0, PLT_ON = 1, PLT_FEM_DETECT = 2, + PLT_CHIP_AWAKE = 3 }; struct wl12xx_rx_filter_field { @@ -366,7 +360,7 @@ struct wl12xx_vif { /* HLIDs bitmap of associated stations */ unsigned long sta_hlid_map[BITS_TO_LONGS( - WL12XX_MAX_LINKS)]; + WLCORE_MAX_LINKS)]; /* recoreded keys - set here before AP startup */ struct wl1271_ap_key *recorded_keys[MAX_NUM_KEYS]; @@ -383,7 +377,7 @@ struct wl12xx_vif { /* counters of packets per AC, across all links in the vif */ int tx_queue_count[NUM_TX_QUEUES]; - unsigned long links_map[BITS_TO_LONGS(WL12XX_MAX_LINKS)]; + unsigned long links_map[BITS_TO_LONGS(WLCORE_MAX_LINKS)]; u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 ssid_len; @@ -456,6 +450,15 @@ struct wl12xx_vif { */ int hw_queue_base; + /* do we have a pending auth reply? (and ROC) */ + bool ap_pending_auth_reply; + + /* time when we sent the pending auth reply */ + unsigned long pending_auth_reply_time; + + /* work for canceling ROC after pending auth reply */ + struct delayed_work pending_auth_complete_work; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) @@ -509,8 +512,8 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif); void wl12xx_queue_recovery_work(struct wl1271 *wl); size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen); int wl1271_rx_filter_alloc_field(struct wl12xx_rx_filter *filter, - u16 offset, u8 flags, - u8 *pattern, u8 len); + u16 offset, u8 flags, + const u8 *pattern, u8 len); void wl1271_rx_filter_free(struct wl12xx_rx_filter *filter); struct wl12xx_rx_filter *wl1271_rx_filter_alloc(void); int wl1271_rx_filter_get_fields_size(struct wl12xx_rx_filter *filter); @@ -539,6 +542,4 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, #define HW_HT_RATES_OFFSET 16 #define HW_MIMO_RATES_OFFSET 24 -#define WL12XX_HW_BLOCK_SIZE 256 - #endif /* __WLCORE_I_H__ */ diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 38d2089f338..d5c371d77dd 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -29,7 +29,6 @@ #include <linux/delay.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/in.h> #include <linux/kernel.h> @@ -43,7 +42,7 @@ #include <linux/slab.h> #include <linux/string.h> #include <linux/wireless.h> -#include <linux/ieee80211.h> +#include <net/cfg80211.h> #include <net/iw_handler.h> @@ -673,8 +672,7 @@ static void wl3501_mgmt_scan_confirm(struct wl3501_card *this, u16 addr) matchflag = 1; if (matchflag) { for (i = 0; i < this->bss_cnt; i++) { - if (!memcmp(this->bss_set[i].bssid, - sig.bssid, ETH_ALEN)) { + if (ether_addr_equal_unaligned(this->bss_set[i].bssid, sig.bssid)) { matchflag = 0; break; } @@ -1455,7 +1453,8 @@ static int wl3501_get_freq(struct net_device *dev, struct iw_request_info *info, { struct wl3501_card *this = netdev_priv(dev); - wrqu->freq.m = ieee80211_dsss_chan_to_freq(this->chan) * 100000; + wrqu->freq.m = 100000 * + ieee80211_channel_to_frequency(this->chan, IEEE80211_BAND_2GHZ); wrqu->freq.e = 1; return 0; } diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index d39c4178c33..6f5c793a785 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -18,7 +18,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/wireless.h> -#include <linux/ieee80211.h> +#include <net/cfg80211.h> #include <net/iw_handler.h> #include <linux/string.h> #include <linux/if_arp.h> @@ -914,11 +914,8 @@ static int zd1201_set_freq(struct net_device *dev, if (freq->e == 0) channel = freq->m; - else { - channel = ieee80211_freq_to_dsss_chan(freq->m); - if (channel < 0) - channel = 0; - } + else + channel = ieee80211_frequency_to_channel(freq->m); err = zd1201_setconfig16(zd, ZD1201_RID_CNFOWNCHANNEL, channel); if (err) diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 71ab320fae8..73a49b86803 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ /* This file implements all the hardware specific functions for the ZD1211 diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index 7ab922209b2..b03786c9f3a 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _ZD_CHIP_H diff --git a/drivers/net/wireless/zd1211rw/zd_def.h b/drivers/net/wireless/zd1211rw/zd_def.h index 9a1b013f81b..41bd755bc13 100644 --- a/drivers/net/wireless/zd1211rw/zd_def.h +++ b/drivers/net/wireless/zd1211rw/zd_def.h @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _ZD_DEF_H diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index c6208a7988e..e7af261e919 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -16,8 +16,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/netdevice.h> @@ -533,9 +532,8 @@ void zd_mac_tx_failed(struct urb *urb) tx_hdr = (struct ieee80211_hdr *)skb->data; /* we skip all frames not matching the reported destination */ - if (unlikely(memcmp(tx_hdr->addr1, tx_status->mac, ETH_ALEN))) { + if (unlikely(!ether_addr_equal(tx_hdr->addr1, tx_status->mac))) continue; - } /* we skip all frames not matching the reported final rate */ @@ -998,7 +996,7 @@ static int filter_ack(struct ieee80211_hw *hw, struct ieee80211_hdr *rx_hdr, continue; tx_hdr = (struct ieee80211_hdr *)skb->data; - if (likely(!memcmp(tx_hdr->addr2, rx_hdr->addr1, ETH_ALEN))) + if (likely(ether_addr_equal(tx_hdr->addr2, rx_hdr->addr1))) { found = 1; break; diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h index c01eca859f9..5a484235308 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.h +++ b/drivers/net/wireless/zd1211rw/zd_mac.h @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _ZD_MAC_H diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c index c875ee05e22..dc179c41451 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.c +++ b/drivers/net/wireless/zd1211rw/zd_rf.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/errno.h> diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h index 725b7c99b23..8f14e25e104 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.h +++ b/drivers/net/wireless/zd1211rw/zd_rf.h @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _ZD_RF_H diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c index 12babcb633c..99aed7d7895 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c index 385c670d129..5fea485be57 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c index 784d9ccb8fe..a93f657a41c 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> diff --git a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c b/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c index c4d324e19c2..61b92402735 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_uw2453.c @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 7ef0b4a181e..a912dc05111 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -15,8 +15,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/kernel.h> @@ -1619,7 +1618,7 @@ static void prepare_read_regs_int(struct zd_usb *usb, atomic_set(&intr->read_regs_enabled, 1); intr->read_regs.req = req; intr->read_regs.req_count = count; - INIT_COMPLETION(intr->read_regs.completion); + reinit_completion(&intr->read_regs.completion); spin_unlock_irq(&intr->lock); } diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h index 45e3bb28a01..a9075f22517 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.h +++ b/drivers/net/wireless/zd1211rw/zd_usb.h @@ -14,8 +14,7 @@ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #ifndef _ZD_USB_H |
