<title>functions/definitions</title>
!Finclude/net/mac80211.h ieee80211_rx_status
!Finclude/net/mac80211.h mac80211_rx_flags
+!Finclude/net/mac80211.h mac80211_tx_info_flags
!Finclude/net/mac80211.h mac80211_tx_control_flags
!Finclude/net/mac80211.h mac80211_rate_control_flags
!Finclude/net/mac80211.h ieee80211_tx_rate
INTEL WIRELESS WIFI LINK (iwlwifi)
M: Johannes Berg <johannes.berg@intel.com>
-M: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+M: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
M: Intel Linux Wireless <ilw@linux.intel.com>
L: linux-wireless@vger.kernel.org
W: http://intellinuxwireless.org
M: Samuel Ortiz <sameo@linux.intel.com>
L: linux-wireless@vger.kernel.org
L: linux-nfc@lists.01.org (moderated for non-subscribers)
-S: Maintained
+S: Supported
F: net/nfc/
F: include/net/nfc/
F: include/uapi/linux/nfc.h
PCI core hostmode operation (external PCI bus).
config BCMA_HOST_SOC
- bool
- depends on BCMA_DRIVER_MIPS
+ bool "Support for BCMA in a SoC"
+ depends on BCMA
+ help
+ Host interface for a Broadcom AIX bus directly mapped into
+ the memory. This only works with the Broadcom SoCs from the
+ BCM47XX line.
+
+ If unsure, say N
config BCMA_DRIVER_MIPS
bool "BCMA Broadcom MIPS core driver"
pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
}
-static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
+static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u16 phy)
{
u32 v;
int i;
}
}
-static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u8 device, u8 address)
+static u16 bcma_pcie_mdio_read(struct bcma_drv_pci *pc, u16 device, u8 address)
{
int max_retries = 10;
u16 ret = 0;
return ret;
}
-static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u8 device,
+static void bcma_pcie_mdio_write(struct bcma_drv_pci *pc, u16 device,
u8 address, u16 data)
{
int max_retries = 10;
pcicore_write32(pc, BCMA_CORE_PCI_MDIO_CONTROL, 0);
}
+static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device,
+ u8 address, u16 data)
+{
+ bcma_pcie_mdio_write(pc, device, address, data);
+ return bcma_pcie_mdio_read(pc, device, address);
+}
+
/**************************************************
* Workarounds.
**************************************************/
}
}
+static void bcma_core_pci_power_save(struct bcma_drv_pci *pc, bool up)
+{
+ u16 data;
+
+ if (pc->core->id.rev >= 15 && pc->core->id.rev <= 20) {
+ data = up ? 0x74 : 0x7C;
+ bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+ BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7F64);
+ bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+ BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
+ } else if (pc->core->id.rev >= 21 && pc->core->id.rev <= 22) {
+ data = up ? 0x75 : 0x7D;
+ bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+ BCMA_CORE_PCI_MDIO_BLK1_MGMT1, 0x7E65);
+ bcma_pcie_mdio_writeread(pc, BCMA_CORE_PCI_MDIO_BLK1,
+ BCMA_CORE_PCI_MDIO_BLK1_MGMT3, data);
+ }
+}
+
/**************************************************
* Init.
**************************************************/
}
EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
-void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
+static void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
{
u32 w;
bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
}
-EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer);
+
+void bcma_core_pci_up(struct bcma_bus *bus)
+{
+ struct bcma_drv_pci *pc;
+
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+ return;
+
+ pc = &bus->drv_pci[0];
+
+ bcma_core_pci_power_save(pc, true);
+
+ bcma_core_pci_extend_L1timer(pc, true);
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_up);
+
+void bcma_core_pci_down(struct bcma_bus *bus)
+{
+ struct bcma_drv_pci *pc;
+
+ if (bus->hosttype != BCMA_HOSTTYPE_PCI)
+ return;
+
+ pc = &bus->drv_pci[0];
+
+ bcma_core_pci_extend_L1timer(pc, false);
+
+ bcma_core_pci_power_save(pc, false);
+}
+EXPORT_SYMBOL_GPL(bcma_core_pci_down);
int bcma_core_pci_plat_dev_init(struct pci_dev *dev)
{
struct bcma_drv_pci_host *pc_host;
+ int readrq;
if (dev->bus->ops->read != bcma_core_pci_hostmode_read_config) {
/* This is not a device on the PCI-core bridge. */
dev->irq = bcma_core_irq(pc_host->pdev->core);
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
+ readrq = pcie_get_readrq(dev);
+ if (readrq > 128) {
+ pr_info("change PCIe max read request size from %i to 128\n", readrq);
+ pcie_set_readrq(dev, 128);
+ }
return 0;
}
EXPORT_SYMBOL(bcma_core_pci_plat_dev_init);
err = bcma_bus_scan(bus);
if (err) {
bcma_err(bus, "Failed to scan: %d\n", err);
- return -1;
+ return err;
}
/* Early init CC core */
{ BCMA_CORE_4706_CHIPCOMMON, "BCM4706 ChipCommon" },
{ BCMA_CORE_4706_SOC_RAM, "BCM4706 SOC RAM" },
{ BCMA_CORE_4706_MAC_GBIT, "BCM4706 GBit MAC" },
+ { BCMA_CORE_PCIEG2, "PCIe Gen 2" },
+ { BCMA_CORE_DMA, "DMA" },
+ { BCMA_CORE_SDIO3, "SDIO3" },
+ { BCMA_CORE_USB20, "USB 2.0" },
+ { BCMA_CORE_USB30, "USB 3.0" },
+ { BCMA_CORE_A9JTAG, "ARM Cortex A9 JTAG" },
+ { BCMA_CORE_DDR23, "Denali DDR2/DDR3 memory controller" },
+ { BCMA_CORE_ROM, "ROM" },
+ { BCMA_CORE_NAND, "NAND flash controller" },
+ { BCMA_CORE_QSPI, "SPI flash controller" },
+ { BCMA_CORE_CHIPCOMMON_B, "Chipcommon B" },
+ { BCMA_CORE_ARMCA9, "ARM Cortex A9 core (ihost)" },
{ BCMA_CORE_AMEMC, "AMEMC (DDR)" },
{ BCMA_CORE_ALTA, "ALTA (I2S)" },
{ BCMA_CORE_INVALID, "Invalid" },
return ent;
}
-static s32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
+static u32 bcma_erom_get_addr_desc(struct bcma_bus *bus, u32 __iomem **eromptr,
u32 type, u8 port)
{
u32 addrl, addrh, sizel, sizeh = 0;
((ent & SCAN_ADDR_TYPE) != type) ||
(((ent & SCAN_ADDR_PORT) >> SCAN_ADDR_PORT_SHIFT) != port)) {
bcma_erom_push_ent(eromptr);
- return -EINVAL;
+ return (u32)-EINVAL;
}
addrl = ent & SCAN_ADDR_ADDR;
struct bcma_device_id *match, int core_num,
struct bcma_device *core)
{
- s32 tmp;
+ u32 tmp;
u8 i, j;
s32 cia, cib;
u8 ports[2], wrappers[2];
* the main register space for the core
*/
tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0);
- if (tmp <= 0) {
+ if (tmp == 0 || IS_ERR_VALUE(tmp)) {
/* Try again to see if it is a bridge */
tmp = bcma_erom_get_addr_desc(bus, eromptr,
SCAN_ADDR_TYPE_BRIDGE, 0);
- if (tmp <= 0) {
+ if (tmp == 0 || IS_ERR_VALUE(tmp)) {
return -EILSEQ;
} else {
bcma_info(bus, "Bridge found\n");
for (j = 0; ; j++) {
tmp = bcma_erom_get_addr_desc(bus, eromptr,
SCAN_ADDR_TYPE_SLAVE, i);
- if (tmp < 0) {
+ if (IS_ERR_VALUE(tmp)) {
/* no more entries for port _i_ */
/* pr_debug("erom: slave port %d "
* "has %d descriptors\n", i, j); */
for (j = 0; ; j++) {
tmp = bcma_erom_get_addr_desc(bus, eromptr,
SCAN_ADDR_TYPE_MWRAP, i);
- if (tmp < 0) {
+ if (IS_ERR_VALUE(tmp)) {
/* no more entries for port _i_ */
/* pr_debug("erom: master wrapper %d "
* "has %d descriptors\n", i, j); */
for (j = 0; ; j++) {
tmp = bcma_erom_get_addr_desc(bus, eromptr,
SCAN_ADDR_TYPE_SWRAP, i + hack);
- if (tmp < 0) {
+ if (IS_ERR_VALUE(tmp)) {
/* no more entries for port _i_ */
/* pr_debug("erom: master wrapper %d "
* has %d descriptors\n", i, j); */
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
- ret = strict_strtol(buf, 10, &result);
+ ret = kstrtol(buf, 10, &result);
if (ret)
return ret;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
- ret = strict_strtol(buf, 10, &result);
+ ret = kstrtol(buf, 10, &result);
if (ret)
return ret;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
- ret = strict_strtol(buf, 10, &result);
+ ret = kstrtol(buf, 10, &result);
if (ret)
return ret;
if (firmwarelen - offset < txlen)
txlen = firmwarelen - offset;
- tx_blocks = (txlen + blksz_dl - 1) / blksz_dl;
+ tx_blocks = DIV_ROUND_UP(txlen, blksz_dl);
memcpy(fwbuf, &firmware[offset], txlen);
}
}
blksz = SDIO_BLOCK_SIZE;
- buf_block_len = (nb + blksz - 1) / blksz;
+ buf_block_len = DIV_ROUND_UP(nb, blksz);
sdio_claim_host(card->func);
config BGMAC
tristate "BCMA bus GBit core support"
- depends on BCMA_HOST_SOC && HAS_DMA
+ depends on BCMA_HOST_SOC && HAS_DMA && BCM47XX
---help---
This driver supports GBit MAC and BCM4706 GBit MAC cores on BCMA bus.
They can be found on BCM47xx SoCs and provide gigabit ethernet.
bool btcoex_enabled;
bool disable_ani;
- bool antenna_diversity;
+ bool bt_ant_diversity;
};
struct sk_buff *ath_rxbuf_alloc(struct ath_common *common,
#include "debug.h"
#include "htc.h"
+void ath10k_bmi_start(struct ath10k *ar)
+{
+ ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n");
+ ar->bmi.done_sent = false;
+}
+
int ath10k_bmi_done(struct ath10k *ar)
{
struct bmi_cmd cmd;
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,
&resp, &rxlen);
if (ret) {
- ath10k_warn("unable to read from the device\n");
+ ath10k_warn("unable to read from the device (%d)\n",
+ ret);
return ret;
}
ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,
NULL, NULL);
if (ret) {
- ath10k_warn("unable to write to the device\n");
+ ath10k_warn("unable to write to the device (%d)\n",
+ ret);
return ret;
}
#define BMI_CE_NUM_TO_TARG 0
#define BMI_CE_NUM_TO_HOST 1
+void ath10k_bmi_start(struct ath10k *ar);
int ath10k_bmi_done(struct ath10k *ar);
int ath10k_bmi_get_target_info(struct ath10k *ar,
struct bmi_target_info *target_info);
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
void __iomem *indicator_addr;
- if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+ 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;
}
ath10k_pci_wake(ar);
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;
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;
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_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;
goto conn_fail;
/* Start HTC */
- status = ath10k_htc_start(ar->htc);
+ status = ath10k_htc_start(&ar->htc);
if (status)
goto conn_fail;
return 0;
timeout:
- ath10k_htc_stop(ar->htc);
+ ath10k_htc_stop(&ar->htc);
conn_fail:
return status;
}
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;
- const struct firmware *fw;
int ret;
- fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
- ar->hw_params.fw.board);
- if (IS_ERR(fw)) {
- ath10k_err("could not fetch board data fw file (%ld)\n",
- PTR_ERR(fw));
- return PTR_ERR(fw);
- }
-
ret = ath10k_push_board_ext_data(ar, fw);
if (ret) {
ath10k_err("could not push board ext data (%d)\n", ret);
}
exit:
- release_firmware(fw);
return ret;
}
static int ath10k_download_and_run_otp(struct ath10k *ar)
{
- const struct firmware *fw;
- u32 address;
+ const struct firmware *fw = ar->otp;
+ u32 address = ar->hw_params.patch_load_addr;
u32 exec_param;
int ret;
/* OTP is optional */
- if (ar->hw_params.fw.otp == NULL) {
- ath10k_info("otp file not defined\n");
- return 0;
- }
-
- address = ar->hw_params.patch_load_addr;
-
- fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
- ar->hw_params.fw.otp);
- if (IS_ERR(fw)) {
- ath10k_warn("could not fetch otp (%ld)\n", PTR_ERR(fw));
+ if (!ar->otp)
return 0;
- }
ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
if (ret) {
}
exit:
- release_firmware(fw);
return ret;
}
static int ath10k_download_fw(struct ath10k *ar)
{
- const struct firmware *fw;
+ const struct firmware *fw = ar->firmware;
u32 address;
int ret;
- if (ar->hw_params.fw.fw == NULL)
- return -EINVAL;
-
address = ar->hw_params.patch_load_addr;
- fw = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
- ar->hw_params.fw.fw);
- if (IS_ERR(fw)) {
- ath10k_err("could not fetch fw (%ld)\n", PTR_ERR(fw));
- return PTR_ERR(fw);
- }
-
ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size);
if (ret) {
ath10k_err("could not write fw (%d)\n", ret);
}
exit:
- release_firmware(fw);
+ return ret;
+}
+
+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->otp && !IS_ERR(ar->otp))
+ release_firmware(ar->otp);
+
+ if (ar->firmware && !IS_ERR(ar->firmware))
+ release_firmware(ar->firmware);
+
+ ar->board_data = NULL;
+ ar->otp = NULL;
+ ar->firmware = NULL;
+}
+
+static int ath10k_core_fetch_firmware_files(struct ath10k *ar)
+{
+ int ret = 0;
+
+ if (ar->hw_params.fw.fw == NULL) {
+ ath10k_err("firmware file not defined\n");
+ return -EINVAL;
+ }
+
+ if (ar->hw_params.fw.board == NULL) {
+ ath10k_err("board data file not defined");
+ 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);
+ ath10k_err("could not fetch board data (%d)\n", ret);
+ goto err;
+ }
+
+ ar->firmware = ath10k_fetch_fw_file(ar,
+ ar->hw_params.fw.dir,
+ ar->hw_params.fw.fw);
+ if (IS_ERR(ar->firmware)) {
+ ret = PTR_ERR(ar->firmware);
+ ath10k_err("could not fetch firmware (%d)\n", ret);
+ goto err;
+ }
+
+ /* OTP may be undefined. If so, don't fetch it at all */
+ if (ar->hw_params.fw.otp == NULL)
+ return 0;
+
+ ar->otp = ath10k_fetch_fw_file(ar,
+ ar->hw_params.fw.dir,
+ ar->hw_params.fw.otp);
+ if (IS_ERR(ar->otp)) {
+ ret = PTR_ERR(ar->otp);
+ ath10k_err("could not fetch otp (%d)\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ ath10k_core_free_firmware_files(ar);
return ret;
}
return 0;
}
+static void ath10k_core_restart(struct work_struct *work)
+{
+ struct ath10k *ar = container_of(work, struct ath10k, restart_work);
+
+ mutex_lock(&ar->conf_mutex);
+
+ switch (ar->state) {
+ case ATH10K_STATE_ON:
+ ath10k_halt(ar);
+ ar->state = ATH10K_STATE_RESTARTING;
+ ieee80211_restart_hw(ar->hw);
+ break;
+ case ATH10K_STATE_OFF:
+ /* this can happen if driver is being unloaded */
+ ath10k_warn("cannot restart a device that hasn't been started\n");
+ break;
+ case ATH10K_STATE_RESTARTING:
+ case ATH10K_STATE_RESTARTED:
+ ar->state = ATH10K_STATE_WEDGED;
+ /* fall through */
+ case ATH10K_STATE_WEDGED:
+ ath10k_warn("device is wedged, will not restart\n");
+ break;
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+}
+
struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
- enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops)
{
struct ath10k *ar;
ar->hif.priv = hif_priv;
ar->hif.ops = hif_ops;
- ar->hif.bus = bus;
-
- ar->free_vdev_map = 0xFF; /* 8 vdevs */
init_completion(&ar->scan.started);
init_completion(&ar->scan.completed);
init_waitqueue_head(&ar->event_queue);
+ INIT_WORK(&ar->restart_work, ath10k_core_restart);
+
return ar;
err_wq:
}
EXPORT_SYMBOL(ath10k_core_destroy);
-
-int ath10k_core_register(struct ath10k *ar)
+int ath10k_core_start(struct ath10k *ar)
{
- struct ath10k_htc_ops htc_ops;
- struct bmi_target_info target_info;
int status;
- memset(&target_info, 0, sizeof(target_info));
- status = ath10k_bmi_get_target_info(ar, &target_info);
- if (status)
- goto err;
-
- ar->target_version = target_info.version;
- ar->hw->wiphy->hw_version = target_info.version;
-
- status = ath10k_init_hw_params(ar);
- if (status)
- goto err;
+ ath10k_bmi_start(ar);
if (ath10k_init_configure_target(ar)) {
status = -EINVAL;
if (status)
goto err;
- htc_ops.target_send_suspend_complete = ath10k_send_suspend_complete;
+ ar->htc.htc_ops.target_send_suspend_complete =
+ ath10k_send_suspend_complete;
- ar->htc = ath10k_htc_create(ar, &htc_ops);
- if (IS_ERR(ar->htc)) {
- status = PTR_ERR(ar->htc);
- ath10k_err("could not create HTC (%d)\n", status);
+ status = ath10k_htc_init(ar);
+ if (status) {
+ ath10k_err("could not init HTC (%d)\n", status);
goto err;
}
status = ath10k_bmi_done(ar);
if (status)
- goto err_htc_destroy;
+ goto err;
status = ath10k_wmi_attach(ar);
if (status) {
ath10k_err("WMI attach failed: %d\n", status);
- goto err_htc_destroy;
+ goto err;
}
- status = ath10k_htc_wait_target(ar->htc);
+ status = ath10k_htc_wait_target(&ar->htc);
if (status)
goto err_wmi_detach;
- ar->htt = ath10k_htt_attach(ar);
- if (!ar->htt) {
- status = -ENOMEM;
+ status = ath10k_htt_attach(ar);
+ if (status) {
+ ath10k_err("could not attach htt (%d)\n", status);
goto err_wmi_detach;
}
goto err_disconnect_htc;
}
- status = ath10k_htt_attach_target(ar->htt);
- if (status)
- goto err_disconnect_htc;
-
- status = ath10k_mac_register(ar);
+ status = ath10k_htt_attach_target(&ar->htt);
if (status)
goto err_disconnect_htc;
- status = ath10k_debug_create(ar);
- if (status) {
- ath10k_err("unable to initialize debugfs\n");
- goto err_unregister_mac;
- }
+ ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1;
return 0;
-err_unregister_mac:
- ath10k_mac_unregister(ar);
err_disconnect_htc:
- ath10k_htc_stop(ar->htc);
+ ath10k_htc_stop(&ar->htc);
err_htt_detach:
- ath10k_htt_detach(ar->htt);
+ ath10k_htt_detach(&ar->htt);
err_wmi_detach:
ath10k_wmi_detach(ar);
-err_htc_destroy:
- ath10k_htc_destroy(ar->htc);
err:
return status;
}
-EXPORT_SYMBOL(ath10k_core_register);
+EXPORT_SYMBOL(ath10k_core_start);
-void ath10k_core_unregister(struct ath10k *ar)
+void ath10k_core_stop(struct ath10k *ar)
{
- /* We must unregister from mac80211 before we stop HTC and HIF.
- * Otherwise we will fail to submit commands to FW and mac80211 will be
- * unhappy about callback failures. */
- ath10k_mac_unregister(ar);
- ath10k_htc_stop(ar->htc);
- ath10k_htt_detach(ar->htt);
+ ath10k_htc_stop(&ar->htc);
+ ath10k_htt_detach(&ar->htt);
ath10k_wmi_detach(ar);
- ath10k_htc_destroy(ar->htc);
}
-EXPORT_SYMBOL(ath10k_core_unregister);
+EXPORT_SYMBOL(ath10k_core_stop);
-int ath10k_core_target_suspend(struct ath10k *ar)
+/* mac80211 manages fw/hw initialization through start/stop hooks. However in
+ * order to know what hw capabilities should be advertised to mac80211 it is
+ * necessary to load the firmware (and tear it down immediately since start
+ * hook will try to init it again) before registering */
+static int ath10k_core_probe_fw(struct ath10k *ar)
{
- int ret;
+ struct bmi_target_info target_info;
+ int ret = 0;
+
+ ret = ath10k_hif_power_up(ar);
+ if (ret) {
+ ath10k_err("could not start pci hif (%d)\n", ret);
+ return ret;
+ }
- ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+ memset(&target_info, 0, sizeof(target_info));
+ ret = ath10k_bmi_get_target_info(ar, &target_info);
+ if (ret) {
+ ath10k_err("could not get target info (%d)\n", ret);
+ ath10k_hif_power_down(ar);
+ return ret;
+ }
- ret = ath10k_wmi_pdev_suspend_target(ar);
- if (ret)
- ath10k_warn("could not suspend target (%d)\n", ret);
+ ar->target_version = target_info.version;
+ ar->hw->wiphy->hw_version = target_info.version;
- return ret;
+ ret = ath10k_init_hw_params(ar);
+ if (ret) {
+ ath10k_err("could not get hw params (%d)\n", ret);
+ ath10k_hif_power_down(ar);
+ return ret;
+ }
+
+ ret = ath10k_core_fetch_firmware_files(ar);
+ if (ret) {
+ ath10k_err("could not fetch firmware files (%d)\n", ret);
+ ath10k_hif_power_down(ar);
+ return ret;
+ }
+
+ 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);
+ return ret;
+ }
+
+ ath10k_core_stop(ar);
+ ath10k_hif_power_down(ar);
+ return 0;
}
-EXPORT_SYMBOL(ath10k_core_target_suspend);
-int ath10k_core_target_resume(struct ath10k *ar)
+int ath10k_core_register(struct ath10k *ar)
{
- int ret;
+ int status;
- ath10k_dbg(ATH10K_DBG_CORE, "%s: called", __func__);
+ status = ath10k_core_probe_fw(ar);
+ if (status) {
+ ath10k_err("could not probe fw (%d)\n", status);
+ return status;
+ }
- ret = ath10k_wmi_pdev_resume_target(ar);
- if (ret)
- ath10k_warn("could not resume target (%d)\n", ret);
+ status = ath10k_mac_register(ar);
+ if (status) {
+ ath10k_err("could not register to mac80211 (%d)\n", status);
+ goto err_release_fw;
+ }
- return ret;
+ status = ath10k_debug_create(ar);
+ if (status) {
+ ath10k_err("unable to initialize debugfs\n");
+ goto err_unregister_mac;
+ }
+
+ return 0;
+
+err_unregister_mac:
+ ath10k_mac_unregister(ar);
+err_release_fw:
+ ath10k_core_free_firmware_files(ar);
+ return status;
+}
+EXPORT_SYMBOL(ath10k_core_register);
+
+void ath10k_core_unregister(struct ath10k *ar)
+{
+ /* We must unregister from mac80211 before we stop HTC and HIF.
+ * Otherwise we will fail to submit commands to FW and mac80211 will be
+ * unhappy about callback failures. */
+ ath10k_mac_unregister(ar);
+ ath10k_core_free_firmware_files(ar);
}
-EXPORT_SYMBOL(ath10k_core_target_resume);
+EXPORT_SYMBOL(ath10k_core_unregister);
MODULE_AUTHOR("Qualcomm Atheros");
MODULE_DESCRIPTION("Core module for QCA988X PCIe devices.");
#include <linux/types.h>
#include <linux/pci.h>
+#include "htt.h"
#include "htc.h"
#include "hw.h"
#include "targaddrs.h"
#define ATH10K_SCAN_ID 0
#define WMI_READY_TIMEOUT (5 * HZ)
#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
+#define ATH10K_NUM_CHANS 38
/* Antenna noise floor */
#define ATH10K_DEFAULT_NOISE_FLOOR -95
struct ath10k;
-enum ath10k_bus {
- ATH10K_BUS_PCI,
-};
-
struct ath10k_skb_cb {
dma_addr_t paddr;
bool is_mapped;
struct completion event_stats_compl;
};
+enum ath10k_state {
+ ATH10K_STATE_OFF = 0,
+ ATH10K_STATE_ON,
+
+ /* When doing firmware recovery the device is first powered down.
+ * mac80211 is supposed to call in to start() hook later on. It is
+ * however possible that driver unloading and firmware crash overlap.
+ * mac80211 can wait on conf_mutex in stop() while the device is
+ * stopped in ath10k_core_restart() work holding conf_mutex. The state
+ * RESTARTED means that the device is up and mac80211 has started hw
+ * reconfiguration. Once mac80211 is done with the reconfiguration we
+ * set the state to STATE_ON in restart_complete(). */
+ ATH10K_STATE_RESTARTING,
+ ATH10K_STATE_RESTARTED,
+
+ /* The device has crashed while restarting hw. This state is like ON
+ * but commands are blocked in HTC and -ECOMM response is given. This
+ * prevents completion timeouts and makes the driver more responsive to
+ * userspace commands. This is also prevents recursive recovery. */
+ ATH10K_STATE_WEDGED,
+};
+
struct ath10k {
struct ath_common ath_common;
struct ieee80211_hw *hw;
u32 hw_max_tx_power;
u32 ht_cap_info;
u32 vht_cap_info;
+ u32 num_rf_chains;
struct targetdef *targetdef;
struct hostdef *hostdef;
struct {
void *priv;
- enum ath10k_bus bus;
const struct ath10k_hif_ops *ops;
} hif;
- struct ath10k_wmi wmi;
-
wait_queue_head_t event_queue;
bool is_target_paused;
struct ath10k_bmi bmi;
-
- struct ath10k_htc *htc;
- struct ath10k_htt *htt;
+ struct ath10k_wmi wmi;
+ struct ath10k_htc htc;
+ struct ath10k_htt htt;
struct ath10k_hw_params {
u32 id;
} fw;
} hw_params;
+ const struct firmware *board_data;
+ const struct firmware *otp;
+ const struct firmware *firmware;
+
struct {
struct completion started;
struct completion completed;
struct completion offchan_tx_completed;
struct sk_buff *offchan_tx_skb;
+ enum ath10k_state state;
+
+ struct work_struct restart_work;
+
+ /* cycle count is reported twice for each visited channel during scan.
+ * access protected by data_lock */
+ u32 survey_last_rx_clear_count;
+ u32 survey_last_cycle_count;
+ struct survey_info survey[ATH10K_NUM_CHANS];
+
#ifdef CONFIG_ATH10K_DEBUGFS
struct ath10k_debug debug;
#endif
};
struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev,
- enum ath10k_bus bus,
const struct ath10k_hif_ops *hif_ops);
void ath10k_core_destroy(struct ath10k *ar);
+int ath10k_core_start(struct ath10k *ar);
+void ath10k_core_stop(struct ath10k *ar);
int ath10k_core_register(struct ath10k *ar);
void ath10k_core_unregister(struct ath10k *ar);
-int ath10k_core_target_suspend(struct ath10k *ar);
-int ath10k_core_target_resume(struct ath10k *ar);
-
#endif /* _CORE_H_ */
struct wmi_pdev_stats *ps;
int i;
- mutex_lock(&ar->conf_mutex);
+ spin_lock_bh(&ar->data_lock);
stats = &ar->debug.target_stats;
}
}
+ spin_unlock_bh(&ar->data_lock);
mutex_unlock(&ar->conf_mutex);
complete(&ar->debug.event_stats_compl);
}
{
struct ath10k *ar = file->private_data;
struct ath10k_target_stats *fw_stats;
- char *buf;
+ char *buf = NULL;
unsigned int len = 0, buf_len = 2500;
- ssize_t ret_cnt;
+ ssize_t ret_cnt = 0;
long left;
int i;
int ret;
fw_stats = &ar->debug.target_stats;
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_ON)
+ goto exit;
+
buf = kzalloc(buf_len, GFP_KERNEL);
if (!buf)
- return -ENOMEM;
+ goto exit;
ret = ath10k_wmi_request_stats(ar, WMI_REQUEST_PEER_STAT);
if (ret) {
ath10k_warn("could not request stats (%d)\n", ret);
- kfree(buf);
- return -EIO;
+ goto exit;
}
left = wait_for_completion_timeout(&ar->debug.event_stats_compl, 1*HZ);
+ if (left <= 0)
+ goto exit;
- if (left <= 0) {
- kfree(buf);
- return -ETIMEDOUT;
- }
-
- mutex_lock(&ar->conf_mutex);
-
+ spin_lock_bh(&ar->data_lock);
len += scnprintf(buf + len, buf_len - len, "\n");
len += scnprintf(buf + len, buf_len - len, "%30s\n",
"ath10k PDEV stats");
fw_stats->peer_stat[i].peer_tx_rate);
len += scnprintf(buf + len, buf_len - len, "\n");
}
+ spin_unlock_bh(&ar->data_lock);
if (len > buf_len)
len = buf_len;
ret_cnt = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+exit:
mutex_unlock(&ar->conf_mutex);
-
kfree(buf);
return ret_cnt;
}
.llseek = default_llseek,
};
+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";
+ return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
+}
+
+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] = {};
+ 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;
+ }
+
+ if (ar->state != ATH10K_STATE_ON &&
+ ar->state != ATH10K_STATE_RESTARTED) {
+ ret = -ENETDOWN;
+ goto exit;
+ }
+
+ ath10k_info("simulating firmware crash\n");
+
+ 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 (ret == 0)
+ ret = count;
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
+static const struct file_operations fops_simulate_fw_crash = {
+ .read = ath10k_read_simulate_fw_crash,
+ .write = ath10k_write_simulate_fw_crash,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
int ath10k_debug_create(struct ath10k *ar)
{
ar->debug.debugfs_phy = debugfs_create_dir("ath10k",
debugfs_create_file("wmi_services", S_IRUSR, ar->debug.debugfs_phy, ar,
&fops_wmi_services);
+ debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy,
+ ar, &fops_simulate_fw_crash);
+
return 0;
}
#endif /* CONFIG_ATH10K_DEBUGFS */
void *request, u32 request_len,
void *response, u32 *response_len);
+ /* Post BMI phase, after FW is loaded. Starts regular operation */
int (*start)(struct ath10k *ar);
+ /* Clean up what start() did. This does not revert to BMI phase. If
+ * desired so, call power_down() and power_up() */
void (*stop)(struct ath10k *ar);
int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
*/
void (*send_complete_check)(struct ath10k *ar, u8 pipe_id, int force);
- void (*init)(struct ath10k *ar,
- struct ath10k_hif_cb *callbacks);
+ void (*set_callbacks)(struct ath10k *ar,
+ struct ath10k_hif_cb *callbacks);
u16 (*get_free_queue_number)(struct ath10k *ar, u8 pipe_id);
+
+ /* Power up the device and enter BMI transfer mode for FW download */
+ int (*power_up)(struct ath10k *ar);
+
+ /* Power down the device and free up resources. stop() must be called
+ * before this if start() was called earlier */
+ void (*power_down)(struct ath10k *ar);
+
+ int (*suspend)(struct ath10k *ar);
+ int (*resume)(struct ath10k *ar);
};
ar->hif.ops->send_complete_check(ar, pipe_id, force);
}
-static inline void ath10k_hif_init(struct ath10k *ar,
- struct ath10k_hif_cb *callbacks)
+static inline void ath10k_hif_set_callbacks(struct ath10k *ar,
+ struct ath10k_hif_cb *callbacks)
{
- ar->hif.ops->init(ar, callbacks);
+ ar->hif.ops->set_callbacks(ar, callbacks);
}
static inline u16 ath10k_hif_get_free_queue_number(struct ath10k *ar,
return ar->hif.ops->get_free_queue_number(ar, pipe_id);
}
+static inline int ath10k_hif_power_up(struct ath10k *ar)
+{
+ return ar->hif.ops->power_up(ar);
+}
+
+static inline void ath10k_hif_power_down(struct ath10k *ar)
+{
+ ar->hif.ops->power_down(ar);
+}
+
+static inline int ath10k_hif_suspend(struct ath10k *ar)
+{
+ if (!ar->hif.ops->suspend)
+ return -EOPNOTSUPP;
+
+ return ar->hif.ops->suspend(ar);
+}
+
+static inline int ath10k_hif_resume(struct ath10k *ar)
+{
+ if (!ar->hif.ops->resume)
+ return -EOPNOTSUPP;
+
+ return ar->hif.ops->resume(ar);
+}
+
#endif /* _HIF_H_ */
{
struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+ if (htc->ar->state == ATH10K_STATE_WEDGED)
+ return -ECOMM;
+
if (eid >= ATH10K_HTC_EP_COUNT) {
ath10k_warn("Invalid endpoint id: %d\n", eid);
return -ENOENT;
}
- skb_push(skb, sizeof(struct ath10k_htc_hdr));
-
spin_lock_bh(&htc->tx_lock);
+ if (htc->stopped) {
+ spin_unlock_bh(&htc->tx_lock);
+ return -ESHUTDOWN;
+ }
+
__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);
struct sk_buff *skb,
unsigned int eid)
{
- struct ath10k_htc *htc = ar->htc;
+ struct ath10k_htc *htc = &ar->htc;
struct ath10k_htc_ep *ep = &htc->endpoint[eid];
- bool stopping;
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);
- stopping = htc->stopping;
- spin_unlock_bh(&htc->tx_lock);
-
- if (!ep->tx_credit_flow_enabled && !stopping)
- /*
- * 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
- */
+ if (!ep->tx_credit_flow_enabled && !htc->stopped)
queue_work(ar->workqueue, &ep->send_work);
+ spin_unlock_bh(&htc->tx_lock);
return 0;
}
u8 pipe_id)
{
int status = 0;
- struct ath10k_htc *htc = ar->htc;
+ struct ath10k_htc *htc = &ar->htc;
struct ath10k_htc_hdr *hdr;
struct ath10k_htc_ep *ep;
u16 payload_len;
tx_alloc = ath10k_htc_get_credit_allocation(htc,
conn_req->service_id);
if (!tx_alloc)
- ath10k_warn("HTC Service %s does not allocate target credits\n",
- htc_service_name(conn_req->service_id));
+ ath10k_dbg(ATH10K_DBG_HTC,
+ "HTC Service %s does not allocate target credits\n",
+ htc_service_name(conn_req->service_id));
skb = ath10k_htc_build_tx_ctrl_skb(htc->ar);
if (!skb) {
struct ath10k_htc_ep *ep;
spin_lock_bh(&htc->tx_lock);
- htc->stopping = true;
+ htc->stopped = true;
spin_unlock_bh(&htc->tx_lock);
for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) {
}
ath10k_hif_stop(htc->ar);
- ath10k_htc_reset_endpoint_states(htc);
}
/* registered target arrival callback from the HIF layer */
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
- struct ath10k_htc_ops *htc_ops)
+int ath10k_htc_init(struct ath10k *ar)
{
struct ath10k_hif_cb htc_callbacks;
struct ath10k_htc_ep *ep = NULL;
- struct ath10k_htc *htc = NULL;
-
- /* FIXME: use struct ath10k instead */
- htc = kzalloc(sizeof(struct ath10k_htc), GFP_KERNEL);
- if (!htc)
- return ERR_PTR(-ENOMEM);
+ struct ath10k_htc *htc = &ar->htc;
spin_lock_init(&htc->tx_lock);
- memcpy(&htc->htc_ops, htc_ops, sizeof(struct ath10k_htc_ops));
-
+ htc->stopped = false;
ath10k_htc_reset_endpoint_states(htc);
/* setup HIF layer callbacks */
/* Get HIF default pipe for HTC message exchange */
ep = &htc->endpoint[ATH10K_HTC_EP_0];
- ath10k_hif_init(ar, &htc_callbacks);
+ ath10k_hif_set_callbacks(ar, &htc_callbacks);
ath10k_hif_get_default_pipe(ar, &ep->ul_pipe_id, &ep->dl_pipe_id);
init_completion(&htc->ctl_resp);
- return htc;
-}
-
-void ath10k_htc_destroy(struct ath10k_htc *htc)
-{
- kfree(htc);
+ return 0;
}
struct ath10k *ar;
struct ath10k_htc_ep endpoint[ATH10K_HTC_EP_COUNT];
- /* protects endpoint and stopping fields */
+ /* protects endpoint and stopped fields */
spinlock_t tx_lock;
struct ath10k_htc_ops htc_ops;
struct ath10k_htc_svc_tx_credits service_tx_alloc[ATH10K_HTC_EP_COUNT];
int target_credit_size;
- bool stopping;
+ bool stopped;
};
-struct ath10k_htc *ath10k_htc_create(struct ath10k *ar,
- struct ath10k_htc_ops *htc_ops);
+int ath10k_htc_init(struct ath10k *ar);
int ath10k_htc_wait_target(struct ath10k_htc *htc);
int ath10k_htc_start(struct ath10k_htc *htc);
int ath10k_htc_connect_service(struct ath10k_htc *htc,
int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
struct sk_buff *packet);
void ath10k_htc_stop(struct ath10k_htc *htc);
-void ath10k_htc_destroy(struct ath10k_htc *htc);
struct sk_buff *ath10k_htc_alloc_skb(int size);
#endif
*/
#include <linux/slab.h>
+#include <linux/if_ether.h>
#include "htt.h"
#include "core.h"
/* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
- status = ath10k_htc_connect_service(htt->ar->htc, &conn_req,
+ status = ath10k_htc_connect_service(&htt->ar->htc, &conn_req,
&conn_resp);
if (status)
return 0;
}
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar)
+int ath10k_htt_attach(struct ath10k *ar)
{
- struct ath10k_htt *htt;
+ struct ath10k_htt *htt = &ar->htt;
int ret;
- htt = kzalloc(sizeof(*htt), GFP_KERNEL);
- if (!htt)
- return NULL;
-
htt->ar = ar;
htt->max_throughput_mbps = 800;
* since ath10k_htt_rx_attach involves sending a rx ring configure
* message to the target.
*/
- if (ath10k_htt_htc_attach(htt))
+ 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) {
goto err_htc_attach;
}
- if (ath10k_htt_rx_attach(htt))
+ 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
8 + /* llc snap */
2; /* ip4 dscp or ip6 priority */
- return htt;
+ return 0;
err_rx_attach:
ath10k_htt_tx_detach(htt);
err_htc_attach:
- kfree(htt);
- return NULL;
+ return ret;
}
#define HTT_TARGET_VERSION_TIMEOUT_HZ (3*HZ)
{
ath10k_htt_rx_detach(htt);
ath10k_htt_tx_detach(htt);
- kfree(htt);
}
#include <linux/bug.h>
-#include "core.h"
#include "htc.h"
#include "rx_desc.h"
#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)
-struct ath10k_htt *ath10k_htt_attach(struct ath10k *ar);
+int ath10k_htt_attach(struct ath10k *ar);
int ath10k_htt_attach_target(struct ath10k_htt *htt);
void ath10k_htt_detach(struct ath10k_htt *htt);
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "core.h"
#include "htc.h"
#include "htt.h"
#include "txrx.h"
return false;
}
+static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
+{
+ struct htt_rx_desc *rxd;
+ u32 flags, info;
+ bool is_ip4, is_ip6;
+ bool is_tcp, is_udp;
+ bool ip_csum_ok, tcpudp_csum_ok;
+
+ rxd = (void *)skb->data - sizeof(*rxd);
+ flags = __le32_to_cpu(rxd->attention.flags);
+ info = __le32_to_cpu(rxd->msdu_start.info1);
+
+ is_ip4 = !!(info & RX_MSDU_START_INFO1_IPV4_PROTO);
+ is_ip6 = !!(info & RX_MSDU_START_INFO1_IPV6_PROTO);
+ is_tcp = !!(info & RX_MSDU_START_INFO1_TCP_PROTO);
+ is_udp = !!(info & RX_MSDU_START_INFO1_UDP_PROTO);
+ ip_csum_ok = !(flags & RX_ATTENTION_FLAGS_IP_CHKSUM_FAIL);
+ tcpudp_csum_ok = !(flags & RX_ATTENTION_FLAGS_TCP_UDP_CHKSUM_FAIL);
+
+ if (!is_ip4 && !is_ip6)
+ return CHECKSUM_NONE;
+ if (!is_tcp && !is_udp)
+ return CHECKSUM_NONE;
+ if (!ip_csum_ok)
+ return CHECKSUM_NONE;
+ if (!tcpudp_csum_ok)
+ return CHECKSUM_NONE;
+
+ return CHECKSUM_UNNECESSARY;
+}
+
static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
u8 *fw_desc;
int i, j;
int ret;
+ int ip_summed;
memset(&info, 0, sizeof(info));
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;
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);
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);
if (tkip_mic_err) {
ath10k_warn("tkip mic error\n");
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
- struct ath10k_htt *htt = ar->htt;
+ struct ath10k_htt *htt = &ar->htt;
struct htt_resp *resp = (struct htt_resp *)skb->data;
/* confirm alignment */
/* At the beginning free queue number should hint us the maximum
* queue length */
- pipe = htt->ar->htc->endpoint[htt->eid].ul_pipe_id;
+ pipe = htt->ar->htc.endpoint[htt->eid].ul_pipe_id;
htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar,
pipe);
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;
+ struct ath10k_htt *htt = &ar->htt;
if (skb_cb->htt.is_conf) {
dev_kfree_skb_any(skb);
ATH10K_SKB_CB(skb)->htt.is_conf = true;
- ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+ ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
if (ret) {
dev_kfree_skb_any(skb);
return ret;
ATH10K_SKB_CB(skb)->htt.is_conf = true;
- ret = ath10k_htc_send(htt->ar->htc, htt->eid, skb);
+ ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb);
if (ret) {
dev_kfree_skb_any(skb);
return ret;
skb_cb->htt.refcount = 2;
skb_cb->htt.msdu = msdu;
- res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+ res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
goto err;
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;
skb_cb->htt.txfrag = txfrag;
skb_cb->htt.msdu = msdu;
- res = ath10k_htc_send(htt->ar->htc, htt->eid, txdesc);
+ res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc);
if (res)
goto err;
#include <net/mac80211.h>
#include <linux/etherdevice.h>
+#include "hif.h"
#include "core.h"
#include "debug.h"
#include "wmi.h"
.macaddr = macaddr,
};
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+
if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE)
arg.key_flags = WMI_KEY_PAIRWISE;
else
struct ath10k *ar = arvif->ar;
int ret;
+ lockdep_assert_held(&ar->conf_mutex);
+
INIT_COMPLETION(ar->install_key_done);
ret = ath10k_send_key(arvif, key, cmd, macaddr);
return 0;
}
+static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value)
+{
+ 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);
+}
+
+static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value)
+{
+ 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);
+}
+
static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
int ret;
spin_unlock_bh(&ar->data_lock);
}
+static void ath10k_peer_cleanup_all(struct ath10k *ar)
+{
+ struct ath10k_peer *peer, *tmp;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ spin_lock_bh(&ar->data_lock);
+ list_for_each_entry_safe(peer, tmp, &ar->peers, list) {
+ list_del(&peer->list);
+ kfree(peer);
+ }
+ spin_unlock_bh(&ar->data_lock);
+}
+
/************************/
/* Interface management */
/************************/
{
int ret;
+ lockdep_assert_held(&ar->conf_mutex);
+
ret = wait_for_completion_timeout(&ar->vdev_setup_done,
ATH10K_VDEV_SETUP_TIMEOUT_HZ);
if (ret == 0)
{
int ret = 0;
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+
if (!info->enable_beacon) {
ath10k_vdev_stop(arvif);
return;
{
int ret = 0;
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+
if (!info->ibss_joined) {
ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, self_peer);
if (ret)
enum wmi_sta_ps_mode psmode;
int ret;
+ lockdep_assert_held(&arvif->ar->conf_mutex);
+
if (vif->type != NL80211_IFTYPE_STATION)
return;
struct ieee80211_bss_conf *bss_conf,
struct wmi_peer_assoc_complete_arg *arg)
{
+ lockdep_assert_held(&ar->conf_mutex);
+
memcpy(arg->addr, sta->addr, ETH_ALEN);
arg->vdev_id = arvif->vdev_id;
arg->peer_aid = sta->aid;
const u8 *rsnie = NULL;
const u8 *wpaie = NULL;
+ lockdep_assert_held(&ar->conf_mutex);
+
bss = cfg80211_get_bss(ar->hw->wiphy, ar->hw->conf.chandef.chan,
info->bssid, NULL, 0, 0, 0);
if (bss) {
u32 ratemask;
int i;
+ lockdep_assert_held(&ar->conf_mutex);
+
sband = ar->hw->wiphy->bands[ar->hw->conf.chandef.chan->band];
ratemask = sta->supp_rates[ar->hw->conf.chandef.chan->band];
rates = sband->bitrates;
int smps;
int i, n;
+ lockdep_assert_held(&ar->conf_mutex);
+
if (!ht_cap->ht_supported)
return;
u32 uapsd = 0;
u32 max_sp = 0;
+ lockdep_assert_held(&ar->conf_mutex);
+
if (sta->wme)
arg->peer_flags |= WMI_PEER_QOS;
{
struct wmi_peer_assoc_complete_arg arg;
+ lockdep_assert_held(&ar->conf_mutex);
+
memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg));
ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg);
struct ieee80211_sta *ap_sta;
int ret;
+ lockdep_assert_held(&ar->conf_mutex);
+
rcu_read_lock();
ap_sta = ieee80211_find_sta(vif, bss_conf->bssid);
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
int ret;
+ lockdep_assert_held(&ar->conf_mutex);
+
/*
* For some reason, calling VDEV-DOWN before VDEV-STOP
* makes the FW to send frames via HTT after disassociation.
{
int ret = 0;
+ lockdep_assert_held(&ar->conf_mutex);
+
ret = ath10k_peer_assoc(ar, arvif, sta, NULL);
if (ret) {
ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr);
{
int ret = 0;
+ lockdep_assert_held(&ar->conf_mutex);
+
ret = ath10k_clear_peer_keys(arvif, sta->addr);
if (ret) {
ath10k_warn("could not clear all peer wep keys (%d)\n", ret);
int ret;
int i;
+ lockdep_assert_held(&ar->conf_mutex);
+
bands = hw->wiphy->bands;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!bands[band])
return ret;
}
-static void ath10k_reg_notifier(struct wiphy *wiphy,
- struct regulatory_request *request)
+static void ath10k_regd_update(struct ath10k *ar)
{
- struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
struct reg_dmn_pair_mapping *regpair;
- struct ath10k *ar = hw->priv;
int ret;
- ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+ lockdep_assert_held(&ar->conf_mutex);
ret = ath10k_update_channel_list(ar);
if (ret)
ath10k_warn("could not update channel list (%d)\n", ret);
regpair = ar->ath_common.regulatory.regpair;
+
/* Target allows setting up per-band regdomain but ath_common provides
* a combined one only */
ret = ath10k_wmi_pdev_set_regdomain(ar,
ath10k_warn("could not set pdev regdomain (%d)\n", ret);
}
+static void ath10k_reg_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+ struct ath10k *ar = hw->priv;
+
+ ath_reg_notifier_apply(wiphy, request, &ar->ath_common.regulatory);
+
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state == ATH10K_STATE_ON)
+ ath10k_regd_update(ar);
+ mutex_unlock(&ar->conf_mutex);
+}
+
/***************/
/* TX handlers */
/***************/
return;
qos_ctl = ieee80211_get_qos_ctl(hdr);
- memmove(qos_ctl, qos_ctl + IEEE80211_QOS_CTL_LEN,
- skb->len - ieee80211_hdrlen(hdr->frame_control));
- skb_trim(skb, skb->len - IEEE80211_QOS_CTL_LEN);
+ memmove(skb->data + IEEE80211_QOS_CTL_LEN,
+ skb->data, (void *)qos_ctl - (void *)skb->data);
+ skb_pull(skb, IEEE80211_QOS_CTL_LEN);
}
static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
int ret;
if (ieee80211_is_mgmt(hdr->frame_control))
- ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+ ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
else if (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. */
- ret = ath10k_htt_mgmt_tx(ar->htt, skb);
+ ret = ath10k_htt_mgmt_tx(&ar->htt, skb);
else
- ret = ath10k_htt_tx(ar->htt, skb);
+ ret = ath10k_htt_tx(&ar->htt, skb);
if (ret) {
ath10k_warn("tx failed (%d). dropping packet.\n", ret);
ret = ath10k_wmi_stop_scan(ar, &arg);
if (ret) {
ath10k_warn("could not submit wmi stop scan (%d)\n", ret);
+ spin_lock_bh(&ar->data_lock);
+ ar->scan.in_progress = false;
+ ath10k_offchan_tx_purge(ar);
+ spin_unlock_bh(&ar->data_lock);
return -EIO;
}
tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
}
- ath10k_tx_h_qos_workaround(hw, control, skb);
- ath10k_tx_h_update_wep_key(skb);
- ath10k_tx_h_add_p2p_noa_ie(ar, skb);
- ath10k_tx_h_seq_no(skb);
+ /* it makes no sense to process injected frames like that */
+ if (info->control.vif &&
+ info->control.vif->type != NL80211_IFTYPE_MONITOR) {
+ ath10k_tx_h_qos_workaround(hw, control, skb);
+ ath10k_tx_h_update_wep_key(skb);
+ ath10k_tx_h_add_p2p_noa_ie(ar, skb);
+ 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;
/*
* Initialize various parameters with default vaules.
*/
+void ath10k_halt(struct ath10k *ar)
+{
+ lockdep_assert_held(&ar->conf_mutex);
+
+ del_timer_sync(&ar->scan.timeout);
+ ath10k_offchan_tx_purge(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);
+ }
+ spin_unlock_bh(&ar->data_lock);
+}
+
static int ath10k_start(struct ieee80211_hw *hw)
{
struct ath10k *ar = hw->priv;
- int ret;
+ int ret = 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state != ATH10K_STATE_OFF &&
+ ar->state != ATH10K_STATE_RESTARTING) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = ath10k_hif_power_up(ar);
+ if (ret) {
+ ath10k_err("could not init hif (%d)\n", ret);
+ ar->state = ATH10K_STATE_OFF;
+ goto exit;
+ }
+
+ 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;
+ }
+
+ 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, WMI_PDEV_PARAM_PMF_QOS, 1);
if (ret)
ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n",
ret);
+ ath10k_regd_update(ar);
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
return 0;
}
{
struct ath10k *ar = hw->priv;
- /* avoid leaks in case FW never confirms scan for offchannel */
+ mutex_lock(&ar->conf_mutex);
+ if (ar->state == ATH10K_STATE_ON ||
+ ar->state == ATH10K_STATE_RESTARTED ||
+ ar->state == ATH10K_STATE_WEDGED)
+ ath10k_halt(ar);
+
+ ar->state = ATH10K_STATE_OFF;
+ mutex_unlock(&ar->conf_mutex);
+
cancel_work_sync(&ar->offchan_tx_work);
- ath10k_offchan_tx_purge(ar);
+ cancel_work_sync(&ar->restart_work);
}
-static int ath10k_config(struct ieee80211_hw *hw, u32 changed)
+static void ath10k_config_ps(struct ath10k *ar)
{
struct ath10k_generic_iter ar_iter;
+
+ 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;
+
+ memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
+ ar_iter.ar = ar;
+
+ ieee80211_iterate_active_interfaces_atomic(
+ ar->hw, IEEE80211_IFACE_ITER_NORMAL,
+ ath10k_ps_iter, &ar_iter);
+
+ if (ar_iter.ret)
+ ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret);
+}
+
+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 flags;
mutex_lock(&ar->conf_mutex);
spin_unlock_bh(&ar->data_lock);
}
- if (changed & IEEE80211_CONF_CHANGE_PS) {
- memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter));
- ar_iter.ar = ar;
- flags = IEEE80211_IFACE_ITER_RESUME_ALL;
-
- ieee80211_iterate_active_interfaces_atomic(hw,
- flags,
- ath10k_ps_iter,
- &ar_iter);
-
- ret = ar_iter.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_destroy(ar);
}
+ ath10k_wmi_flush_tx(ar);
mutex_unlock(&ar->conf_mutex);
return ret;
}
mutex_lock(&ar->conf_mutex);
+ memset(arvif, 0, sizeof(*arvif));
+
arvif->ar = ar;
arvif->vif = vif;
ath10k_warn("Failed to set PSPOLL count: %d\n", ret);
}
+ 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",
+ arvif->vdev_id, ret);
+
+ 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",
+ arvif->vdev_id, ret);
+
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)
ar->monitor_present = true;
arg.ssids[i].len = req->ssids[i].ssid_len;
arg.ssids[i].ssid = req->ssids[i].ssid;
}
+ } else {
+ arg.scan_ctrl_flags |= WMI_SCAN_FLAG_PASSIVE;
}
if (req->n_channels) {
u32 value = 0;
int ret = 0;
+ lockdep_assert_held(&ar->conf_mutex);
+
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
return 0;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
u32 rts = ar_iter->ar->hw->wiphy->rts_threshold;
- rts = min_t(u32, rts, ATH10K_RTS_MAX);
+ lockdep_assert_held(&arvif->ar->conf_mutex);
- ar_iter->ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
- WMI_VDEV_PARAM_RTS_THRESHOLD,
- rts);
+ /* 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);
ar_iter.ar = ar;
mutex_lock(&ar->conf_mutex);
- ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- ath10k_set_rts_iter, &ar_iter);
+ 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;
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;
- int ret;
- frag = clamp_t(u32, frag,
- ATH10K_FRAGMT_THRESHOLD_MIN,
- ATH10K_FRAGMT_THRESHOLD_MAX);
+ lockdep_assert_held(&arvif->ar->conf_mutex);
- ret = ath10k_wmi_vdev_set_param(ar_iter->ar, arvif->vdev_id,
- WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD,
- frag);
+ /* 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 = ret;
+ 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);
ar_iter.ar = ar;
mutex_lock(&ar->conf_mutex);
- ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- ath10k_set_frag_iter, &ar_iter);
+ ieee80211_iterate_active_interfaces_atomic(
+ hw, IEEE80211_IFACE_ITER_NORMAL,
+ ath10k_set_frag_iter, &ar_iter);
mutex_unlock(&ar->conf_mutex);
return ar_iter.ret;
static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
{
struct ath10k *ar = hw->priv;
+ bool skip;
int ret;
/* mac80211 doesn't care if we really xmit queued frames or not
if (drop)
return;
- ret = wait_event_timeout(ar->htt->empty_tx_wq, ({
+ mutex_lock(&ar->conf_mutex);
+
+ if (ar->state == ATH10K_STATE_WEDGED)
+ goto skip;
+
+ ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
bool empty;
- spin_lock_bh(&ar->htt->tx_lock);
- empty = bitmap_empty(ar->htt->used_msdu_ids,
- ar->htt->max_num_pending_tx);
- spin_unlock_bh(&ar->htt->tx_lock);
- (empty);
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ empty = bitmap_empty(ar->htt.used_msdu_ids,
+ ar->htt.max_num_pending_tx);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ skip = (ar->state == ATH10K_STATE_WEDGED);
+
+ (empty || skip);
}), ATH10K_FLUSH_TIMEOUT_HZ);
- if (ret <= 0)
+
+ if (ret <= 0 || skip)
ath10k_warn("tx not flushed\n");
+
+skip:
+ mutex_unlock(&ar->conf_mutex);
}
/* TODO: Implement this function properly
return 1;
}
+#ifdef CONFIG_PM
+static int ath10k_suspend(struct ieee80211_hw *hw,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct ath10k *ar = hw->priv;
+ int ret;
+
+ ar->is_target_paused = false;
+
+ ret = ath10k_wmi_pdev_suspend_target(ar);
+ 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;
+ }
+
+ ret = ath10k_hif_suspend(ar);
+ if (ret) {
+ ath10k_warn("could not suspend hif (%d)\n", ret);
+ goto resume;
+ }
+
+ return 0;
+resume:
+ ret = ath10k_wmi_pdev_resume_target(ar);
+ if (ret)
+ ath10k_warn("could not resume target (%d)\n", ret);
+ return 1;
+}
+
+static int ath10k_resume(struct ieee80211_hw *hw)
+{
+ struct ath10k *ar = hw->priv;
+ int ret;
+
+ ret = ath10k_hif_resume(ar);
+ if (ret) {
+ ath10k_warn("could not resume hif (%d)\n", ret);
+ return 1;
+ }
+
+ ret = ath10k_wmi_pdev_resume_target(ar);
+ if (ret) {
+ ath10k_warn("could not resume target (%d)\n", ret);
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+static void ath10k_restart_complete(struct ieee80211_hw *hw)
+{
+ struct ath10k *ar = hw->priv;
+
+ mutex_lock(&ar->conf_mutex);
+
+ /* If device failed to restart it will be in a different state, e.g.
+ * ATH10K_STATE_WEDGED */
+ if (ar->state == ATH10K_STATE_RESTARTED) {
+ ath10k_info("device successfully recovered\n");
+ ar->state = ATH10K_STATE_ON;
+ }
+
+ mutex_unlock(&ar->conf_mutex);
+}
+
+static int ath10k_get_survey(struct ieee80211_hw *hw, int idx,
+ struct survey_info *survey)
+{
+ struct ath10k *ar = hw->priv;
+ struct ieee80211_supported_band *sband;
+ struct survey_info *ar_survey = &ar->survey[idx];
+ int ret = 0;
+
+ mutex_lock(&ar->conf_mutex);
+
+ 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) {
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ memcpy(survey, ar_survey, sizeof(*survey));
+ spin_unlock_bh(&ar->data_lock);
+
+ survey->channel = &sband->channels[idx];
+
+exit:
+ mutex_unlock(&ar->conf_mutex);
+ return ret;
+}
+
static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_tx,
.start = ath10k_start,
.set_frag_threshold = ath10k_set_frag_threshold,
.flush = ath10k_flush,
.tx_last_beacon = ath10k_tx_last_beacon,
+ .restart_complete = ath10k_restart_complete,
+ .get_survey = ath10k_get_survey,
+#ifdef CONFIG_PM
+ .suspend = ath10k_suspend,
+ .resume = ath10k_resume,
+#endif
};
#define RATETAB_ENT(_rate, _rateid, _flags) { \
.max = 8,
.types = BIT(NL80211_IFTYPE_STATION)
| BIT(NL80211_IFTYPE_P2P_CLIENT)
- | BIT(NL80211_IFTYPE_P2P_GO)
- | BIT(NL80211_IFTYPE_AP)
- }
+ },
+ {
+ .max = 3,
+ .types = BIT(NL80211_IFTYPE_P2P_GO)
+ },
+ {
+ .max = 7,
+ .types = BIT(NL80211_IFTYPE_AP)
+ },
};
static const struct ieee80211_iface_combination ath10k_if_comb = {
{
struct ieee80211_sta_vht_cap vht_cap = {0};
u16 mcs_map;
+ int i;
vht_cap.vht_supported = 1;
vht_cap.cap = ar->vht_cap_info;
- /* FIXME: check dynamically how many streams board supports */
- mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 2 |
- IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
- IEEE80211_VHT_MCS_NOT_SUPPORTED << 14;
+ mcs_map = 0;
+ for (i = 0; i < 8; i++) {
+ if (i < ar->num_rf_chains)
+ mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i*2);
+ else
+ mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i*2);
+ }
vht_cap.vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
vht_cap.vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
if (ar->vht_cap_info & WMI_VHT_CAP_MAX_MPDU_LEN_MASK)
ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU;
- for (i = 0; i < WMI_MAX_SPATIAL_STREAM; i++)
+ for (i = 0; i < ar->num_rf_chains; i++)
ht_cap.mcs.rx_mask[i] = 0xFF;
ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
channels = kmemdup(ath10k_2ghz_channels,
sizeof(ath10k_2ghz_channels),
GFP_KERNEL);
- if (!channels)
- return -ENOMEM;
+ if (!channels) {
+ ret = -ENOMEM;
+ goto err_free;
+ }
band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
band->n_channels = ARRAY_SIZE(ath10k_2ghz_channels);
sizeof(ath10k_5ghz_channels),
GFP_KERNEL);
if (!channels) {
- if (ar->phy_capability & WHAL_WLAN_11G_CAPABILITY) {
- band = &ar->mac.sbands[IEEE80211_BAND_2GHZ];
- kfree(band->channels);
- }
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_free;
}
band = &ar->mac.sbands[IEEE80211_BAND_5GHZ];
ar->hw->wiphy->iface_combinations = &ath10k_if_comb;
ar->hw->wiphy->n_iface_combinations = 1;
+ ar->hw->netdev_features = NETIF_F_HW_CSUM;
+
ret = ath_regd_init(&ar->ath_common.regulatory, ar->hw->wiphy,
ath10k_reg_notifier);
if (ret) {
ath10k_err("Regulatory initialization failed\n");
- return ret;
+ goto err_free;
}
ret = ieee80211_register_hw(ar->hw);
if (ret) {
ath10k_err("ieee80211 registration failed: %d\n", ret);
- return ret;
+ goto err_free;
}
if (!ath_is_world_regd(&ar->ath_common.regulatory)) {
ret = regulatory_hint(ar->hw->wiphy,
ar->ath_common.regulatory.alpha2);
if (ret)
- goto exit;
+ goto err_unregister;
}
return 0;
-exit:
+
+err_unregister:
ieee80211_unregister_hw(ar->hw);
+err_free:
+ kfree(ar->mac.sbands[IEEE80211_BAND_2GHZ].channels);
+ kfree(ar->mac.sbands[IEEE80211_BAND_5GHZ].channels);
+
return ret;
}
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_halt(struct ath10k *ar);
static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
#include "ce.h"
#include "pci.h"
-unsigned int ath10k_target_ps;
+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");
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 const struct ce_attr host_ce_config_wlan[] = {
/* host->target HTC control and raw streams */
reg_dump_values[i + 1],
reg_dump_values[i + 2],
reg_dump_values[i + 3]);
+
+ ieee80211_queue_work(ar->hw, &ar->restart_work);
}
static void ath10k_pci_hif_send_complete_check(struct ath10k *ar, u8 pipe,
ath10k_ce_per_engine_service(ar, pipe);
}
-static void ath10k_pci_hif_post_init(struct ath10k *ar,
- struct ath10k_hif_cb *callbacks)
+static void ath10k_pci_hif_set_callbacks(struct ath10k *ar,
+ struct ath10k_hif_cb *callbacks)
{
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
}
}
+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);
+}
+
static void ath10k_pci_hif_stop(struct ath10k *ar)
{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
+ /* Irqs are never explicitly re-enabled. They are implicitly re-enabled
+ * by ath10k_pci_start_intr(). */
+ ath10k_pci_disable_irqs(ar);
+
ath10k_pci_stop_ce(ar);
/* At this point, asynchronous threads are stopped, the target should
ath10k_pci_process_ce(ar);
ath10k_pci_cleanup_ce(ar);
ath10k_pci_buffer_cleanup(ar);
- ath10k_pci_ce_deinit(ar);
+
+ ar_pci->started = 0;
}
static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar,
ath10k_pci_sleep(ar);
}
+static int ath10k_pci_hif_power_up(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ int ret;
+
+ ret = ath10k_pci_start_intr(ar);
+ if (ret) {
+ ath10k_err("could not start interrupt handling (%d)\n", ret);
+ goto err;
+ }
+
+ /*
+ * Bring the target up cleanly.
+ *
+ * The target may be in an undefined state with an AUX-powered Target
+ * and a Host in WoW mode. If the Host crashes, loses power, or is
+ * restarted (without unloading the driver) then the Target is left
+ * (aux) powered and running. On a subsequent driver load, the Target
+ * 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);
+
+ ret = ath10k_pci_reset_target(ar);
+ if (ret)
+ goto err_irq;
+
+ 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)
+ goto err_ps;
+
+ ret = ath10k_pci_init_config(ar);
+ if (ret)
+ goto err_ce;
+
+ ret = ath10k_pci_wake_target_cpu(ar);
+ if (ret) {
+ ath10k_err("could not wake up target CPU (%d)\n", ret);
+ goto err_ce;
+ }
+
+ return 0;
+
+err_ce:
+ ath10k_pci_ce_deinit(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 void ath10k_pci_hif_power_down(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ ath10k_pci_stop_intr(ar);
+
+ ath10k_pci_ce_deinit(ar);
+ if (!test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
+ ath10k_do_pci_sleep(ar);
+}
+
+#ifdef CONFIG_PM
+
+#define ATH10K_PCI_PM_CONTROL 0x44
+
+static int ath10k_pci_hif_suspend(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct pci_dev *pdev = ar_pci->pdev;
+ u32 val;
+
+ pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+ if ((val & 0x000000ff) != 0x3) {
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+ (val & 0xffffff00) | 0x03);
+ }
+
+ return 0;
+}
+
+static int ath10k_pci_hif_resume(struct ath10k *ar)
+{
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+ struct pci_dev *pdev = ar_pci->pdev;
+ u32 val;
+
+ pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
+
+ if ((val & 0x000000ff) != 0) {
+ pci_restore_state(pdev);
+ pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
+ val & 0xffffff00);
+ /*
+ * Suspend/Resume resets the PCI configuration space,
+ * so we have to re-disable the RETRY_TIMEOUT register (0x41)
+ * to keep PCI Tx retries from interfering with C3 CPU state
+ */
+ pci_read_config_dword(pdev, 0x40, &val);
+
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+ }
+
+ return 0;
+}
+#endif
+
static const struct ath10k_hif_ops ath10k_pci_hif_ops = {
.send_head = ath10k_pci_hif_send_head,
.exchange_bmi_msg = ath10k_pci_hif_exchange_bmi_msg,
.map_service_to_pipe = ath10k_pci_hif_map_service_to_pipe,
.get_default_pipe = ath10k_pci_hif_get_default_pipe,
.send_complete_check = ath10k_pci_hif_send_complete_check,
- .init = ath10k_pci_hif_post_init,
+ .set_callbacks = ath10k_pci_hif_set_callbacks,
.get_free_queue_number = ath10k_pci_hif_get_free_queue_number,
+ .power_up = ath10k_pci_hif_power_up,
+ .power_down = ath10k_pci_hif_power_down,
+#ifdef CONFIG_PM
+ .suspend = ath10k_pci_hif_suspend,
+ .resume = ath10k_pci_hif_resume,
+#endif
};
static void ath10k_pci_ce_tasklet(unsigned long ptr)
ret = request_irq(ar_pci->pdev->irq + MSI_ASSIGN_FW,
ath10k_pci_msi_fw_handler,
IRQF_SHARED, "ath10k_pci", ar);
- if (ret)
+ if (ret) {
+ ath10k_warn("request_irq(%d) failed %d\n",
+ ar_pci->pdev->irq + MSI_ASSIGN_FW, ret);
+
+ pci_disable_msi(ar_pci->pdev);
return ret;
+ }
for (i = MSI_ASSIGN_CE_INITIAL; i <= MSI_ASSIGN_CE_MAX; i++) {
ret = request_irq(ar_pci->pdev->irq + i,
return 0;
}
-static void ath10k_pci_device_reset(struct ath10k_pci *ar_pci)
+static void ath10k_pci_device_reset(struct ath10k *ar)
{
- struct ath10k *ar = ar_pci->ar;
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
void __iomem *mem = ar_pci->mem;
int i;
u32 val;
case ATH10K_PCI_FEATURE_MSI_X:
ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n");
break;
- case ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND:
+ case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND:
ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n");
break;
+ case ATH10K_PCI_FEATURE_SOC_POWER_SAVE:
+ ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n");
+ break;
}
}
}
switch (pci_dev->device) {
case QCA988X_1_0_DEVICE_ID:
- set_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features);
+ 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);
goto err_ar_pci;
}
+ if (ath10k_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_BUS_PCI,
- &ath10k_pci_hif_ops);
+ ar = ath10k_core_create(ar_pci, ar_pci->dev, &ath10k_pci_hif_ops);
if (!ar) {
ath10k_err("ath10k_core_create failed!\n");
ret = -EINVAL;
}
/* Enable QCA988X_1.0 HW workarounds */
- if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features))
+ 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->cacheline_sz = dma_get_cache_alignment();
- ret = ath10k_pci_start_intr(ar);
- if (ret) {
- ath10k_err("could not start interrupt handling (%d)\n", ret);
- goto err_iomap;
- }
-
- /*
- * Bring the target up cleanly.
- *
- * The target may be in an undefined state with an AUX-powered Target
- * and a Host in WoW mode. If the Host crashes, loses power, or is
- * restarted (without unloading the driver) then the Target is left
- * (aux) powered and running. On a subsequent driver load, the Target
- * 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_pci);
-
- ret = ath10k_pci_reset_target(ar);
- if (ret)
- goto err_intr;
-
- if (ath10k_target_ps) {
- ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save enabled\n");
- } else {
- /* Force AWAKE forever */
- ath10k_dbg(ATH10K_DBG_PCI, "on-chip power save disabled\n");
- ath10k_do_pci_wake(ar);
- }
-
- ret = ath10k_pci_ce_init(ar);
- if (ret)
- goto err_intr;
-
- ret = ath10k_pci_init_config(ar);
- if (ret)
- goto err_ce;
-
- ret = ath10k_pci_wake_target_cpu(ar);
- if (ret) {
- ath10k_err("could not wake up target CPU (%d)\n", ret);
- goto err_ce;
- }
-
ret = ath10k_core_register(ar);
if (ret) {
ath10k_err("could not register driver core (%d)\n", ret);
- goto err_ce;
+ goto err_iomap;
}
return 0;
-err_ce:
- ath10k_pci_ce_deinit(ar);
-err_intr:
- ath10k_pci_stop_intr(ar);
err_iomap:
pci_iounmap(pdev, mem);
err_master:
tasklet_kill(&ar_pci->msi_fw_err);
ath10k_core_unregister(ar);
- ath10k_pci_stop_intr(ar);
pci_set_drvdata(pdev, NULL);
pci_iounmap(pdev, ar_pci->mem);
kfree(ar_pci);
}
-#if defined(CONFIG_PM_SLEEP)
-
-#define ATH10K_PCI_PM_CONTROL 0x44
-
-static int ath10k_pci_suspend(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
- struct ath10k *ar = pci_get_drvdata(pdev);
- struct ath10k_pci *ar_pci;
- u32 val;
- int ret, retval;
-
- ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
- if (!ar)
- return -ENODEV;
-
- ar_pci = ath10k_pci_priv(ar);
- if (!ar_pci)
- return -ENODEV;
-
- if (ath10k_core_target_suspend(ar))
- return -EBUSY;
-
- 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);
- retval = ret;
- goto resume;
- } else if (ret == 0) {
- ath10k_warn("suspend timed out - target pause event never came\n");
- retval = EIO;
- goto resume;
- }
-
- /*
- * reset is_target_paused and host can check that in next time,
- * or it will always be TRUE and host just skip the waiting
- * condition, it causes target assert due to host already
- * suspend
- */
- ar->is_target_paused = false;
-
- pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
- if ((val & 0x000000ff) != 0x3) {
- pci_save_state(pdev);
- pci_disable_device(pdev);
- pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
- (val & 0xffffff00) | 0x03);
- }
-
- return 0;
-resume:
- ret = ath10k_core_target_resume(ar);
- if (ret)
- ath10k_warn("could not resume (%d)\n", ret);
-
- return retval;
-}
-
-static int ath10k_pci_resume(struct device *device)
-{
- struct pci_dev *pdev = to_pci_dev(device);
- struct ath10k *ar = pci_get_drvdata(pdev);
- struct ath10k_pci *ar_pci;
- int ret;
- u32 val;
-
- ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__);
-
- if (!ar)
- return -ENODEV;
- ar_pci = ath10k_pci_priv(ar);
-
- if (!ar_pci)
- return -ENODEV;
-
- ret = pci_enable_device(pdev);
- if (ret) {
- ath10k_warn("cannot enable PCI device: %d\n", ret);
- return ret;
- }
-
- pci_read_config_dword(pdev, ATH10K_PCI_PM_CONTROL, &val);
-
- if ((val & 0x000000ff) != 0) {
- pci_restore_state(pdev);
- pci_write_config_dword(pdev, ATH10K_PCI_PM_CONTROL,
- val & 0xffffff00);
- /*
- * Suspend/Resume resets the PCI configuration space,
- * so we have to re-disable the RETRY_TIMEOUT register (0x41)
- * to keep PCI Tx retries from interfering with C3 CPU state
- */
- pci_read_config_dword(pdev, 0x40, &val);
-
- if ((val & 0x0000ff00) != 0)
- pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
- }
-
- ret = ath10k_core_target_resume(ar);
- if (ret)
- ath10k_warn("target resume failed: %d\n", ret);
-
- return ret;
-}
-
-static SIMPLE_DEV_PM_OPS(ath10k_dev_pm_ops,
- ath10k_pci_suspend,
- ath10k_pci_resume);
-
-#define ATH10K_PCI_PM_OPS (&ath10k_dev_pm_ops)
-
-#else
-
-#define ATH10K_PCI_PM_OPS NULL
-
-#endif /* CONFIG_PM_SLEEP */
-
MODULE_DEVICE_TABLE(pci, ath10k_pci_id_table);
static struct pci_driver ath10k_pci_driver = {
.id_table = ath10k_pci_id_table,
.probe = ath10k_pci_probe,
.remove = ath10k_pci_remove,
- .driver.pm = ATH10K_PCI_PM_OPS,
};
static int __init ath10k_pci_init(void)
enum ath10k_pci_features {
ATH10K_PCI_FEATURE_MSI_X = 0,
- ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND = 1,
+ ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1,
+ ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 2,
/* keep last */
ATH10K_PCI_FEATURE_COUNT
struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
void __iomem *addr = ar_pci->mem;
- if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WARKAROUND, ar_pci->features)) {
+ 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);
return ioread32(ar_pci->mem + offset);
}
-extern unsigned int ath10k_target_ps;
-
void ath10k_do_pci_wake(struct ath10k *ar);
void ath10k_do_pci_sleep(struct ath10k *ar);
static inline void ath10k_pci_wake(struct ath10k *ar)
{
- if (ath10k_target_ps)
+ 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);
}
static inline void ath10k_pci_sleep(struct ath10k *ar)
{
- if (ath10k_target_ps)
+ struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+
+ if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features))
ath10k_do_pci_sleep(ar);
}
{
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);
trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len);
- status = ath10k_htc_send(ar->htc, ar->wmi.eid, skb);
+ status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb);
if (status) {
dev_kfree_skb_any(skb);
atomic_dec(&ar->wmi.pending_tx_count);
return 0;
}
+static int freq_to_idx(struct ath10k *ar, int freq)
+{
+ struct ieee80211_supported_band *sband;
+ int band, ch, idx = 0;
+
+ for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
+ sband = ar->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 ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb)
{
- ath10k_dbg(ATH10K_DBG_WMI, "WMI_CHAN_INFO_EVENTID\n");
+ struct wmi_chan_info_event *ev;
+ struct survey_info *survey;
+ u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count;
+ int idx;
+
+ ev = (struct wmi_chan_info_event *)skb->data;
+
+ err_code = __le32_to_cpu(ev->err_code);
+ freq = __le32_to_cpu(ev->freq);
+ cmd_flags = __le32_to_cpu(ev->cmd_flags);
+ noise_floor = __le32_to_cpu(ev->noise_floor);
+ rx_clear_count = __le32_to_cpu(ev->rx_clear_count);
+ cycle_count = __le32_to_cpu(ev->cycle_count);
+
+ ath10k_dbg(ATH10K_DBG_WMI,
+ "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n",
+ err_code, freq, cmd_flags, noise_floor, rx_clear_count,
+ cycle_count);
+
+ spin_lock_bh(&ar->data_lock);
+
+ if (!ar->scan.in_progress) {
+ ath10k_warn("chan info event without a scan request?\n");
+ goto exit;
+ }
+
+ idx = freq_to_idx(ar, freq);
+ if (idx >= ARRAY_SIZE(ar->survey)) {
+ ath10k_warn("chan info: invalid frequency %d (idx %d out of bounds)\n",
+ freq, idx);
+ goto exit;
+ }
+
+ if (cmd_flags & WMI_CHAN_INFO_FLAG_COMPLETE) {
+ /* During scanning chan info is reported twice for each
+ * visited channel. The reported cycle count is global
+ * and per-channel cycle count must be calculated */
+
+ cycle_count -= ar->survey_last_cycle_count;
+ rx_clear_count -= ar->survey_last_rx_clear_count;
+
+ survey = &ar->survey[idx];
+ survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count);
+ survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count);
+ survey->noise = noise_floor;
+ survey->filled = SURVEY_INFO_CHANNEL_TIME |
+ SURVEY_INFO_CHANNEL_TIME_RX |
+ SURVEY_INFO_NOISE_DBM;
+ }
+
+ ar->survey_last_rx_clear_count = rx_clear_count;
+ ar->survey_last_cycle_count = cycle_count;
+
+exit:
+ spin_unlock_bh(&ar->data_lock);
}
static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
ie = (u8 *)cfg80211_find_ie(WLAN_EID_TIM, ies,
(u8 *)skb_tail_pointer(bcn) - ies);
if (!ie) {
- /* highly unlikely for mac80211 */
- ath10k_warn("no tim ie found;\n");
+ if (arvif->vdev_type != WMI_VDEV_TYPE_IBSS)
+ ath10k_warn("no tim ie found;\n");
return;
}
(__le32_to_cpu(ev->sw_version_1) & 0xffff0000) >> 16;
ar->fw_version_build = (__le32_to_cpu(ev->sw_version_1) & 0x0000ffff);
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_dbg(ATH10K_DBG_WMI,
- "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u\n",
+ "wmi event service ready sw_ver 0x%08x sw_ver1 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n",
__le32_to_cpu(ev->sw_version),
__le32_to_cpu(ev->sw_version_1),
__le32_to_cpu(ev->abi_version),
__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_mem_reqs),
+ __le32_to_cpu(ev->num_rf_chains));
complete(&ar->wmi.service_ready);
}
/* connect to control service */
conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL;
- status = ath10k_htc_connect_service(ar->htc, &conn_req, &conn_resp);
+ status = ath10k_htc_connect_service(&ar->htc, &conn_req, &conn_resp);
if (status) {
ath10k_warn("failed to connect to WMI CONTROL service status: %d\n",
status);
if (arg->key_data)
memcpy(cmd->key_data, arg->key_data, arg->key_len);
+ 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);
}
cmd->peer_vht_rates.tx_mcs_set =
__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);
}
ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id);
return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID);
}
+
+int ath10k_wmi_force_fw_hang(struct ath10k *ar,
+ enum wmi_force_fw_hang_type type, u32 delay_ms)
+{
+ struct wmi_force_fw_hang_cmd *cmd;
+ struct sk_buff *skb;
+
+ skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_force_fw_hang_cmd *)skb->data;
+ cmd->type = __cpu_to_le32(type);
+ cmd->delay_ms = __cpu_to_le32(delay_ms);
+
+ ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n",
+ type, delay_ms);
+ return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID);
+}
WMI_PDEV_FTM_INTG_CMDID,
WMI_VDEV_SET_KEEPALIVE_CMDID,
WMI_VDEV_GET_KEEPALIVE_CMDID,
+ WMI_FORCE_FW_HANG_CMDID,
/* GPIO Configuration */
WMI_GPIO_CONFIG_CMDID = WMI_CMD_GRP(WMI_GRP_GPIO),
__le32 cycle_count;
} __packed;
+#define WMI_CHAN_INFO_FLAG_COMPLETE BIT(0)
+
+/* FIXME: empirically extrapolated */
+#define WMI_CHAN_INFO_MSEC(x) ((x) / 76595)
+
/* Beacon filter wmi command info */
#define BCN_FLT_MAX_SUPPORTED_IES 256
#define BCN_FLT_MAX_ELEMS_IE_LIST (BCN_FLT_MAX_SUPPORTED_IES / 32)
struct wmi_sta_keepalive_arp_resp arp_resp;
} __packed;
+enum wmi_force_fw_hang_type {
+ WMI_FORCE_FW_HANG_ASSERT = 1,
+ WMI_FORCE_FW_HANG_NO_DETECT,
+ WMI_FORCE_FW_HANG_CTRL_EP_FULL,
+ WMI_FORCE_FW_HANG_EMPTY_POINT,
+ WMI_FORCE_FW_HANG_STACK_OVERFLOW,
+ WMI_FORCE_FW_HANG_INFINITE_LOOP,
+};
+
+#define WMI_FORCE_FW_HANG_RANDOM_TIME 0xFFFFFFFF
+
+struct wmi_force_fw_hang_cmd {
+ __le32 type;
+ __le32 delay_ms;
+} __packed;
+
#define ATH10K_RTS_MAX 2347
#define ATH10K_FRAGMT_THRESHOLD_MIN 540
#define ATH10K_FRAGMT_THRESHOLD_MAX 2346
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);
#endif /* _WMI_H_ */
#include <linux/average.h>
#include <linux/leds.h>
#include <net/mac80211.h>
+#include <net/cfg80211.h>
/* RX/TX descriptor hw structs
* TODO: Driver part should only see sw structs */
#include <linux/etherdevice.h>
#include <linux/nl80211.h>
+#include <net/cfg80211.h>
#include <net/ieee80211_radiotap.h>
#include <asm/unaligned.h>
.flags = IEEE80211_RATE_SHORT_PREAMBLE },
{ .bitrate = 60,
.hw_value = ATH5K_RATE_CODE_6M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 90,
.hw_value = ATH5K_RATE_CODE_9M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 120,
.hw_value = ATH5K_RATE_CODE_12M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 180,
.hw_value = ATH5K_RATE_CODE_18M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 240,
.hw_value = ATH5K_RATE_CODE_24M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 360,
.hw_value = ATH5K_RATE_CODE_36M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 480,
.hw_value = ATH5K_RATE_CODE_48M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
{ .bitrate = 540,
.hw_value = ATH5K_RATE_CODE_54M,
- .flags = 0 },
+ .flags = IEEE80211_RATE_SUPPORTS_5MHZ |
+ IEEE80211_RATE_SUPPORTS_10MHZ },
};
static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
* Called with ah->lock.
*/
int
-ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan)
+ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef)
{
ATH5K_DBG(ah, ATH5K_DEBUG_RESET,
"channel set, resetting (%u -> %u MHz)\n",
- ah->curchan->center_freq, chan->center_freq);
+ ah->curchan->center_freq, chandef->chan->center_freq);
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ ah->ah_bwmode = AR5K_BWMODE_DEFAULT;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ ah->ah_bwmode = AR5K_BWMODE_5MHZ;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ ah->ah_bwmode = AR5K_BWMODE_10MHZ;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
/*
* To switch channels clear any pending DMA operations;
* hardware at the new frequency, and then re-enable
* the relevant bits of the h/w.
*/
- return ath5k_reset(ah, chan, true);
+ return ath5k_reset(ah, chandef->chan, true);
}
void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
rxs->rate_idx = ath5k_hw_to_driver_rix(ah, rs->rs_rate);
rxs->flag |= ath5k_rx_decrypted(ah, skb, rs);
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_5MHZ:
+ rxs->flag |= RX_FLAG_5MHZ;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ rxs->flag |= RX_FLAG_10MHZ;
+ break;
+ default:
+ break;
+ }
if (rxs->rate_idx >= 0 && rs->rs_rate ==
ah->sbands[ah->curchan->band].bitrates[rxs->rate_idx].hw_value_short)
/* SW support for IBSS_RSN is provided by mac80211 */
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
+ hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_5_10_MHZ;
+
/* both antennas can be configured as RX or TX */
hw->wiphy->available_antennas_tx = 0x3;
hw->wiphy->available_antennas_rx = 0x3;
void ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah,
struct ieee80211_vif *vif);
-int ath5k_chan_set(struct ath5k_hw *ah, struct ieee80211_channel *chan);
+int ath5k_chan_set(struct ath5k_hw *ah, struct cfg80211_chan_def *chandef);
void ath5k_txbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
void ath5k_rxbuf_free_skb(struct ath5k_hw *ah, struct ath5k_buf *bf);
void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ath5k_hw *ah = file->private_data;
char buf[20];
- if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ count = min_t(size_t, count, sizeof(buf) - 1);
+ if (copy_from_user(buf, userbuf, count))
return -EFAULT;
+ buf[count] = '\0';
if (strncmp(buf, "disable", 7) == 0) {
AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE);
pr_info("debugfs disable beacons\n");
unsigned int i;
char buf[20];
- if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ count = min_t(size_t, count, sizeof(buf) - 1);
+ if (copy_from_user(buf, userbuf, count))
return -EFAULT;
+ buf[count] = '\0';
for (i = 0; i < ARRAY_SIZE(dbg_info); i++) {
if (strncmp(buf, dbg_info[i].name,
strlen(dbg_info[i].name)) == 0) {
unsigned int i;
char buf[20];
- if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ count = min_t(size_t, count, sizeof(buf) - 1);
+ if (copy_from_user(buf, userbuf, count))
return -EFAULT;
+ buf[count] = '\0';
if (strncmp(buf, "diversity", 9) == 0) {
ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT);
pr_info("debug: enable diversity\n");
struct ath5k_statistics *st = &ah->stats;
char buf[20];
- if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ count = min_t(size_t, count, sizeof(buf) - 1);
+ if (copy_from_user(buf, userbuf, count))
return -EFAULT;
+ buf[count] = '\0';
if (strncmp(buf, "clear", 5) == 0) {
st->rxerr_crc = 0;
st->rxerr_phy = 0;
struct ath5k_hw *ah = file->private_data;
char buf[20];
- if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ count = min_t(size_t, count, sizeof(buf) - 1);
+ if (copy_from_user(buf, userbuf, count))
return -EFAULT;
+ buf[count] = '\0';
if (strncmp(buf, "sens-low", 8) == 0) {
ath5k_ani_init(ah, ATH5K_ANI_MODE_MANUAL_HIGH);
} else if (strncmp(buf, "sens-high", 9) == 0) {
struct ath5k_hw *ah = file->private_data;
char buf[20];
- if (copy_from_user(buf, userbuf, min(count, sizeof(buf))))
+ count = min_t(size_t, count, sizeof(buf) - 1);
+ if (copy_from_user(buf, userbuf, count))
return -EFAULT;
+ buf[count] = '\0';
if (strncmp(buf, "start", 5) == 0)
ieee80211_wake_queues(ah->hw);
else if (strncmp(buf, "stop", 4) == 0)
mutex_lock(&ah->lock);
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- ret = ath5k_chan_set(ah, conf->chandef.chan);
+ ret = ath5k_chan_set(ah, &conf->chandef);
if (ret < 0)
goto unlock;
}
sifs = AR5K_INIT_SIFS_HALF_RATE;
preamble *= 2;
sym_time *= 2;
+ bitrate = DIV_ROUND_UP(bitrate, 2);
break;
case AR5K_BWMODE_5MHZ:
sifs = AR5K_INIT_SIFS_QUARTER_RATE;
preamble *= 4;
sym_time *= 4;
+ bitrate = DIV_ROUND_UP(bitrate, 4);
break;
default:
sifs = AR5K_INIT_SIFS_DEFAULT_BG;
{
struct ieee80211_channel *channel = ah->ah_current_channel;
enum ieee80211_band band;
+ struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock;
u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
+ u32 rate_flags, i;
if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
return -EINVAL;
else
band = IEEE80211_BAND_2GHZ;
- rate = &ah->sbands[band].bitrates[0];
+ switch (ah->ah_bwmode) {
+ case AR5K_BWMODE_5MHZ:
+ rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ;
+ break;
+ case AR5K_BWMODE_10MHZ:
+ rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ;
+ break;
+ default:
+ rate_flags = 0;
+ break;
+ }
+ sband = &ah->sbands[band];
+ rate = NULL;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+ rate = &sband->bitrates[i];
+ break;
+ }
+ if (WARN_ON(!rate))
+ return -EINVAL;
+
ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false);
/* ack_tx_time includes an SIFS already */
clear_bit(WMI_READY, &ar->flag);
+ if (ar->fw_recovery.enable)
+ del_timer_sync(&ar->fw_recovery.hb_timer);
+
/*
* After wmi_shudown all WMI events will be dropped. We
* need to cleanup the buffers allocated in AP mode and
struct ath6kl_sta *conn = NULL;
u8 i, max_conn;
+ if (is_zero_ether_addr(node_addr))
+ return NULL;
+
max_conn = (vif->nw_type == AP_NETWORK) ? AP_MAX_NUM_STA : 0;
for (i = 0; i < max_conn; i++) {
ath6kl_warn("nla_put failed on testmode rx skb!\n");
}
-int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len)
{
struct ath6kl *ar = wiphy_priv(wiphy);
struct nlattr *tb[ATH6KL_TM_ATTR_MAX + 1];
#ifdef CONFIG_NL80211_TESTMODE
void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len);
-int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len);
+int ath6kl_tm_cmd(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len);
#else
{
}
-static inline int ath6kl_tm_cmd(struct wiphy *wiphy, void *data, int len)
+static inline int ath6kl_tm_cmd(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ void *data, int len)
{
return 0;
}
config ATH9K_DEBUGFS
bool "Atheros ath9k debugging"
- depends on ATH9K
+ depends on ATH9K && DEBUG_FS
select MAC80211_DEBUGFS
select RELAY
---help---
has to be passed to mac80211 using the module parameter,
ieee80211_default_rc_algo.
+config ATH9K_RFKILL
+ bool "Atheros ath9k rfkill support" if EXPERT
+ depends on ATH9K
+ depends on RFKILL=y || RFKILL=ATH9K
+ default y
+ help
+ Say Y to have ath9k poll the RF-Kill GPIO every couple of
+ seconds. Turn off to save power, but enable it if you have
+ a platform that can toggle the RF-Kill GPIO.
+
config ATH9K_HTC
tristate "Atheros HTC based wireless cards support"
depends on USB && MAC80211
#include "ath9k.h"
-static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
+/*
+ * AR9285
+ * ======
+ *
+ * EEPROM has 2 4-bit fields containing the card configuration.
+ *
+ * antdiv_ctl1:
+ * ------------
+ * bb_enable_ant_div_lnadiv : 1
+ * bb_ant_div_alt_gaintb : 1
+ * bb_ant_div_main_gaintb : 1
+ * bb_enable_ant_fast_div : 1
+ *
+ * antdiv_ctl2:
+ * -----------
+ * bb_ant_div_alt_lnaconf : 2
+ * bb_ant_div_main_lnaconf : 2
+ *
+ * The EEPROM bits are used as follows:
+ * ------------------------------------
+ *
+ * bb_enable_ant_div_lnadiv - Enable LNA path rx antenna diversity/combining.
+ * Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *
+ * bb_ant_div_[alt/main]_gaintb - 0 -> Antenna config Alt/Main uses gaintable 0
+ * 1 -> Antenna config Alt/Main uses gaintable 1
+ * Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ *
+ * bb_enable_ant_fast_div - Enable fast antenna diversity.
+ * Set in AR_PHY_CCK_DETECT.
+ *
+ * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
+ * Set in AR_PHY_MULTICHAIN_GAIN_CTL.
+ * 10=LNA1
+ * 01=LNA2
+ * 11=LNA1+LNA2
+ * 00=LNA1-LNA2
+ *
+ * AR9485 / AR9565 / AR9331
+ * ========================
+ *
+ * The same bits are present in the EEPROM, but the location in the
+ * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
+ *
+ * ant_div_alt_lnaconf ==> bit 0~1
+ * ant_div_main_lnaconf ==> bit 2~3
+ * ant_div_alt_gaintb ==> bit 4
+ * ant_div_main_gaintb ==> bit 5
+ * enable_ant_div_lnadiv ==> bit 6
+ * enable_ant_fast_div ==> bit 7
+ */
+
+static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
+ int alt_ratio, int maxdelta,
int mindelta, int main_rssi_avg,
int alt_rssi_avg, int pkt_count)
{
- return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
- (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
- (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
+ if (pkt_count <= 50)
+ return false;
+
+ if (alt_rssi_avg > main_rssi_avg + mindelta)
+ return true;
+
+ if (alt_ratio >= antcomb->ant_ratio2 &&
+ alt_rssi_avg >= antcomb->low_rssi_thresh &&
+ (alt_rssi_avg > main_rssi_avg + maxdelta))
+ return true;
+
+ return false;
}
-static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
- int curr_main_set, int curr_alt_set,
- int alt_rssi_avg, int main_rssi_avg)
+static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
+ struct ath_ant_comb *antcomb,
+ int alt_ratio, int alt_rssi_avg,
+ int main_rssi_avg)
{
- bool result = false;
- switch (div_group) {
+ bool result, set1, set2;
+
+ result = set1 = set2 = false;
+
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
+ conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
+ set1 = true;
+
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
+ conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+ set2 = true;
+
+ switch (conf->div_group) {
case 0:
if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
result = true;
break;
case 1:
case 2:
- if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
- (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
- (alt_rssi_avg >= (main_rssi_avg - 5))) ||
- ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
- (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
- (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
- (alt_rssi_avg >= 4))
+ if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
+ break;
+
+ if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
+ (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
+ (alt_ratio > antcomb->ant_ratio))
result = true;
- else
- result = false;
+
+ break;
+ case 3:
+ if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
+ break;
+
+ if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
+ (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
+ (alt_ratio > antcomb->ant_ratio))
+ result = true;
+
break;
}
}
}
+static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
+ struct ath_hw_antcomb_conf *conf)
+{
+ /* set alt to the conf with maximun ratio */
+ if (antcomb->first_ratio && antcomb->second_ratio) {
+ if (antcomb->rssi_second > antcomb->rssi_third) {
+ /* first alt*/
+ if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+ (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
+ /* Set alt LNA1 or LNA2*/
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ else
+ /* Set alt to A+B or A-B */
+ conf->alt_lna_conf =
+ antcomb->first_quick_scan_conf;
+ } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+ (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
+ /* Set alt LNA1 or LNA2 */
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ } else {
+ /* Set alt to A+B or A-B */
+ conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+ }
+ } else if (antcomb->first_ratio) {
+ /* first alt */
+ if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+ (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
+ /* Set alt LNA1 or LNA2 */
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ else
+ /* Set alt to A+B or A-B */
+ conf->alt_lna_conf = antcomb->first_quick_scan_conf;
+ } else if (antcomb->second_ratio) {
+ /* second alt */
+ if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
+ (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
+ /* Set alt LNA1 or LNA2 */
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ else
+ /* Set alt to A+B or A-B */
+ conf->alt_lna_conf = antcomb->second_quick_scan_conf;
+ } else {
+ /* main is largest */
+ if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
+ (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
+ /* Set alt LNA1 or LNA2 */
+ if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ else
+ /* Set alt to A+B or A-B */
+ conf->alt_lna_conf = antcomb->main_conf;
+ }
+}
+
static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
struct ath_hw_antcomb_conf *div_ant_conf,
int main_rssi_avg, int alt_rssi_avg,
if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
/* main is LNA1 */
- if (ath_is_alt_ant_ratio_better(alt_ratio,
+ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
main_rssi_avg, alt_rssi_avg,
else
antcomb->first_ratio = false;
} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
- if (ath_is_alt_ant_ratio_better(alt_ratio,
+ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
main_rssi_avg, alt_rssi_avg,
else
antcomb->first_ratio = false;
} else {
- if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
- (alt_rssi_avg > main_rssi_avg +
- ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
- (alt_rssi_avg > main_rssi_avg)) &&
- (antcomb->total_pkt_count > 50))
+ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+ 0,
+ main_rssi_avg, alt_rssi_avg,
+ antcomb->total_pkt_count))
antcomb->first_ratio = true;
else
antcomb->first_ratio = false;
antcomb->rssi_first = main_rssi_avg;
antcomb->rssi_third = alt_rssi_avg;
- if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
+ switch(antcomb->second_quick_scan_conf) {
+ case ATH_ANT_DIV_COMB_LNA1:
antcomb->rssi_lna1 = alt_rssi_avg;
- else if (antcomb->second_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA2)
+ break;
+ case ATH_ANT_DIV_COMB_LNA2:
antcomb->rssi_lna2 = alt_rssi_avg;
- else if (antcomb->second_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
+ break;
+ case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
antcomb->rssi_lna2 = main_rssi_avg;
else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
antcomb->rssi_lna1 = main_rssi_avg;
+ break;
+ default:
+ break;
}
if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
- if (ath_is_alt_ant_ratio_better(alt_ratio,
+ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
main_rssi_avg, alt_rssi_avg,
else
antcomb->second_ratio = false;
} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
- if (ath_is_alt_ant_ratio_better(alt_ratio,
+ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
main_rssi_avg, alt_rssi_avg,
else
antcomb->second_ratio = false;
} else {
- if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
- (alt_rssi_avg > main_rssi_avg +
- ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
- (alt_rssi_avg > main_rssi_avg)) &&
- (antcomb->total_pkt_count > 50))
+ if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
+ ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
+ 0,
+ main_rssi_avg, alt_rssi_avg,
+ antcomb->total_pkt_count))
antcomb->second_ratio = true;
else
antcomb->second_ratio = false;
}
- /* set alt to the conf with maximun ratio */
- if (antcomb->first_ratio && antcomb->second_ratio) {
- if (antcomb->rssi_second > antcomb->rssi_third) {
- /* first alt*/
- if ((antcomb->first_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA1) ||
- (antcomb->first_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA2))
- /* Set alt LNA1 or LNA2*/
- if (div_ant_conf->main_lna_conf ==
- ATH_ANT_DIV_COMB_LNA2)
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- else
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- else
- /* Set alt to A+B or A-B */
- div_ant_conf->alt_lna_conf =
- antcomb->first_quick_scan_conf;
- } else if ((antcomb->second_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA1) ||
- (antcomb->second_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA2)) {
- /* Set alt LNA1 or LNA2 */
- if (div_ant_conf->main_lna_conf ==
- ATH_ANT_DIV_COMB_LNA2)
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- else
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- } else {
- /* Set alt to A+B or A-B */
- div_ant_conf->alt_lna_conf =
- antcomb->second_quick_scan_conf;
- }
- } else if (antcomb->first_ratio) {
- /* first alt */
- if ((antcomb->first_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA1) ||
- (antcomb->first_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA2))
- /* Set alt LNA1 or LNA2 */
- if (div_ant_conf->main_lna_conf ==
- ATH_ANT_DIV_COMB_LNA2)
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- else
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- else
- /* Set alt to A+B or A-B */
- div_ant_conf->alt_lna_conf =
- antcomb->first_quick_scan_conf;
- } else if (antcomb->second_ratio) {
- /* second alt */
- if ((antcomb->second_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA1) ||
- (antcomb->second_quick_scan_conf ==
- ATH_ANT_DIV_COMB_LNA2))
- /* Set alt LNA1 or LNA2 */
- if (div_ant_conf->main_lna_conf ==
- ATH_ANT_DIV_COMB_LNA2)
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- else
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- else
- /* Set alt to A+B or A-B */
- div_ant_conf->alt_lna_conf =
- antcomb->second_quick_scan_conf;
- } else {
- /* main is largest */
- if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
- (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
- /* Set alt LNA1 or LNA2 */
- if (div_ant_conf->main_lna_conf ==
- ATH_ANT_DIV_COMB_LNA2)
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- else
- div_ant_conf->alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- else
- /* Set alt to A+B or A-B */
- div_ant_conf->alt_lna_conf = antcomb->main_conf;
- }
+ ath_ant_set_alt_ratio(antcomb, div_ant_conf);
+
break;
default:
break;
ant_conf->fast_div_bias = 0x1;
break;
case 0x10: /* LNA2 A-B */
- if (!(antcomb->scan) &&
- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
ant_conf->fast_div_bias = 0x1;
else
ant_conf->fast_div_bias = 0x2;
ant_conf->fast_div_bias = 0x1;
break;
case 0x13: /* LNA2 A+B */
- if (!(antcomb->scan) &&
- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
ant_conf->fast_div_bias = 0x1;
else
ant_conf->fast_div_bias = 0x2;
break;
case 0x20: /* LNA1 A-B */
- if (!(antcomb->scan) &&
- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
ant_conf->fast_div_bias = 0x1;
else
ant_conf->fast_div_bias = 0x2;
ant_conf->fast_div_bias = 0x1;
break;
case 0x23: /* LNA1 A+B */
- if (!(antcomb->scan) &&
- (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
+ if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
ant_conf->fast_div_bias = 0x1;
else
ant_conf->fast_div_bias = 0x2;
default:
break;
}
+
+ if (antcomb->fast_div_bias)
+ ant_conf->fast_div_bias = antcomb->fast_div_bias;
} else if (ant_conf->div_group == 3) {
switch ((ant_conf->main_lna_conf << 4) |
ant_conf->alt_lna_conf) {
}
}
+static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
+ struct ath_hw_antcomb_conf *conf,
+ int curr_alt_set, int alt_rssi_avg,
+ int main_rssi_avg)
+{
+ switch (curr_alt_set) {
+ case ATH_ANT_DIV_COMB_LNA2:
+ antcomb->rssi_lna2 = alt_rssi_avg;
+ antcomb->rssi_lna1 = main_rssi_avg;
+ antcomb->scan = true;
+ /* set to A+B */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+ break;
+ case ATH_ANT_DIV_COMB_LNA1:
+ antcomb->rssi_lna1 = alt_rssi_avg;
+ antcomb->rssi_lna2 = main_rssi_avg;
+ antcomb->scan = true;
+ /* set to A+B */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+ break;
+ case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
+ antcomb->rssi_add = alt_rssi_avg;
+ antcomb->scan = true;
+ /* set to A-B */
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+ break;
+ case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
+ antcomb->rssi_sub = alt_rssi_avg;
+ antcomb->scan = false;
+ if (antcomb->rssi_lna2 >
+ (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
+ /* use LNA2 as main LNA */
+ if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
+ (antcomb->rssi_add > antcomb->rssi_sub)) {
+ /* set to A+B */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+ } else if (antcomb->rssi_sub >
+ antcomb->rssi_lna1) {
+ /* set to A-B */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+ } else {
+ /* set to LNA1 */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ }
+ } else {
+ /* use LNA1 as main LNA */
+ if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
+ (antcomb->rssi_add > antcomb->rssi_sub)) {
+ /* set to A+B */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
+ } else if (antcomb->rssi_sub >
+ antcomb->rssi_lna1) {
+ /* set to A-B */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
+ } else {
+ /* set to LNA2 */
+ conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
+ struct ath_ant_comb *antcomb,
+ int alt_ratio, int alt_rssi_avg,
+ int main_rssi_avg, int curr_main_set,
+ int curr_alt_set)
+{
+ bool ret = false;
+
+ if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
+ alt_rssi_avg, main_rssi_avg)) {
+ if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
+ /*
+ * Switch main and alt LNA.
+ */
+ div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
+ div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+ }
+
+ ret = true;
+ } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
+ (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
+ /*
+ Set alt to another LNA.
+ */
+ if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
+ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
+ else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
+ div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
+
+ ret = true;
+ }
+
+ return ret;
+}
+
+static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
+{
+ int alt_ratio;
+
+ if (!antcomb->scan || !antcomb->alt_good)
+ return false;
+
+ if (time_after(jiffies, antcomb->scan_start_time +
+ msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
+ return true;
+
+ if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
+ alt_ratio = ((antcomb->alt_recv_cnt * 100) /
+ antcomb->total_pkt_count);
+ if (alt_ratio < antcomb->ant_ratio)
+ return true;
+ }
+
+ return false;
+}
+
void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
{
struct ath_hw_antcomb_conf div_ant_conf;
int main_rssi = rs->rs_rssi_ctl0;
int alt_rssi = rs->rs_rssi_ctl1;
int rx_ant_conf, main_ant_conf;
- bool short_scan = false;
+ bool short_scan = false, ret;
rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
ATH_ANT_RX_MASK;
main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
ATH_ANT_RX_MASK;
+ if (alt_rssi >= antcomb->low_rssi_thresh) {
+ antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
+ antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
+ } else {
+ antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
+ antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
+ }
+
/* Record packet only when both main_rssi and alt_rssi is positive */
if (main_rssi > 0 && alt_rssi > 0) {
antcomb->total_pkt_count++;
antcomb->main_total_rssi += main_rssi;
antcomb->alt_total_rssi += alt_rssi;
+
if (main_ant_conf == rx_ant_conf)
antcomb->main_recv_cnt++;
else
antcomb->alt_recv_cnt++;
}
- /* Short scan check */
- if (antcomb->scan && antcomb->alt_good) {
- if (time_after(jiffies, antcomb->scan_start_time +
- msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
- short_scan = true;
- else
- if (antcomb->total_pkt_count ==
- ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
- alt_ratio = ((antcomb->alt_recv_cnt * 100) /
- antcomb->total_pkt_count);
- if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
- short_scan = true;
- }
+ if (main_ant_conf == rx_ant_conf) {
+ ANT_STAT_INC(ANT_MAIN, recv_cnt);
+ ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
+ } else {
+ ANT_STAT_INC(ANT_ALT, recv_cnt);
+ ANT_LNA_INC(ANT_ALT, rx_ant_conf);
}
+ /* Short scan check */
+ short_scan = ath_ant_short_scan_check(antcomb);
+
if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
- rs->rs_moreaggr) && !short_scan)
+ rs->rs_moreaggr) && !short_scan)
return;
if (antcomb->total_pkt_count) {
antcomb->total_pkt_count);
}
-
ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
curr_alt_set = div_ant_conf.alt_lna_conf;
curr_main_set = div_ant_conf.main_lna_conf;
-
antcomb->count++;
if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
- if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
+ if (alt_ratio > antcomb->ant_ratio) {
ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
main_rssi_avg);
antcomb->alt_good = true;
}
if (!antcomb->scan) {
- if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
- alt_ratio, curr_main_set, curr_alt_set,
- alt_rssi_avg, main_rssi_avg)) {
- if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
- /* Switch main and alt LNA */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- }
-
- goto div_comb_done;
- } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
- (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
- /* Set alt to another LNA */
- if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
-
- goto div_comb_done;
- }
-
- if ((alt_rssi_avg < (main_rssi_avg +
- div_ant_conf.lna1_lna2_delta)))
+ ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
+ alt_rssi_avg, main_rssi_avg,
+ curr_main_set, curr_alt_set);
+ if (ret)
goto div_comb_done;
}
+ if (!antcomb->scan &&
+ (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
+ goto div_comb_done;
+
if (!antcomb->scan_not_start) {
- switch (curr_alt_set) {
- case ATH_ANT_DIV_COMB_LNA2:
- antcomb->rssi_lna2 = alt_rssi_avg;
- antcomb->rssi_lna1 = main_rssi_avg;
- antcomb->scan = true;
- /* set to A+B */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
- break;
- case ATH_ANT_DIV_COMB_LNA1:
- antcomb->rssi_lna1 = alt_rssi_avg;
- antcomb->rssi_lna2 = main_rssi_avg;
- antcomb->scan = true;
- /* set to A+B */
- div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
- break;
- case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
- antcomb->rssi_add = alt_rssi_avg;
- antcomb->scan = true;
- /* set to A-B */
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
- break;
- case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
- antcomb->rssi_sub = alt_rssi_avg;
- antcomb->scan = false;
- if (antcomb->rssi_lna2 >
- (antcomb->rssi_lna1 +
- ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
- /* use LNA2 as main LNA */
- if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
- (antcomb->rssi_add > antcomb->rssi_sub)) {
- /* set to A+B */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
- } else if (antcomb->rssi_sub >
- antcomb->rssi_lna1) {
- /* set to A-B */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
- } else {
- /* set to LNA1 */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- }
- } else {
- /* use LNA1 as main LNA */
- if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
- (antcomb->rssi_add > antcomb->rssi_sub)) {
- /* set to A+B */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
- } else if (antcomb->rssi_sub >
- antcomb->rssi_lna1) {
- /* set to A-B */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
- } else {
- /* set to LNA2 */
- div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
- div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
- }
- }
- break;
- default:
- break;
- }
+ ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
+ alt_rssi_avg, main_rssi_avg);
} else {
if (!antcomb->alt_good) {
antcomb->scan_not_start = false;
/* Set alt to another LNA */
if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
+ ATH_ANT_DIV_COMB_LNA2;
div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
+ ATH_ANT_DIV_COMB_LNA1;
} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
div_ant_conf.main_lna_conf =
- ATH_ANT_DIV_COMB_LNA1;
+ ATH_ANT_DIV_COMB_LNA1;
div_ant_conf.alt_lna_conf =
- ATH_ANT_DIV_COMB_LNA2;
+ ATH_ANT_DIV_COMB_LNA2;
}
goto div_comb_done;
}
+ ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
+ main_rssi_avg, alt_rssi_avg,
+ alt_ratio);
+ antcomb->quick_scan_cnt++;
}
- ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
- main_rssi_avg, alt_rssi_avg,
- alt_ratio);
-
- antcomb->quick_scan_cnt++;
-
div_comb_done:
ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
+ ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
antcomb->scan_start_time = jiffies;
antcomb->total_pkt_count = 0;
antcomb->main_recv_cnt = 0;
antcomb->alt_recv_cnt = 0;
}
-
-void ath_ant_comb_update(struct ath_softc *sc)
-{
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ath_hw_antcomb_conf div_ant_conf;
- u8 lna_conf;
-
- ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
-
- if (sc->ant_rx == 1)
- lna_conf = ATH_ANT_DIV_COMB_LNA1;
- else
- lna_conf = ATH_ANT_DIV_COMB_LNA2;
-
- div_ant_conf.main_lna_conf = lna_conf;
- div_ant_conf.alt_lna_conf = lna_conf;
-
- ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
-
- if (common->antenna_diversity)
- ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
-}
if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
val |= AR_WA_D3_L1_DISABLE;
} else {
- if (((AR_SREV_9285(ah) ||
- AR_SREV_9271(ah) ||
- AR_SREV_9287(ah)) &&
- (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) ||
- (AR_SREV_9280(ah) &&
- (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) {
- val |= AR_WA_D3_L1_DISABLE;
+ if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) {
+ if (AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
+ val |= AR_WA_D3_L1_DISABLE;
+ } else if (AR_SREV_9280(ah)) {
+ if (AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE)
+ val |= AR_WA_D3_L1_DISABLE;
}
}
} else {
if (ah->config.pcie_waen) {
val = ah->config.pcie_waen;
- if (!power_off)
- val &= (~AR_WA_D3_L1_DISABLE);
+ val &= (~AR_WA_D3_L1_DISABLE);
} else {
- if (AR_SREV_9285(ah) ||
- AR_SREV_9271(ah) ||
- AR_SREV_9287(ah)) {
+ if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) {
val = AR9285_WA_DEFAULT;
- if (!power_off)
- val &= (~AR_WA_D3_L1_DISABLE);
- }
- else if (AR_SREV_9280(ah)) {
+ val &= (~AR_WA_D3_L1_DISABLE);
+ } else if (AR_SREV_9280(ah)) {
/*
* For AR9280 chips, bit 22 of 0x4004
* needs to be set.
*/
val = AR9280_WA_DEFAULT;
- if (!power_off)
- val &= (~AR_WA_D3_L1_DISABLE);
+ val &= (~AR_WA_D3_L1_DISABLE);
} else {
val = AR_WA_DEFAULT;
}
REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
}
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static void ar9002_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
+{
+ struct ath_btcoex_hw *btcoex = &ah->btcoex_hw;
+ u8 antdiv_ctrl1, antdiv_ctrl2;
+ u32 regval;
+
+ if (enable) {
+ antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE;
+ antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE;
+
+ /*
+ * Don't disable BT ant to allow BB to control SWCOM.
+ */
+ btcoex->bt_coex_mode2 &= (~(AR_BT_DISABLE_BT_ANT));
+ REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
+
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, ATH_BT_COEX_ANT_DIV_SWITCH_COM);
+ REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+ } else {
+ /*
+ * Disable antenna diversity, use LNA1 only.
+ */
+ antdiv_ctrl1 = ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A;
+ antdiv_ctrl2 = ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A;
+
+ /*
+ * Disable BT Ant. to allow concurrent BT and WLAN receive.
+ */
+ btcoex->bt_coex_mode2 |= AR_BT_DISABLE_BT_ANT;
+ REG_WRITE(ah, AR_BT_COEX_MODE2, btcoex->bt_coex_mode2);
+
+ /*
+ * Program SWCOM table to make sure RF switch always parks
+ * at BT side.
+ */
+ REG_WRITE(ah, AR_PHY_SWITCH_COM, 0);
+ REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000);
+ }
+
+ regval = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+ regval &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
+ /*
+ * Clear ant_fast_div_bias [14:9] since for WB195,
+ * the main LNA is always LNA1.
+ */
+ regval &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+ regval |= SM(antdiv_ctrl1, AR_PHY_9285_ANT_DIV_CTL);
+ regval |= SM(antdiv_ctrl2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
+ regval |= SM((antdiv_ctrl2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
+ regval |= SM((antdiv_ctrl1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
+ regval |= SM((antdiv_ctrl1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
+ REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+
+ regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+ regval &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+ regval |= SM((antdiv_ctrl1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+ REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+}
+
+#endif
+
static void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
struct ath_spec_scan *param)
{
ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger;
ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait;
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+ ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity;
+#endif
+
ar9002_hw_set_nf_limits(ah);
}
#define AR_PHY_9285_ANT_DIV_ALT_GAINTB_S 29
#define AR_PHY_9285_ANT_DIV_MAIN_GAINTB 0x40000000
#define AR_PHY_9285_ANT_DIV_MAIN_GAINTB_S 30
-#define AR_PHY_9285_ANT_DIV_LNA1 2
-#define AR_PHY_9285_ANT_DIV_LNA2 1
-#define AR_PHY_9285_ANT_DIV_LNA1_PLUS_LNA2 3
-#define AR_PHY_9285_ANT_DIV_LNA1_MINUS_LNA2 0
#define AR_PHY_9285_ANT_DIV_GAINTB_0 0
#define AR_PHY_9285_ANT_DIV_GAINTB_1 1
+#define ATH_BT_COEX_ANTDIV_CONTROL1_ENABLE 0x0b
+#define ATH_BT_COEX_ANTDIV_CONTROL2_ENABLE 0x09
+#define ATH_BT_COEX_ANTDIV_CONTROL1_FIXED_A 0x04
+#define ATH_BT_COEX_ANTDIV_CONTROL2_FIXED_A 0x09
+#define ATH_BT_COEX_ANT_DIV_SWITCH_COM 0x66666666
+
#define AR_PHY_EXT_CCA0 0x99b8
#define AR_PHY_EXT_CCA0_THRESH62 0x000000FF
#define AR_PHY_EXT_CCA0_THRESH62_S 0
return le16_to_cpu(ar9003_modal_header(ah, is2ghz)->switchcomspdt);
}
-
-static u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
+u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz)
{
return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon);
}
-static u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
+u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz)
{
return le32_to_cpu(ar9003_modal_header(ah, is2ghz)->antCtrlCommon2);
}
static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
{
+ struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_hw_capabilities *pCap = &ah->caps;
int chain;
u32 regval, value, gpio;
}
value = ar9003_hw_ant_ctrl_common_2_get(ah, is2ghz);
+ if (AR_SREV_9485(ah) && common->bt_ant_diversity) {
+ regval &= ~AR_SWITCH_TABLE_COM2_ALL;
+ regval |= ah->config.ant_ctrl_comm2g_switch_enable;
+
+ }
REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2, AR_SWITCH_TABLE_COM2_ALL, value);
if ((AR_SREV_9462(ah)) && (ah->rxchainmask == 0x2)) {
regval &= (~AR_PHY_ANT_DIV_LNADIV);
regval |= ((value >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+ if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+ regval |= AR_ANT_DIV_ENABLE;
+
if (AR_SREV_9565(ah)) {
- if (ah->shared_chain_lnadiv) {
+ if (common->bt_ant_diversity) {
regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S);
} else {
regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S);
REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
- /*enable fast_div */
+ /* enable fast_div */
regval = REG_READ(ah, AR_PHY_CCK_DETECT);
regval &= (~AR_FAST_DIV_ENABLE);
regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+
+ if (AR_SREV_9485(ah) && common->bt_ant_diversity)
+ regval |= AR_FAST_DIV_ENABLE;
+
REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
AR_PHY_ANT_DIV_ALT_GAINTB |
AR_PHY_ANT_DIV_MAIN_GAINTB));
/* by default use LNA1 for the main antenna */
- regval |= (AR_PHY_ANT_DIV_LNA1 <<
+ regval |= (ATH_ANT_DIV_COMB_LNA1 <<
AR_PHY_ANT_DIV_MAIN_LNACONF_S);
- regval |= (AR_PHY_ANT_DIV_LNA2 <<
+ regval |= (ATH_ANT_DIV_COMB_LNA2 <<
AR_PHY_ANT_DIV_ALT_LNACONF_S);
REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
}
else
value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+ if (ah->config.alt_mingainidx)
+ REG_RMW_FIELD(ah, AR_PHY_EXT_ATTEN_CTL_0,
+ AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
+ value);
+
REG_RMW_FIELD(ah, ext_atten_reg[i],
AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN,
value);
s32 ar9003_hw_get_tx_gain_idx(struct ath_hw *ah);
s32 ar9003_hw_get_rx_gain_idx(struct ath_hw *ah);
+u32 ar9003_hw_ant_ctrl_common_get(struct ath_hw *ah, bool is2ghz);
+u32 ar9003_hw_ant_ctrl_common_2_get(struct ath_hw *ah, bool is2ghz);
u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz);
if (!ah->is_clk_25mhz)
INIT_INI_ARRAY(&ah->iniAdditional,
ar9340_1p0_radio_core_40M);
- } else if (AR_SREV_9485_11(ah)) {
+ } else if (AR_SREV_9485_11_OR_LATER(ah)) {
/* mac */
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
ar9485_1_1_mac_core);
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_lowest_ob_db_tx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485_modes_lowest_ob_db_tx_gain_1_1);
else if (AR_SREV_9550(ah))
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_high_ob_db_tx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_high_ob_db_tx_gain_1_1);
else if (AR_SREV_9580(ah))
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_low_ob_db_tx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_low_ob_db_tx_gain_1_1);
else if (AR_SREV_9580(ah))
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_high_power_tx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_high_power_tx_gain_1_1);
else if (AR_SREV_9580(ah))
static void ar9003_tx_gain_table_mode5(struct ath_hw *ah)
{
- if (AR_SREV_9485_11(ah))
+ if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_green_ob_db_tx_gain_1_1);
else if (AR_SREV_9340(ah))
if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9340Modes_low_ob_db_and_spur_tx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesTxGain,
ar9485Modes_green_spur_ob_db_tx_gain_1_1);
else if (AR_SREV_9580(ah))
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9340Common_rx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9485_common_rx_gain_1_1);
else if (AR_SREV_9550(ah)) {
else if (AR_SREV_9340(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9340Common_wo_xlna_rx_gain_table_1p0);
- else if (AR_SREV_9485_11(ah))
+ else if (AR_SREV_9485_11_OR_LATER(ah))
INIT_INI_ARRAY(&ah->iniModesRxGain,
ar9485Common_wo_xlna_rx_gain_1_1);
else if (AR_SREV_9462_21(ah))
static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
bool power_off)
{
+ /*
+ * Increase L1 Entry Latency. Some WB222 boards don't have
+ * this change in eeprom/OTP.
+ *
+ */
+ if (AR_SREV_9462(ah)) {
+ u32 val = ah->config.aspm_l1_fix;
+ if ((val & 0xff000000) == 0x17000000) {
+ val &= 0x00ffffff;
+ val |= 0x27000000;
+ REG_WRITE(ah, 0x570c, val);
+ }
+ }
+
/* Nothing to do on restore for 11N */
if (!power_off /* !restore */) {
/* set bit 19 to allow forcing of pcie core into L1 state */
REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA);
-
- /* Several PCIe massages to ensure proper behaviour */
- if (ah->config.pcie_waen)
- REG_WRITE(ah, AR_WA, ah->config.pcie_waen);
- else
- REG_WRITE(ah, AR_WA, ah->WARegVal);
+ REG_WRITE(ah, AR_WA, ah->WARegVal);
}
/*
rxs->rs_rate = MS(rxsp->status1, AR_RxRate);
rxs->rs_more = (rxsp->status2 & AR_RxMore) ? 1 : 0;
+ rxs->rs_firstaggr = (rxsp->status11 & AR_RxFirstAggr) ? 1 : 0;
rxs->rs_isaggr = (rxsp->status11 & AR_RxAggr) ? 1 : 0;
rxs->rs_moreaggr = (rxsp->status11 & AR_RxMoreAggr) ? 1 : 0;
rxs->rs_antenna = (MS(rxsp->status4, AR_RxAntenna) & 0x7);
REG_SET_BIT(ah, AR_PHY_CCK_DETECT,
AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
+
+ if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
+ REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
+ AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
+
+ if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+ AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
+ ah->enabled_cals |= TX_IQ_CAL;
+ 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;
+ }
}
static void ar9003_hw_prog_ini(struct ath_hw *ah,
if (chan->channel == 2484)
ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
- if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
- REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE,
- AR_GLB_SWREG_DISCONT_EN_BT_WLAN);
-
ah->modes_index = modesIndex;
ar9003_hw_override_ini(ah);
ar9003_hw_set_channel_regs(ah, chan);
ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask);
ath9k_hw_apply_txpower(ah, chan, false);
- if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
- if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0,
- AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL))
- ah->enabled_cals |= TX_IQ_CAL;
- 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;
- }
-
return 0;
}
* is_on == 0 means MRC CCK is OFF (more noise imm)
*/
bool is_on = param ? 1 : 0;
+
+ if (ah->caps.rx_chainmask == 1)
+ break;
+
REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
AR_PHY_MRC_CCK_ENABLE, is_on);
REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
}
-static void ar9003_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
- bool enable)
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
{
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
u8 ant_div_ctl1;
u32 regval;
- if (!AR_SREV_9565(ah))
+ if (!AR_SREV_9485(ah) && !AR_SREV_9565(ah))
return;
- ah->shared_chain_lnadiv = enable;
+ if (AR_SREV_9485(ah)) {
+ regval = ar9003_hw_ant_ctrl_common_2_get(ah,
+ IS_CHAN_2GHZ(ah->curchan));
+ if (enable) {
+ regval &= ~AR_SWITCH_TABLE_COM2_ALL;
+ regval |= ah->config.ant_ctrl_comm2g_switch_enable;
+ }
+ REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM_2,
+ AR_SWITCH_TABLE_COM2_ALL, regval);
+ }
+
ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
+ /*
+ * Set MAIN/ALT LNA conf.
+ * Set MAIN/ALT gain_tb.
+ */
regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
regval &= (~AR_ANT_DIV_CTRL_ALL);
regval |= (ant_div_ctl1 & 0x3f) << AR_ANT_DIV_CTRL_ALL_S;
- regval &= ~AR_PHY_ANT_DIV_LNADIV;
- regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
-
- if (enable)
- regval |= AR_ANT_DIV_ENABLE;
-
REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
- regval = REG_READ(ah, AR_PHY_CCK_DETECT);
- regval &= ~AR_FAST_DIV_ENABLE;
- regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
-
- if (enable)
- regval |= AR_FAST_DIV_ENABLE;
-
- REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
-
- if (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_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,
- (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_BTCOEX_WL_LNADIV,
- AR_BTCOEX_WL_LNADIV_FORCE_ON);
-
+ if (AR_SREV_9485_11_OR_LATER(ah)) {
+ /*
+ * Enable LNA diversity.
+ */
regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
- regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
- AR_PHY_ANT_DIV_ALT_LNACONF |
- AR_PHY_ANT_DIV_MAIN_GAINTB |
- AR_PHY_ANT_DIV_ALT_GAINTB);
- regval |= (AR_PHY_ANT_DIV_LNA1 << AR_PHY_ANT_DIV_MAIN_LNACONF_S);
- regval |= (AR_PHY_ANT_DIV_LNA2 << AR_PHY_ANT_DIV_ALT_LNACONF_S);
+ regval &= ~AR_PHY_ANT_DIV_LNADIV;
+ regval |= ((ant_div_ctl1 >> 6) & 0x1) << AR_PHY_ANT_DIV_LNADIV_S;
+ if (enable)
+ regval |= AR_ANT_DIV_ENABLE;
+
REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+
+ /*
+ * Enable fast antenna diversity.
+ */
+ regval = REG_READ(ah, AR_PHY_CCK_DETECT);
+ regval &= ~AR_FAST_DIV_ENABLE;
+ regval |= ((ant_div_ctl1 >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S;
+ if (enable)
+ regval |= AR_FAST_DIV_ENABLE;
+
+ REG_WRITE(ah, AR_PHY_CCK_DETECT, regval);
+
+ if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+ regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+ regval &= (~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+ AR_PHY_ANT_DIV_ALT_LNACONF |
+ AR_PHY_ANT_DIV_ALT_GAINTB |
+ AR_PHY_ANT_DIV_MAIN_GAINTB));
+ /*
+ * Set MAIN to LNA1 and ALT to LNA2 at the
+ * beginning.
+ */
+ regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+ AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+ regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+ AR_PHY_ANT_DIV_ALT_LNACONF_S);
+ REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+ }
+ } else if (AR_SREV_9565(ah)) {
+ if (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_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,
+ (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_BTCOEX_WL_LNADIV,
+ AR_BTCOEX_WL_LNADIV_FORCE_ON);
+
+ regval = REG_READ(ah, AR_PHY_MC_GAIN_CTRL);
+ regval &= ~(AR_PHY_ANT_DIV_MAIN_LNACONF |
+ AR_PHY_ANT_DIV_ALT_LNACONF |
+ AR_PHY_ANT_DIV_MAIN_GAINTB |
+ AR_PHY_ANT_DIV_ALT_GAINTB);
+ regval |= (ATH_ANT_DIV_COMB_LNA1 <<
+ AR_PHY_ANT_DIV_MAIN_LNACONF_S);
+ regval |= (ATH_ANT_DIV_COMB_LNA2 <<
+ AR_PHY_ANT_DIV_ALT_LNACONF_S);
+ REG_WRITE(ah, AR_PHY_MC_GAIN_CTRL, regval);
+ }
}
}
+#endif
+
static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
struct ath9k_channel *chan,
u8 *ini_reloaded)
REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
+ if (AR_SREV_9462_20_OR_LATER(ah)) {
+ /*
+ * CUS217 mix LNA mode.
+ */
+ if (ar9003_hw_get_rx_gain_idx(ah) == 2) {
+ REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_core,
+ 1, regWrites);
+ REG_WRITE_ARRAY(&ah->ini_modes_rxgain_bb_postamble,
+ modesIndex, regWrites);
+ }
+ }
+
/*
* For 5GHz channels requiring Fast Clock, apply
* different modal values.
if (AR_SREV_9565(ah))
REG_WRITE_ARRAY(&ah->iniModesFastClock, 1, regWrites);
- REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites);
+ /*
+ * JAPAN regulatory.
+ */
+ if (chan->channel == 2484)
+ ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1);
ah->modes_index = modesIndex;
*ini_reloaded = true;
ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
- ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv;
ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger;
ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait;
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+ ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity;
+#endif
+
ar9003_hw_set_nf_limits(ah);
ar9003_hw_set_radar_conf(ah);
memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs));
#define AR_PHY_SFCORR_SPUR_SUBCHNL_SD_S 28
#define AR_PHY_EXT_CCA_THRESH62 0x007F0000
#define AR_PHY_EXT_CCA_THRESH62_S 16
+#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX 0x0000FF00
+#define AR_PHY_EXTCHN_PWRTHR1_ANT_DIV_ALT_ANT_MINGAINIDX_S 8
#define AR_PHY_EXT_MINCCA_PWR 0x01FF0000
#define AR_PHY_EXT_MINCCA_PWR_S 16
#define AR_PHY_EXT_CYCPWR_THR1 0x0000FE00L
#define AR_PHY_ANT_DIV_MAIN_GAINTB 0x40000000
#define AR_PHY_ANT_DIV_MAIN_GAINTB_S 30
-#define AR_PHY_ANT_DIV_LNA1_MINUS_LNA2 0x0
-#define AR_PHY_ANT_DIV_LNA2 0x1
-#define AR_PHY_ANT_DIV_LNA1 0x2
-#define AR_PHY_ANT_DIV_LNA1_PLUS_LNA2 0x3
-
#define AR_PHY_EXTCHN_PWRTHR1 (AR_AGC_BASE + 0x2c)
#define AR_PHY_EXT_CHN_WIN (AR_AGC_BASE + 0x30)
#define AR_PHY_20_40_DET_THR (AR_AGC_BASE + 0x34)
/*************************/
#define ATH_TXBUF_RESET(_bf) do { \
- (_bf)->bf_stale = false; \
(_bf)->bf_lastbf = NULL; \
(_bf)->bf_next = NULL; \
memset(&((_bf)->bf_state), 0, \
sizeof(struct ath_buf_state)); \
} while (0)
-#define ATH_RXBUF_RESET(_bf) do { \
- (_bf)->bf_stale = false; \
- } while (0)
-
/**
* enum buffer_type - Buffer type flags
*
#define ATH_AGGR_ENCRYPTDELIM 10
/* minimum h/w qdepth to be sustained to maximize aggregation */
#define ATH_AGGR_MIN_QDEPTH 2
-#define ATH_AMPDU_SUBFRAME_DEFAULT 32
+/* minimum h/w qdepth for non-aggregated traffic */
+#define ATH_NON_AGGR_MIN_QDEPTH 8
#define IEEE80211_SEQ_SEQ_SHIFT 4
#define IEEE80211_SEQ_MAX 4096
#define ATH_TX_COMPLETE_POLL_INT 1000
-enum ATH_AGGR_STATUS {
- ATH_AGGR_DONE,
- ATH_AGGR_BAW_CLOSED,
- ATH_AGGR_LIMITED,
-};
-
#define ATH_TXFIFO_DEPTH 8
struct ath_txq {
int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
struct ath_atx_ac {
struct ath_txq *txq;
- int sched;
struct list_head list;
struct list_head tid_q;
bool clear_ps_filter;
+ bool sched;
};
struct ath_frame_info {
int framelen;
enum ath9k_key_type keytype;
u8 keyix;
- u8 retries;
u8 rtscts_rate;
+ u8 retries : 7;
+ u8 baw_tracked : 1;
};
struct ath_buf_state {
u8 bf_type;
u8 bfs_paprd;
u8 ndelim;
+ bool stale;
u16 seqno;
unsigned long bfs_paprd_timestamp;
};
void *bf_desc; /* virtual addr of desc */
dma_addr_t bf_daddr; /* physical addr of desc */
dma_addr_t bf_buf_addr; /* physical addr of data buffer, for DMA */
- bool bf_stale;
struct ieee80211_tx_rate rates[4];
struct ath_buf_state bf_state;
};
struct ath_atx_tid {
struct list_head list;
struct sk_buff_head buf_q;
+ struct sk_buff_head retry_q;
struct ath_node *an;
struct ath_atx_ac *ac;
unsigned long tx_buf[BITS_TO_LONGS(ATH_TID_MAX_BUFS)];
- int bar_index;
u16 seq_start;
u16 seq_next;
u16 baw_size;
- int tidno;
+ u8 tidno;
int baw_head; /* first un-acked tx buffer */
int baw_tail; /* next unused tx buffer slot */
+
+ s8 bar_index;
bool sched;
bool paused;
bool active;
struct ieee80211_vif *vif; /* interface with which we're associated */
struct ath_atx_tid tid[IEEE80211_NUM_TIDS];
struct ath_atx_ac ac[IEEE80211_NUM_ACS];
- int ps_key;
u16 maxampdu;
u8 mpdudensity;
+ s8 ps_key;
bool sleeping;
-
-#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
- struct dentry *node_stat;
-#endif
+ bool no_ps_filter;
};
struct ath_tx_control {
struct ath_descdma rxdma;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
+ struct ath_buf *buf_hold;
struct sk_buff *frag;
u32 ampdu_ref;
/********/
struct ath_vif {
+ struct ath_node mcast_node;
int av_bslot;
bool primary_sta_vif;
__le64 tsf_adjust; /* TSF adjustment for staggered beacons */
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);
/*******************/
/* Link Monitoring */
#define ATH_ANT_DIV_COMB_MAX_COUNT 100
#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO 30
#define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2 20
+#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
-enum ath9k_ant_div_comb_lna_conf {
- ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
- ATH_ANT_DIV_COMB_LNA2,
- ATH_ANT_DIV_COMB_LNA1,
- ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
-};
-
struct ath_ant_comb {
u16 count;
u16 total_pkt_count;
int rssi_first;
int rssi_second;
int rssi_third;
+ int ant_ratio;
+ int ant_ratio2;
bool alt_good;
int quick_scan_cnt;
- int main_conf;
+ enum ath9k_ant_div_comb_lna_conf main_conf;
enum ath9k_ant_div_comb_lna_conf first_quick_scan_conf;
enum ath9k_ant_div_comb_lna_conf second_quick_scan_conf;
bool first_ratio;
bool second_ratio;
unsigned long scan_start_time;
+
+ /*
+ * Card-specific config values.
+ */
+ int low_rssi_thresh;
+ int fast_div_bias;
};
void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
-void ath_ant_comb_update(struct ath_softc *sc);
/********************/
/* 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_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
/*
* Default cache line size, in bytes.
#endif
struct ath_descdma txsdma;
+ struct ieee80211_vif *csa_vif;
struct ath_ant_comb ant_comb;
u8 ant_tx, ant_rx;
(unsigned long long)tsfadjust, avp->av_bslot);
}
+bool ath9k_csa_is_finished(struct ath_softc *sc)
+{
+ struct ieee80211_vif *vif;
+
+ vif = sc->csa_vif;
+ if (!vif || !vif->csa_active)
+ return false;
+
+ if (!ieee80211_csa_is_complete(vif))
+ return false;
+
+ ieee80211_csa_finish(vif);
+
+ sc->csa_vif = NULL;
+ return true;
+}
+
void ath9k_beacon_tasklet(unsigned long data)
{
struct ath_softc *sc = (struct ath_softc *)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];
}
EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
-static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
{
u32 chanmode = 0;
- switch (chan->band) {
+ switch (chandef->chan->band) {
case IEEE80211_BAND_2GHZ:
- switch (channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
chanmode = CHANNEL_G_HT20;
break;
- case NL80211_CHAN_HT40PLUS:
- chanmode = CHANNEL_G_HT40PLUS;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ chanmode = CHANNEL_G_HT40PLUS;
+ else
+ chanmode = CHANNEL_G_HT40MINUS;
break;
- case NL80211_CHAN_HT40MINUS:
- chanmode = CHANNEL_G_HT40MINUS;
+ default:
break;
}
break;
case IEEE80211_BAND_5GHZ:
- switch (channel_type) {
- case NL80211_CHAN_NO_HT:
- case NL80211_CHAN_HT20:
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ case NL80211_CHAN_WIDTH_20:
chanmode = CHANNEL_A_HT20;
break;
- case NL80211_CHAN_HT40PLUS:
- chanmode = CHANNEL_A_HT40PLUS;
+ case NL80211_CHAN_WIDTH_40:
+ if (chandef->center_freq1 > chandef->chan->center_freq)
+ chanmode = CHANNEL_A_HT40PLUS;
+ else
+ chanmode = CHANNEL_A_HT40MINUS;
break;
- case NL80211_CHAN_HT40MINUS:
- chanmode = CHANNEL_A_HT40MINUS;
+ default:
break;
}
break;
* Update internal channel flags.
*/
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type)
+ struct cfg80211_chan_def *chandef)
{
- ichan->channel = chan->center_freq;
- ichan->chan = chan;
+ ichan->channel = chandef->chan->center_freq;
+ ichan->chan = chandef->chan;
- if (chan->band == IEEE80211_BAND_2GHZ) {
+ if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
ichan->chanmode = CHANNEL_G;
ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
} else {
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
}
- if (channel_type != NL80211_CHAN_NO_HT)
- ichan->chanmode = ath9k_get_extchanmode(chan, channel_type);
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ ichan->channelFlags |= CHANNEL_QUARTER;
+ break;
+ case NL80211_CHAN_WIDTH_10:
+ ichan->channelFlags |= CHANNEL_HALF;
+ break;
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ break;
+ case NL80211_CHAN_WIDTH_20:
+ case NL80211_CHAN_WIDTH_40:
+ ichan->chanmode = ath9k_get_extchanmode(chandef);
+ break;
+ default:
+ WARN_ON(1);
+ }
}
EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
chan_idx = curchan->hw_value;
channel = &ah->channels[chan_idx];
- ath9k_cmn_update_ichannel(channel, curchan,
- cfg80211_get_chandef_type(&hw->conf.chandef));
+ ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
return channel;
}
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
- struct ieee80211_channel *chan,
- enum nl80211_channel_type channel_type);
+ struct cfg80211_chan_def *chandef);
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
struct ath_hw *ah);
int ath9k_cmn_count_streams(unsigned int chainmask, int max);
#define DMA_BUF_LEN 1024
-static ssize_t read_file_tx_chainmask(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;
- char buf[32];
- unsigned int len;
-
- len = sprintf(buf, "0x%08x\n", ah->txchainmask);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_tx_chainmask(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ath_softc *sc = file->private_data;
- struct ath_hw *ah = sc->sc_ah;
- unsigned long mask;
- 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, &mask))
- return -EINVAL;
-
- ah->txchainmask = mask;
- ah->caps.tx_chainmask = mask;
- return count;
-}
-
-static const struct file_operations fops_tx_chainmask = {
- .read = read_file_tx_chainmask,
- .write = write_file_tx_chainmask,
- .open = simple_open,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
-};
-
-
-static ssize_t read_file_rx_chainmask(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;
- char buf[32];
- unsigned int len;
-
- len = sprintf(buf, "0x%08x\n", ah->rxchainmask);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_rx_chainmask(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct ath_softc *sc = file->private_data;
- struct ath_hw *ah = sc->sc_ah;
- unsigned long mask;
- 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, &mask))
- return -EINVAL;
-
- ah->rxchainmask = mask;
- ah->caps.rx_chainmask = mask;
- return count;
-}
-
-static const struct file_operations fops_rx_chainmask = {
- .read = read_file_rx_chainmask,
- .write = write_file_rx_chainmask,
- .open = simple_open,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
-};
static ssize_t read_file_ani(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
.llseek = default_llseek,
};
-static ssize_t read_file_ant_diversity(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static ssize_t read_file_bt_ant_diversity(struct file *file,
+ 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];
unsigned int len;
- len = sprintf(buf, "%d\n", common->antenna_diversity);
+ len = sprintf(buf, "%d\n", common->bt_ant_diversity);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
-static ssize_t write_file_ant_diversity(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
+static ssize_t write_file_bt_ant_diversity(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);
- unsigned long antenna_diversity;
+ struct ath9k_hw_capabilities *pCap = &sc->sc_ah->caps;
+ unsigned long bt_ant_diversity;
char buf[32];
ssize_t len;
if (copy_from_user(buf, user_buf, len))
return -EFAULT;
- if (!AR_SREV_9565(sc->sc_ah))
+ if (!(pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV))
goto exit;
buf[len] = '\0';
- if (kstrtoul(buf, 0, &antenna_diversity))
+ if (kstrtoul(buf, 0, &bt_ant_diversity))
return -EINVAL;
- common->antenna_diversity = !!antenna_diversity;
+ common->bt_ant_diversity = !!bt_ant_diversity;
ath9k_ps_wakeup(sc);
- ath_ant_comb_update(sc);
- ath_dbg(common, CONFIG, "Antenna diversity: %d\n",
- common->antenna_diversity);
+ ath9k_hw_set_bt_ant_diversity(sc->sc_ah, common->bt_ant_diversity);
+ ath_dbg(common, CONFIG, "Enable WLAN/BT RX Antenna diversity: %d\n",
+ common->bt_ant_diversity);
ath9k_ps_restore(sc);
exit:
return count;
}
-static const struct file_operations fops_ant_diversity = {
- .read = read_file_ant_diversity,
- .write = write_file_ant_diversity,
+static const struct file_operations fops_bt_ant_diversity = {
+ .read = read_file_bt_ant_diversity,
+ .write = write_file_bt_ant_diversity,
+ .open = simple_open,
+ .owner = THIS_MODULE,
+ .llseek = default_llseek,
+};
+
+#endif
+
+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)
+{
+ 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];
+
+ as_main->lna_attempt_cnt[div_ant_conf->main_lna_conf]++;
+ as_alt->lna_attempt_cnt[div_ant_conf->alt_lna_conf]++;
+
+ as_main->rssi_avg = main_rssi_avg;
+ as_alt->rssi_avg = alt_rssi_avg;
+}
+
+static ssize_t read_file_antenna_diversity(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;
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ 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;
+ ssize_t retval = 0;
+ char *buf;
+ 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");
+ 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);
+ 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]);
+
+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_antenna_diversity = {
+ .read = read_file_antenna_diversity,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
return retval;
}
+static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq,
+ char *buf, ssize_t size)
+{
+ ssize_t len = 0;
+
+ 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);
+
+ ath_txq_unlock(sc, txq);
+ return len;
+}
+
static ssize_t read_file_queues(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
txq = sc->tx.txq_map[i];
- len += snprintf(buf + len, size - len, "(%s): ", qname[i]);
-
- 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);
-
- ath_txq_unlock(sc, txq);
+ len += snprintf(buf + len, size - len, "(%s): ", qname[i]);
+ len += print_queue(sc, txq, buf + len, size - len);
}
+ len += snprintf(buf + len, size - len, "(CAB): ");
+ len += print_queue(sc, sc->beacon.cabq, buf + len, size - len);
+
if (len > size)
len = size;
struct dentry *dir)
{
struct ath_node *an = (struct ath_node *)sta->drv_priv;
- an->node_stat = debugfs_create_file("node_stat", S_IRUGO,
- dir, an, &fops_node_stat);
-}
-
-void ath9k_sta_remove_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_remove(an->node_stat);
+ debugfs_create_file("node_stat", S_IRUGO, dir, an, &fops_node_stat);
}
/* Ethtool support for get-stats */
&fops_reset);
debugfs_create_file("recv", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_recv);
- debugfs_create_file("rx_chainmask", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc, &fops_rx_chainmask);
- debugfs_create_file("tx_chainmask", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
+ debugfs_create_u8("rx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
+ &ah->rxchainmask);
+ debugfs_create_u8("tx_chainmask", S_IRUSR, sc->debug.debugfs_phy,
+ &ah->txchainmask);
debugfs_create_file("ani", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_ani);
debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, &sc->sc_ah->gpio_val);
- debugfs_create_file("diversity", S_IRUSR | S_IWUSR,
- sc->debug.debugfs_phy, sc, &fops_ant_diversity);
+ debugfs_create_file("antenna_diversity", S_IRUSR,
+ sc->debug.debugfs_phy, sc, &fops_antenna_diversity);
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+ debugfs_create_file("bt_ant_diversity", S_IRUSR | S_IWUSR,
+ sc->debug.debugfs_phy, sc, &fops_bt_ant_diversity);
debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc,
&fops_btcoex);
#endif
#ifdef CONFIG_ATH9K_DEBUGFS
#define TX_STAT_INC(q, c) sc->debug.stats.txstats[q].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 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)
#endif
enum ath_reset_type {
u32 rx_spectral;
};
+#define ANT_MAIN 0
+#define ANT_ALT 1
+
+struct ath_antenna_stats {
+ u32 recv_cnt;
+ u32 rssi_avg;
+ u32 lna_recv_cnt[4];
+ u32 lna_attempt_cnt[4];
+};
+
struct ath_stats {
struct ath_interrupt_stats istats;
struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES];
struct ath_rx_stats rxstats;
struct ath_dfs_stats dfs_stats;
+ struct ath_antenna_stats ant_stats[2];
u32 reset[__RESET_TYPE_MAX];
};
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct dentry *dir);
-void ath9k_sta_remove_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
#define RX_STAT_INC(c) /* NOP */
static inline void ath9k_deinit_debug(struct ath_softc *sc)
{
}
-
static inline void ath_debug_stat_interrupt(struct ath_softc *sc,
enum ath9k_int status)
{
}
-
static inline void ath_debug_stat_tx(struct ath_softc *sc,
struct ath_buf *bf,
struct ath_tx_status *ts,
unsigned int flags)
{
}
-
static inline void ath_debug_stat_rx(struct ath_softc *sc,
struct ath_rx_status *rs)
{
+}
+static inline 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)
+{
+
}
#endif /* CONFIG_ATH9K_DEBUGFS */
static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
struct ath9k_channel *chan)
{
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
struct modal_eep_4k_header *pModal;
struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
struct base_eep_header_4k *pBase = &eep->baseEepHeader;
REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
regVal = REG_READ(ah, AR_PHY_CCK_DETECT);
+
+ if (pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
+ /*
+ * If diversity combining is enabled,
+ * set MAIN to LNA1 and ALT to LNA2 initially.
+ */
+ regVal = REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
+ regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF |
+ AR_PHY_9285_ANT_DIV_ALT_LNACONF));
+
+ regVal |= (ATH_ANT_DIV_COMB_LNA1 <<
+ AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
+ regVal |= (ATH_ANT_DIV_COMB_LNA2 <<
+ AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
+ regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
+ regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
+ REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
+ }
}
if (pModal->version >= 2) {
cmd->skb = skb;
cmd->hif_dev = hif_dev;
- usb_fill_bulk_urb(urb, hif_dev->udev,
- usb_sndbulkpipe(hif_dev->udev, USB_REG_OUT_PIPE),
+ usb_fill_int_urb(urb, hif_dev->udev,
+ usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE),
skb->data, skb->len,
- hif_usb_regout_cb, cmd);
+ hif_usb_regout_cb, cmd, 1);
usb_anchor_urb(urb, &hif_dev->regout_submitted);
ret = usb_submit_urb(urb, GFP_KERNEL);
return;
}
- usb_fill_bulk_urb(urb, hif_dev->udev,
- usb_rcvbulkpipe(hif_dev->udev,
+ usb_fill_int_urb(urb, hif_dev->udev,
+ usb_rcvintpipe(hif_dev->udev,
USB_REG_IN_PIPE),
nskb->data, MAX_REG_IN_BUF_SIZE,
- ath9k_hif_usb_reg_in_cb, nskb);
+ ath9k_hif_usb_reg_in_cb, nskb, 1);
}
resubmit:
goto err_skb;
}
- usb_fill_bulk_urb(urb, hif_dev->udev,
- usb_rcvbulkpipe(hif_dev->udev,
+ usb_fill_int_urb(urb, hif_dev->udev,
+ usb_rcvintpipe(hif_dev->udev,
USB_REG_IN_PIPE),
skb->data, MAX_REG_IN_BUF_SIZE,
- ath9k_hif_usb_reg_in_cb, skb);
+ ath9k_hif_usb_reg_in_cb, skb, 1);
/* Anchor URB */
usb_anchor_urb(urb, &hif_dev->reg_in_submitted);
static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev)
{
- struct usb_host_interface *alt = &hif_dev->interface->altsetting[0];
- struct usb_endpoint_descriptor *endp;
- int ret, idx;
+ int ret;
ret = ath9k_hif_usb_download_fw(hif_dev);
if (ret) {
return ret;
}
- /* On downloading the firmware to the target, the USB descriptor of EP4
- * is 'patched' to change the type of the endpoint to Bulk. This will
- * bring down CPU usage during the scan period.
- */
- for (idx = 0; idx < alt->desc.bNumEndpoints; idx++) {
- endp = &alt->endpoint[idx].desc;
- if ((endp->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT) {
- endp->bmAttributes &= ~USB_ENDPOINT_XFERTYPE_MASK;
- endp->bmAttributes |= USB_ENDPOINT_XFER_BULK;
- endp->bInterval = 0;
- }
- }
-
/* Alloc URBs */
ret = ath9k_hif_usb_alloc_urbs(hif_dev);
if (ret) {
if (!buf)
return;
- ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, USB_REG_OUT_PIPE),
+ ret = usb_interrupt_msg(udev, usb_sndintpipe(udev, USB_REG_OUT_PIPE),
buf, 4, NULL, HZ);
if (ret)
dev_err(&udev->dev, "ath9k_htc: USB reboot failed\n");
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || chip_reset) {
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
- enum nl80211_channel_type channel_type =
- cfg80211_get_chandef_type(&hw->conf.chandef);
int pos = curchan->hw_value;
ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
curchan->center_freq);
ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
- hw->conf.chandef.chan,
- channel_type);
+ &hw->conf.chandef);
if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
ath_err(common, "Unable to set channel\n");
ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf);
}
-static inline void ath9k_hw_antctrl_shared_chain_lnadiv(struct ath_hw *ah,
- bool enable)
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+
+static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
{
- if (ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv)
- ath9k_hw_ops(ah)->antctrl_shared_chain_lnadiv(ah, enable);
+ if (ath9k_hw_ops(ah)->set_bt_ant_diversity)
+ ath9k_hw_ops(ah)->set_bt_ant_diversity(ah, enable);
}
+#endif
+
/* Private hardware call ops */
/* PHY ops */
ah->config.ack_6mb = 0x0;
ah->config.cwm_ignore_extcca = 0;
ah->config.pcie_clock_req = 0;
- ah->config.pcie_waen = 0;
ah->config.analog_shiftreg = 1;
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
tx_lat += 11;
- sifstime *= 2;
+ sifstime = 32;
ack_offset = 16;
slottime = 13;
} else if (IS_CHAN_QUARTER_RATE(chan)) {
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
tx_lat += 22;
- sifstime *= 4;
+ sifstime = 64;
ack_offset = 32;
slottime = 21;
} else {
ctstimeout += 48 - sifstime - ah->slottime;
}
-
ath9k_hw_set_sifs_time(ah, sifstime);
ath9k_hw_setslottime(ah, slottime);
ath9k_hw_set_ack_timeout(ah, acktimeout);
struct ath9k_channel *chan)
{
struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ bool band_switch = false, mode_diff = false;
+ u8 ini_reloaded = 0;
u32 qnum;
int r;
- bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
- bool band_switch, mode_diff;
- u8 ini_reloaded;
- band_switch = (chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ)) !=
- (ah->curchan->channelFlags & (CHANNEL_2GHZ |
- CHANNEL_5GHZ));
- mode_diff = (chan->chanmode != ah->curchan->chanmode);
+ 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);
+ }
for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
if (ath9k_hw_numtxpending(ah, qnum)) {
return false;
}
- if (edma && (band_switch || mode_diff)) {
+ if (band_switch || mode_diff) {
ath9k_hw_mark_phy_inactive(ah);
udelay(5);
- ath9k_hw_init_pll(ah, NULL);
+ if (band_switch)
+ ath9k_hw_init_pll(ah, chan);
if (ath9k_hw_fast_chan_change(ah, chan, &ini_reloaded)) {
ath_err(common, "Failed to do fast channel change\n");
}
ath9k_hw_set_clockrate(ah);
ath9k_hw_apply_txpower(ah, chan, false);
- ath9k_hw_rfbus_done(ah);
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
ath9k_hw_set_delta_slope(ah, chan);
ath9k_hw_spur_mitigate_freq(ah, chan);
- if (edma && (band_switch || mode_diff)) {
- ah->ah_flags |= AH_FASTCC;
- if (band_switch || ini_reloaded)
- ah->eep_ops->set_board_values(ah, chan);
+ if (band_switch || ini_reloaded)
+ ah->eep_ops->set_board_values(ah, chan);
- ath9k_hw_init_bb(ah, chan);
+ ath9k_hw_init_bb(ah, chan);
+ ath9k_hw_rfbus_done(ah);
- if (band_switch || ini_reloaded)
- ath9k_hw_init_cal(ah, chan);
+ if (band_switch || ini_reloaded) {
+ ah->ah_flags |= AH_FASTCC;
+ ath9k_hw_init_cal(ah, chan);
ah->ah_flags &= ~AH_FASTCC;
}
/*
* Fast channel change:
* (Change synthesizer based on channel freq without resetting chip)
- *
- * Don't do FCC when
- * - Flag is not set
- * - Chip is just coming out of full sleep
- * - Channel to be set is same as current channel
- * - Channel flags are different, (eg.,moving from 2GHz to 5GHz channel)
*/
static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath_common *common = ath9k_hw_common(ah);
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
int ret;
if (AR_SREV_9280(ah) && common->bus_ops->ath_bus_type == ATH_PCI)
(CHANNEL_HALF | CHANNEL_QUARTER))
goto fail;
- if ((chan->channelFlags & CHANNEL_ALL) !=
- (ah->curchan->channelFlags & CHANNEL_ALL))
- 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 (!(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 (!ath9k_hw_check_alive(ah))
goto fail;
ath9k_hw_apply_gpio_override(ah);
- if (AR_SREV_9565(ah) && ah->shared_chain_lnadiv)
+ if (AR_SREV_9565(ah) && common->bt_ant_diversity)
REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON);
return 0;
else
pCap->rts_aggr_limit = (8 * 1024);
-#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
+#ifdef CONFIG_ATH9K_RFKILL
ah->rfsilent = ah->eep_ops->get_eeprom(ah, EEP_RF_SILENT);
if (ah->rfsilent & EEP_RFSILENT_ENABLED) {
ah->rfkill_gpio =
if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah))
pCap->hw_caps |= ATH9K_HW_CAP_SGI_20;
- if (AR_SREV_9285(ah))
+ if (AR_SREV_9285(ah)) {
if (ah->eep_ops->get_eeprom(ah, EEP_MODAL_VER) >= 3) {
ant_div_ctl1 =
ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
- if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1))
+ if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) {
pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+ ath_info(common, "Enable LNA combining\n");
+ }
}
+ }
+
if (AR_SREV_9300_20_OR_LATER(ah)) {
if (ah->eep_ops->get_eeprom(ah, EEP_CHAIN_MASK_REDUCE))
pCap->hw_caps |= ATH9K_HW_CAP_APM;
}
-
if (AR_SREV_9330(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) {
ant_div_ctl1 = ah->eep_ops->get_eeprom(ah, EEP_ANT_DIV_CTL1);
- /*
- * enable the diversity-combining algorithm only when
- * both enable_lna_div and enable_fast_div are set
- * Table for Diversity
- * ant_div_alt_lnaconf bit 0-1
- * ant_div_main_lnaconf bit 2-3
- * ant_div_alt_gaintb bit 4
- * ant_div_main_gaintb bit 5
- * enable_ant_div_lnadiv bit 6
- * enable_ant_fast_div bit 7
- */
- if ((ant_div_ctl1 >> 0x6) == 0x3)
+ if ((ant_div_ctl1 >> 0x6) == 0x3) {
pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB;
+ ath_info(common, "Enable LNA combining\n");
+ }
}
if (ath9k_hw_dfs_tested(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;
}
ATH9K_HW_CAP_DFS = BIT(16),
ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(17),
ATH9K_HW_CAP_PAPRD = BIT(18),
+ ATH9K_HW_CAP_FCC_BAND_SWITCH = BIT(19),
+ ATH9K_HW_CAP_BT_ANT_DIV = BIT(20),
};
/*
u16 ani_poll_interval; /* ANI poll interval in ms */
/* Platform specific config */
+ u32 aspm_l1_fix;
u32 xlna_gpio;
+ u32 ant_ctrl_comm2g_switch_enable;
bool xatten_margin_cfg;
+ bool alt_mingainidx;
};
enum ath9k_int {
struct ath_hw_antcomb_conf *antconf);
void (*antdiv_comb_conf_set)(struct ath_hw *ah,
struct ath_hw_antcomb_conf *antconf);
- void (*antctrl_shared_chain_lnadiv)(struct ath_hw *hw, bool enable);
void (*spectral_scan_config)(struct ath_hw *ah,
struct ath_spec_scan *param);
void (*spectral_scan_trigger)(struct ath_hw *ah);
void (*spectral_scan_wait)(struct ath_hw *ah);
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+ void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable);
+#endif
};
struct ath_nf_limits {
bool aspm_enabled;
bool is_monitoring;
bool need_an_top2_fixup;
- bool shared_chain_lnadiv;
u16 tx_trig_level;
u32 nf_regs[6];
module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444);
MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence");
-static int ath9k_enable_diversity;
-module_param_named(enable_diversity, ath9k_enable_diversity, int, 0444);
-MODULE_PARM_DESC(enable_diversity, "Enable Antenna diversity for AR9565");
+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 */
RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE),
RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE),
- 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),
+ 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)),
};
#ifdef CONFIG_MAC80211_LEDS
static void ath9k_init_platform(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
struct ath_common *common = ath9k_hw_common(ah);
if (common->bus_ops->ath_bus_type != ATH_PCI)
ATH9K_PCI_CUS230)) {
ah->config.xlna_gpio = 9;
ah->config.xatten_margin_cfg = true;
+ ah->config.alt_mingainidx = true;
+ ah->config.ant_ctrl_comm2g_switch_enable = 0x000BBB88;
+ sc->ant_comb.low_rssi_thresh = 20;
+ sc->ant_comb.fast_div_bias = 3;
ath_info(common, "Set parameters for %s\n",
(sc->driver_data & ATH9K_PCI_CUS198) ?
"CUS198" : "CUS230");
- } else if (sc->driver_data & ATH9K_PCI_CUS217) {
+ }
+
+ if (sc->driver_data & ATH9K_PCI_CUS217)
ath_info(common, "CUS217 card detected\n");
+
+ 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");
+ }
+
+ if (sc->driver_data & ATH9K_PCI_D3_L1_WAR) {
+ ah->config.pcie_waen = 0x0040473b;
+ ath_info(common, "Enable WAR for ASPM D3/L1\n");
}
}
{
struct ath9k_platform_data *pdata = sc->dev->platform_data;
struct ath_hw *ah = NULL;
+ struct ath9k_hw_capabilities *pCap;
struct ath_common *common;
int ret = 0, i;
int csz = 0;
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);
ath9k_init_platform(sc);
/*
- * Enable Antenna diversity only when BTCOEX is disabled
- * and the user manually requests the feature.
+ * Enable WLAN/BT RX Antenna diversity only when:
+ *
+ * - BTCOEX is disabled.
+ * - the user manually requests the feature.
+ * - the HW cap is set using the platform data.
*/
- if (!common->btcoex_enabled && ath9k_enable_diversity)
- common->antenna_diversity = 1;
+ if (!common->btcoex_enabled && ath9k_bt_ant_diversity &&
+ (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV))
+ common->bt_ant_diversity = 1;
spin_lock_init(&common->cc_lock);
struct ieee80211_supported_band *sband;
struct ieee80211_channel *chan;
struct ath_hw *ah = sc->sc_ah;
+ struct cfg80211_chan_def chandef;
int i;
sband = &sc->sbands[band];
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
ah->curchan = &ah->channels[chan->hw_value];
- ath9k_cmn_update_ichannel(ah->curchan, chan, NL80211_CHAN_HT20);
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
+ ath9k_cmn_update_ichannel(ah->curchan, &chandef);
ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
}
}
hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
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) &&
txq->axq_tx_inprogress = true;
}
}
- ath_txq_unlock_complete(sc, txq);
+ ath_txq_unlock(sc, txq);
}
if (needreset) {
rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate);
rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0;
+ rs->rs_firstaggr = (ads.ds_rxstatus8 & AR_RxFirstAggr) ? 1 : 0;
rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0;
- rs->rs_moreaggr =
- (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
+ rs->rs_moreaggr = (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0;
rs->rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna);
/* directly mapped flags for ieee80211_rx_status */
int8_t rs_rssi_ext1;
int8_t rs_rssi_ext2;
u8 rs_isaggr;
+ u8 rs_firstaggr;
u8 rs_moreaggr;
u8 rs_num_delims;
u8 rs_flags;
#define AR_RxAggr 0x00020000
#define AR_PostDelimCRCErr 0x00040000
#define AR_RxStatusRsvd71 0x3ff80000
+#define AR_RxFirstAggr 0x20000000
#define AR_DecryptBusyErr 0x40000000
#define AR_KeyMiss 0x80000000
ath_restart_work(sc);
}
- if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx != 3)
- ath_ant_comb_update(sc);
-
ieee80211_wake_queues(sc->hw);
return true;
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_node *an = &avp->mcast_node;
mutex_lock(&sc->mutex);
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
+ an->sc = sc;
+ an->sta = NULL;
+ an->vif = vif;
+ an->no_ps_filter = true;
+ ath_tx_node_init(sc, an);
+
mutex_unlock(&sc->mutex);
return 0;
}
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_vif *avp = (void *)vif->drv_priv;
ath_dbg(common, CONFIG, "Detach Interface\n");
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);
+ ath_tx_node_cleanup(sc, &avp->mcast_node);
+
mutex_unlock(&sc->mutex);
}
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
- enum nl80211_channel_type channel_type =
- cfg80211_get_chandef_type(&conf->chandef);
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 type: %d\n",
- curchan->center_freq, channel_type);
+ 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);
spin_unlock_irqrestore(&common->cc_lock, flags);
ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
- curchan, channel_type);
+ &conf->chandef);
/*
* If the operating channel changes, change the survey in-use flags
struct ath_softc *sc = hw->priv;
struct ath_node *an = (struct ath_node *) sta->drv_priv;
- if (!sta->ht_cap.ht_supported)
- return;
-
switch (cmd) {
case STA_NOTIFY_SLEEP:
an->sleeping = true;
{
struct ath_hw *ah = sc->sc_ah;
struct ath9k_wow_pattern *wow_pattern = NULL;
- struct cfg80211_wowlan_trig_pkt_pattern *patterns = wowlan->patterns;
+ struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
int mask_len;
s8 i = 0;
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 ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS)
.sta_add_debugfs = ath9k_sta_add_debugfs,
- .sta_remove_debugfs = ath9k_sta_remove_debugfs,
#endif
.sw_scan_start = ath9k_sw_scan_start,
.sw_scan_complete = ath9k_sw_scan_complete,
+ .channel_switch_beacon = ath9k_channel_switch_beacon,
};
{ PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */
{ PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */
+
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x1C71),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ PCI_VENDOR_ID_FOXCONN,
+ 0xE01F),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ 0x11AD, /* LITEON */
+ 0x6632),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ 0x11AD, /* LITEON */
+ 0x6642),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ PCI_VENDOR_ID_QMI,
+ 0x0306),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ 0x185F, /* WNC */
+ 0x309D),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ 0x10CF, /* Fujitsu */
+ 0x147C),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ 0x10CF, /* Fujitsu */
+ 0x147D),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002A,
+ 0x10CF, /* Fujitsu */
+ 0x1536),
+ .driver_data = ATH9K_PCI_D3_L1_WAR },
+
+ /* AR9285 card for Asus */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x002B,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x2C37),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+
{ PCI_VDEVICE(ATHEROS, 0x002B) }, /* PCI-E */
{ PCI_VDEVICE(ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */
{ PCI_VDEVICE(ATHEROS, 0x002D) }, /* PCI */
0x0032,
PCI_VENDOR_ID_AZWAVE,
0x2086),
- .driver_data = ATH9K_PCI_CUS198 },
+ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0032,
PCI_VENDOR_ID_AZWAVE,
0x1237),
- .driver_data = ATH9K_PCI_CUS198 },
+ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0032,
PCI_VENDOR_ID_AZWAVE,
0x2126),
- .driver_data = ATH9K_PCI_CUS198 },
+ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_AZWAVE,
+ 0x126A),
+ .driver_data = ATH9K_PCI_CUS198 | ATH9K_PCI_BT_ANT_DIV },
/* PCI-E CUS230 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0032,
PCI_VENDOR_ID_AZWAVE,
0x2152),
- .driver_data = ATH9K_PCI_CUS230 },
+ .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
0x0032,
PCI_VENDOR_ID_FOXCONN,
0xE075),
- .driver_data = ATH9K_PCI_CUS230 },
+ .driver_data = ATH9K_PCI_CUS230 | ATH9K_PCI_BT_ANT_DIV },
+
+ /* WB225 */
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_ATHEROS,
+ 0x3119),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_ATHEROS,
+ 0x3122),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ 0x185F, /* WNC */
+ 0x3119),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ 0x185F, /* WNC */
+ 0x3027),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0x4105),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0x4106),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0x410D),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0x410E),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0x410F),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0xC706),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0xC680),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_SAMSUNG,
+ 0xC708),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_LENOVO,
+ 0x3218),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
+ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+ 0x0032,
+ PCI_VENDOR_ID_LENOVO,
+ 0x3219),
+ .driver_data = ATH9K_PCI_BT_ANT_DIV },
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
{ PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
return;
}
+ /*
+ * 0x70c - Ack Frequency Register.
+ *
+ * Bits 27:29 - DEFAULT_L1_ENTRANCE_LATENCY.
+ *
+ * 000 : 1 us
+ * 001 : 2 us
+ * 010 : 4 us
+ * 011 : 8 us
+ * 100 : 16 us
+ * 101 : 32 us
+ * 110/111 : 64 us
+ */
+ if (AR_SREV_9462(ah))
+ pci_read_config_dword(pdev, 0x70c, &ah->config.aspm_l1_fix);
+
pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm);
if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) {
ah->aspm_enabled = true;
#define AR_PHY_PLL_CONTROL 0x16180
#define AR_PHY_PLL_MODE 0x16184
+enum ath9k_ant_div_comb_lna_conf {
+ ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2,
+ ATH_ANT_DIV_COMB_LNA2,
+ ATH_ANT_DIV_COMB_LNA1,
+ ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2,
+};
+
#endif
}
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++;
}
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)
{
ath_rc_init(sc, priv_sta);
ath_dbg(ath9k_hw_common(sc->sc_ah), CONFIG,
- "Operating HT Bandwidth changed to: %d\n",
- cfg80211_get_chandef_type(&sc->hw->conf.chandef));
+ "Operating Bandwidth changed to: %d\n",
+ sc->hw->conf.chandef.width);
}
}
struct ath_desc *ds;
struct sk_buff *skb;
- ATH_RXBUF_RESET(bf);
-
ds = bf->bf_desc;
ds->ds_link = 0; /* link to null */
ds->ds_data = bf->bf_buf_addr;
sc->rx.rxlink = &ds->ds_link;
}
+static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
+{
+ if (sc->rx.buf_hold)
+ ath_rx_buf_link(sc, sc->rx.buf_hold);
+
+ sc->rx.buf_hold = bf;
+}
+
static void ath_setdefantenna(struct ath_softc *sc, u32 antenna)
{
/* XXX block beacon interrupts */
skb = bf->bf_mpdu;
- ATH_RXBUF_RESET(bf);
memset(skb->data, 0, ah->caps.rx_status_len);
dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
ah->caps.rx_status_len, DMA_TO_DEVICE);
static void ath_rx_edma_init_queue(struct ath_rx_edma *rx_edma, int size)
{
- skb_queue_head_init(&rx_edma->rx_fifo);
+ __skb_queue_head_init(&rx_edma->rx_fifo);
rx_edma->rx_fifo_hwsize = size;
}
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
+ 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);
}
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
+ if (bf == sc->rx.buf_hold)
+ return NULL;
+
ds = bf->bf_desc;
/*
bool is_mc, is_valid_tkip, strip_mic, mic_error;
struct ath_hw *ah = common->ah;
__le16 fc;
- u8 rx_status_len = ah->caps.rx_status_len;
fc = hdr->frame_control;
!test_bit(rx_stats->rs_keyix, common->ccmp_keymap))
rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS;
- if (!rx_stats->rs_datalen) {
- RX_STAT_INC(rx_len_err);
- return false;
- }
-
- /*
- * 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 - rx_status_len)) {
- RX_STAT_INC(rx_len_err);
- return false;
- }
-
- /* Only use error bits from the last fragment */
- if (rx_stats->rs_more)
- return true;
-
mic_error = is_valid_tkip && !ieee80211_is_ctl(fc) &&
!ieee80211_has_morefrags(fc) &&
!(le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) &&
rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
mic_error = false;
}
- if (rx_stats->rs_status & ATH9K_RXERR_PHY)
- return false;
if ((rx_stats->rs_status & ATH9K_RXERR_DECRYPT) ||
(!is_mc && (rx_stats->rs_status & ATH9K_RXERR_KEYMISS))) {
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;
static void ath9k_process_rssi(struct ath_common *common,
struct ieee80211_hw *hw,
- struct ieee80211_hdr *hdr,
- struct ath_rx_status *rx_stats)
+ 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;
- if (!rx_stats->is_mybeacon ||
- ((ah->opmode != NL80211_IFTYPE_STATION) &&
- (ah->opmode != NL80211_IFTYPE_ADHOC)))
+ /*
+ * RSSI is not available for subframes in an A-MPDU.
+ */
+ if (rx_stats->rs_moreaggr) {
+ rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
return;
-
- if (rx_stats->rs_rssi != ATH9K_RSSI_BAD && !rx_stats->rs_moreaggr)
- 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;
-
- /* Update Beacon RSSI, this is used by ANI. */
- ah->stats.avgbrssi = rssi;
-}
-
-/*
- * 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
- * decryption key or real decryption error. This let us keep statistics there.
- */
-static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
- struct ieee80211_hdr *hdr,
- struct ath_rx_status *rx_stats,
- struct ieee80211_rx_status *rx_status,
- bool *decrypt_error)
-{
- struct ieee80211_hw *hw = sc->hw;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- bool discard_current = sc->rx.discard_next;
-
- sc->rx.discard_next = rx_stats->rs_more;
- if (discard_current)
- 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.
+ * Check if the RSSI for the last subframe in an A-MPDU
+ * or an unaggregated frame is valid.
*/
- if (!ath9k_rx_accept(common, hdr, rx_status, rx_stats, decrypt_error))
- return -EINVAL;
-
- /* Only use status info from the last fragment */
- if (rx_stats->rs_more)
- return 0;
+ if (rx_stats->rs_rssi == ATH9K_RSSI_BAD) {
+ rxs->flag |= RX_FLAG_NO_SIGNAL_VAL;
+ return;
+ }
- if (ath9k_process_rate(common, hw, rx_stats, rx_status))
- return -EINVAL;
+ /*
+ * 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;
- ath9k_process_rssi(common, hw, hdr, rx_stats);
+ if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
+ rssi = ATH_EP_RND(last_rssi, ATH_RSSI_EP_MULTIPLIER);
+ if (rssi < 0)
+ rssi = 0;
- rx_status->band = hw->conf.chandef.chan->band;
- rx_status->freq = hw->conf.chandef.chan->center_freq;
- rx_status->signal = ah->noise + rx_stats->rs_rssi;
- rx_status->antenna = rx_stats->rs_antenna;
- rx_status->flag |= RX_FLAG_MACTIME_END;
- if (rx_stats->rs_moreaggr)
- rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL;
+ ah->stats.avgbrssi = rssi;
+ }
- sc->rx.discard_next = false;
- return 0;
+ rxs->signal = ah->noise + rx_stats->rs_rssi;
}
-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)
+static void ath9k_process_tsf(struct ath_rx_status *rs,
+ struct ieee80211_rx_status *rxs,
+ u64 tsf)
{
- 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);
- }
+ u32 tsf_lower = tsf & 0xffffffff;
- keyix = rx_stats->rs_keyix;
+ rxs->mactime = (tsf & ~0xffffffffULL) | rs->rs_tstamp;
+ if (rs->rs_tstamp > tsf_lower &&
+ unlikely(rs->rs_tstamp - tsf_lower > 0x10000000))
+ rxs->mactime -= 0x100000000ULL;
- 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;
+ if (rs->rs_tstamp < tsf_lower &&
+ unlikely(tsf_lower - rs->rs_tstamp > 0x10000000))
+ rxs->mactime += 0x100000000ULL;
}
#ifdef CONFIG_ATH9K_DEBUGFS
#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
+ * decryption key or real decryption error. This let us keep statistics there.
+ */
+static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
+ struct sk_buff *skb,
+ struct ath_rx_status *rx_stats,
+ struct ieee80211_rx_status *rx_status,
+ bool *decrypt_error, u64 tsf)
+{
+ struct ieee80211_hw *hw = sc->hw;
+ struct ath_hw *ah = sc->sc_ah;
+ 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;
+
+ /*
+ * Discard zero-length packets.
+ */
+ if (!rx_stats->rs_datalen) {
+ RX_STAT_INC(rx_len_err);
+ return -EINVAL;
+ }
+
+ /*
+ * 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;
+ }
+
+ /* Only use status info from the last fragment */
+ if (rx_stats->rs_more)
+ return 0;
+
+ /*
+ * Return immediately if the RX descriptor has been marked
+ * as corrupt based on the various error bits.
+ *
+ * This is different from the other corrupt descriptor
+ * condition handled above.
+ */
+ if (rx_stats->rs_status & ATH9K_RXERR_CORRUPT_DESC) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ hdr = (struct ieee80211_hdr *) (skb->data + ah->caps.rx_status_len);
+
+ ath9k_process_tsf(rx_stats, rx_status, tsf);
+ ath_debug_stat_rx(sc, rx_stats);
+
+ /*
+ * Process PHY errors and return so that the packet
+ * can be dropped.
+ */
+ if (rx_stats->rs_status & ATH9K_RXERR_PHY) {
+ ath9k_dfs_process_phyerr(sc, hdr, rx_stats, rx_status->mactime);
+ if (ath_process_fft(sc, hdr, rx_stats, rx_status->mactime))
+ RX_STAT_INC(rx_spectral);
+
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /*
+ * 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;
+ }
+
+ 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 (ath9k_process_rate(common, hw, rx_stats, rx_status)) {
+ ret =-EINVAL;
+ goto exit;
+ }
+
+ ath9k_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->antenna = rx_stats->rs_antenna;
+ rx_status->flag |= RX_FLAG_MACTIME_END;
+
+#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
+ if (ieee80211_is_data_present(hdr->frame_control) &&
+ !ieee80211_is_qos_nullfunc(hdr->frame_control))
+ 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;
+
+ 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;
+}
+
+/*
+ * Run the LNA combining algorithm only in these cases:
+ *
+ * Standalone WLAN cards with both LNA/Antenna diversity
+ * enabled in the EEPROM.
+ *
+ * WLAN+BT cards which are in the supported card list
+ * in ath_pci_id_table and the user has loaded the
+ * driver with "bt_ant_diversity" set to true.
+ */
+static void ath9k_antenna_check(struct ath_softc *sc,
+ struct ath_rx_status *rs)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath9k_hw_capabilities *pCap = &ah->caps;
+ struct ath_common *common = ath9k_hw_common(ah);
+
+ if (!(ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB))
+ return;
+
+ /*
+ * All MPDUs in an aggregate will use the same LNA
+ * as the first MPDU.
+ */
+ if (rs->rs_isaggr && !rs->rs_firstaggr)
+ return;
+
+ /*
+ * Change the default rx antenna if rx diversity
+ * chooses the other antenna 3 times in a row.
+ */
+ if (sc->rx.defant != rs->rs_antenna) {
+ if (++sc->rx.rxotherant >= 3)
+ ath_setdefantenna(sc, rs->rs_antenna);
+ } else {
+ sc->rx.rxotherant = 0;
+ }
+
+ if (pCap->hw_caps & ATH9K_HW_CAP_BT_ANT_DIV) {
+ if (common->bt_ant_diversity)
+ ath_ant_comb_scan(sc, rs);
+ } else {
+ ath_ant_comb_scan(sc, rs);
+ }
+}
+
static void ath9k_apply_ampdu_details(struct ath_softc *sc,
struct ath_rx_status *rs, struct ieee80211_rx_status *rxs)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hw *hw = sc->hw;
- struct ieee80211_hdr *hdr;
int retval;
struct ath_rx_status rs;
enum ath9k_rx_qtype qtype;
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
int dma_type;
- u8 rx_status_len = ah->caps.rx_status_len;
u64 tsf = 0;
- u32 tsf_lower = 0;
unsigned long flags;
dma_addr_t new_buf_addr;
qtype = hp ? ATH9K_RX_QUEUE_HP : ATH9K_RX_QUEUE_LP;
tsf = ath9k_hw_gettsf64(ah);
- tsf_lower = tsf & 0xffffffff;
do {
bool decrypt_error = false;
else
hdr_skb = skb;
- hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len);
rxs = IEEE80211_SKB_RXCB(hdr_skb);
- 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))
- rs.is_mybeacon = true;
- else
- rs.is_mybeacon = false;
- }
- else
- rs.is_mybeacon = false;
-
- if (ieee80211_is_data_present(hdr->frame_control) &&
- !ieee80211_is_qos_nullfunc(hdr->frame_control))
- sc->rx.num_pkts++;
-
- ath_debug_stat_rx(sc, &rs);
-
memset(rxs, 0, sizeof(struct ieee80211_rx_status));
- rxs->mactime = (tsf & ~0xffffffffULL) | rs.rs_tstamp;
- if (rs.rs_tstamp > tsf_lower &&
- unlikely(rs.rs_tstamp - tsf_lower > 0x10000000))
- rxs->mactime -= 0x100000000ULL;
-
- if (rs.rs_tstamp < tsf_lower &&
- unlikely(tsf_lower - rs.rs_tstamp > 0x10000000))
- rxs->mactime += 0x100000000ULL;
-
- if (rs.rs_phyerr == ATH9K_PHYERR_RADAR)
- ath9k_dfs_process_phyerr(sc, hdr, &rs, rxs->mactime);
-
- if (rs.rs_status & ATH9K_RXERR_PHY) {
- if (ath_process_fft(sc, hdr, &rs, rxs->mactime)) {
- RX_STAT_INC(rx_spectral);
- goto requeue_drop_frag;
- }
- }
-
- retval = ath9k_rx_skb_preprocess(sc, hdr, &rs, rxs,
- &decrypt_error);
+ retval = ath9k_rx_skb_preprocess(sc, hdr_skb, &rs, rxs,
+ &decrypt_error, tsf);
if (retval)
goto requeue_drop_frag;
- if (rs.is_mybeacon) {
- sc->hw_busy_count = 0;
- ath_start_rx_poll(sc, 3);
- }
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
sc->rx.frag = skb;
goto requeue;
}
- if (rs.rs_status & ATH9K_RXERR_CORRUPT_DESC)
- goto requeue_drop_frag;
if (sc->rx.frag) {
int space = skb->len - skb_tailroom(hdr_skb);
skb = hdr_skb;
}
-
- if (ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) {
-
- /*
- * change the default rx antenna if rx diversity
- * chooses the other antenna 3 times in a row.
- */
- if (sc->rx.defant != rs.rs_antenna) {
- if (++sc->rx.rxotherant >= 3)
- ath_setdefantenna(sc, rs.rs_antenna);
- } else {
- sc->rx.rxotherant = 0;
- }
-
- }
-
if (rxs->flag & RX_FLAG_MMIC_STRIPPED)
skb_trim(skb, skb->len - 8);
ath_rx_ps(sc, skb, rs.is_mybeacon);
spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
- if ((ah->caps.hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB) && sc->ant_rx == 3)
- ath_ant_comb_scan(sc, &rs);
+ ath9k_antenna_check(sc, &rs);
ath9k_apply_ampdu_details(sc, &rs, rxs);
if (edma) {
ath_rx_edma_buf_link(sc, qtype);
} else {
- ath_rx_buf_link(sc, bf);
+ ath_rx_buf_relink(sc, bf);
ath9k_hw_rxena(ah);
}
} while (1);
#define AR_SREV_9485(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485))
-#define AR_SREV_9485_11(_ah) \
- (AR_SREV_9485(_ah) && \
- ((_ah)->hw_version.macRev == AR_SREV_REVISION_9485_11))
+#define AR_SREV_9485_11_OR_LATER(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485) && \
+ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9485_11))
#define AR_SREV_9485_OR_LATER(_ah) \
(((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9485))
static void ath_send_bar(struct ath_atx_tid *tid, u16 seqno)
{
+ if (!tid->an->sta)
+ return;
+
ieee80211_send_bar(tid->an->vif, tid->an->sta->addr, tid->tidno,
seqno << IEEE80211_SEQ_SEQ_SHIFT);
}
}
}
+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;
+ return ATH_AN_2_TID(an, tidno);
+}
+
+static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
+{
+ return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q);
+}
+
+static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid)
+{
+ struct sk_buff *skb;
+
+ skb = __skb_dequeue(&tid->retry_q);
+ if (!skb)
+ skb = __skb_dequeue(&tid->buf_q);
+
+ return skb;
+}
+
+/*
+ * ath_tx_tid_change_state:
+ * - clears a-mpdu flag of previous session
+ * - force sequence number allocation to fix next BlockAck Window
+ */
+static void
+ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid)
+{
+ struct ath_txq *txq = tid->ac->txq;
+ struct ieee80211_tx_info *tx_info;
+ struct sk_buff *skb, *tskb;
+ struct ath_buf *bf;
+ struct ath_frame_info *fi;
+
+ skb_queue_walk_safe(&tid->buf_q, skb, tskb) {
+ fi = get_frame_info(skb);
+ bf = fi->bf;
+
+ tx_info = IEEE80211_SKB_CB(skb);
+ tx_info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+
+ if (bf)
+ continue;
+
+ bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+ if (!bf) {
+ __skb_unlink(skb, &tid->buf_q);
+ ath_txq_skb_done(sc, txq, skb);
+ ieee80211_free_txskb(sc->hw, skb);
+ continue;
+ }
+ }
+
+}
+
static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
{
struct ath_txq *txq = tid->ac->txq;
memset(&ts, 0, sizeof(ts));
- while ((skb = __skb_dequeue(&tid->buf_q))) {
+ while ((skb = __skb_dequeue(&tid->retry_q))) {
fi = get_frame_info(skb);
bf = fi->bf;
-
if (!bf) {
- bf = ath_tx_setup_buffer(sc, txq, tid, skb);
- if (!bf) {
- ath_txq_skb_done(sc, txq, skb);
- ieee80211_free_txskb(sc->hw, skb);
- continue;
- }
+ ath_txq_skb_done(sc, txq, skb);
+ ieee80211_free_txskb(sc->hw, skb);
+ continue;
}
- if (fi->retries) {
- list_add_tail(&bf->list, &bf_head);
+ if (fi->baw_tracked) {
ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
- ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
sendbar = true;
- } else {
- ath_set_rates(tid->an->vif, tid->an->sta, bf);
- ath_tx_send_normal(sc, txq, NULL, skb);
}
+
+ list_add_tail(&bf->list, &bf_head);
+ ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
}
if (sendbar) {
}
static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
- u16 seqno)
+ struct ath_buf *bf)
{
+ struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
+ u16 seqno = bf->bf_state.seqno;
int index, cindex;
index = ATH_BA_INDEX(tid->seq_start, seqno);
cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
__set_bit(cindex, tid->tx_buf);
+ fi->baw_tracked = 1;
if (index >= ((tid->baw_tail - tid->baw_head) &
(ATH_TID_MAX_BUFS - 1))) {
}
}
-/*
- * TODO: For frame(s) that are in the retry state, we will reuse the
- * sequence number(s) without setting the retry bit. The
- * alternative is to give up on these and BAR the receiver's window
- * forward.
- */
static void ath_tid_drain(struct ath_softc *sc, struct ath_txq *txq,
struct ath_atx_tid *tid)
memset(&ts, 0, sizeof(ts));
INIT_LIST_HEAD(&bf_head);
- while ((skb = __skb_dequeue(&tid->buf_q))) {
+ while ((skb = ath_tid_dequeue(tid))) {
fi = get_frame_info(skb);
bf = fi->bf;
}
list_add_tail(&bf->list, &bf_head);
-
- ath_tx_update_baw(sc, tid, bf->bf_state.seqno);
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
}
-
- tid->seq_next = tid->seq_start;
- tid->baw_tail = tid->baw_head;
- tid->bar_index = -1;
}
static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq,
struct ieee80211_tx_rate rates[4];
struct ath_frame_info *fi;
int nframes;
- u8 tidno;
bool flush = !!(ts->ts_status & ATH9K_TX_FLUSH);
int i, retries;
int bar_index = -1;
while (bf) {
bf_next = bf->bf_next;
- if (!bf->bf_stale || bf_next != NULL)
+ if (!bf->bf_state.stale || bf_next != NULL)
list_move_tail(&bf->list, &bf_head);
ath_tx_complete_buf(sc, bf, txq, &bf_head, ts, 0);
}
an = (struct ath_node *)sta->drv_priv;
- tidno = ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK;
- tid = ATH_AN_2_TID(an, tidno);
+ tid = ath_get_skb_tid(sc, an, skb);
seq_first = tid->seq_start;
isba = ts->ts_flags & ATH9K_TX_BA;
* Only BlockAcks have a TID and therefore normal Acks cannot be
* checked
*/
- if (isba && tidno != ts->tid)
+ if (isba && tid->tidno != ts->tid)
txok = false;
isaggr = bf_isaggr(bf);
tx_info = IEEE80211_SKB_CB(skb);
fi = get_frame_info(skb);
- if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
+ if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno) ||
+ !tid->active) {
/*
* Outside of the current BlockAck window,
* maybe part of a previous session
* not a holding desc.
*/
INIT_LIST_HEAD(&bf_head);
- if (bf_next != NULL || !bf_last->bf_stale)
+ if (bf_next != NULL || !bf_last->bf_state.stale)
list_move_tail(&bf->list, &bf_head);
if (!txpending) {
ieee80211_sta_eosp(sta);
}
/* retry the un-acked ones */
- if (bf->bf_next == NULL && bf_last->bf_stale) {
+ if (bf->bf_next == NULL && bf_last->bf_state.stale) {
struct ath_buf *tbf;
tbf = ath_clone_txbuf(sc, bf_last);
if (an->sleeping)
ieee80211_sta_set_buffered(sta, tid->tidno, true);
- skb_queue_splice(&bf_pending, &tid->buf_q);
+ skb_queue_splice_tail(&bf_pending, &tid->retry_q);
if (!an->sleeping) {
ath_tx_queue_tid(txq, tid);
} else
ath_tx_complete_aggr(sc, txq, bf, bf_head, ts, txok);
- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) && !flush)
+ if (!flush)
ath_txq_schedule(sc, txq);
}
static struct ath_buf *
ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
- struct ath_atx_tid *tid)
+ struct ath_atx_tid *tid, struct sk_buff_head **q)
{
+ struct ieee80211_tx_info *tx_info;
struct ath_frame_info *fi;
struct sk_buff *skb;
struct ath_buf *bf;
u16 seqno;
while (1) {
- skb = skb_peek(&tid->buf_q);
+ *q = &tid->retry_q;
+ if (skb_queue_empty(*q))
+ *q = &tid->buf_q;
+
+ skb = skb_peek(*q);
if (!skb)
break;
bf = fi->bf;
if (!fi->bf)
bf = ath_tx_setup_buffer(sc, txq, tid, skb);
+ else
+ bf->bf_state.stale = false;
if (!bf) {
- __skb_unlink(skb, &tid->buf_q);
+ __skb_unlink(skb, *q);
ath_txq_skb_done(sc, txq, skb);
ieee80211_free_txskb(sc->hw, skb);
continue;
}
+ bf->bf_next = NULL;
+ bf->bf_lastbf = bf;
+
+ tx_info = IEEE80211_SKB_CB(skb);
+ tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
+ if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) {
+ bf->bf_state.bf_type = 0;
+ return bf;
+ }
+
bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR;
seqno = bf->bf_state.seqno;
INIT_LIST_HEAD(&bf_head);
list_add(&bf->list, &bf_head);
- __skb_unlink(skb, &tid->buf_q);
+ __skb_unlink(skb, *q);
ath_tx_update_baw(sc, tid, seqno);
ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
continue;
}
- bf->bf_next = NULL;
- bf->bf_lastbf = bf;
return bf;
}
return NULL;
}
-static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc,
- struct ath_txq *txq,
- struct ath_atx_tid *tid,
- struct list_head *bf_q,
- int *aggr_len)
+static bool
+ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_atx_tid *tid, struct list_head *bf_q,
+ struct ath_buf *bf_first, struct sk_buff_head *tid_q,
+ int *aggr_len)
{
#define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
- struct ath_buf *bf, *bf_first = NULL, *bf_prev = NULL;
- int rl = 0, nframes = 0, ndelim, prev_al = 0;
+ struct ath_buf *bf = bf_first, *bf_prev = NULL;
+ int nframes = 0, ndelim;
u16 aggr_limit = 0, al = 0, bpad = 0,
- al_delta, h_baw = tid->baw_size / 2;
- enum ATH_AGGR_STATUS status = ATH_AGGR_DONE;
+ al_delta, h_baw = tid->baw_size / 2;
struct ieee80211_tx_info *tx_info;
struct ath_frame_info *fi;
struct sk_buff *skb;
+ bool closed = false;
- do {
- bf = ath_tx_get_tid_subframe(sc, txq, tid);
- if (!bf) {
- status = ATH_AGGR_BAW_CLOSED;
- break;
- }
+ bf = bf_first;
+ aggr_limit = ath_lookup_rate(sc, bf, tid);
+ do {
skb = bf->bf_mpdu;
fi = get_frame_info(skb);
- if (!bf_first)
- bf_first = bf;
-
- if (!rl) {
- ath_set_rates(tid->an->vif, tid->an->sta, bf);
- aggr_limit = ath_lookup_rate(sc, bf, tid);
- rl = 1;
- }
-
/* do not exceed aggregation limit */
al_delta = ATH_AGGR_DELIM_SZ + fi->framelen;
+ if (nframes) {
+ if (aggr_limit < al + bpad + al_delta ||
+ ath_lookup_legacy(bf) || nframes >= h_baw)
+ break;
- if (nframes &&
- ((aggr_limit < (al + bpad + al_delta + prev_al)) ||
- ath_lookup_legacy(bf))) {
- status = ATH_AGGR_LIMITED;
- break;
- }
-
- tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
- if (nframes && (tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE))
- break;
-
- /* do not exceed subframe limit */
- if (nframes >= min((int)h_baw, ATH_AMPDU_SUBFRAME_DEFAULT)) {
- status = ATH_AGGR_LIMITED;
- break;
+ tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+ if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
+ !(tx_info->flags & IEEE80211_TX_CTL_AMPDU))
+ break;
}
/* add padding for previous frame to aggregation length */
bf->bf_next = NULL;
/* link buffers of this frame to the aggregate */
- if (!fi->retries)
- ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+ if (!fi->baw_tracked)
+ ath_tx_addto_baw(sc, tid, bf);
bf->bf_state.ndelim = ndelim;
- __skb_unlink(skb, &tid->buf_q);
+ __skb_unlink(skb, tid_q);
list_add_tail(&bf->list, bf_q);
if (bf_prev)
bf_prev->bf_next = bf;
bf_prev = bf;
- } while (!skb_queue_empty(&tid->buf_q));
+ bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+ if (!bf) {
+ closed = true;
+ break;
+ }
+ } while (ath_tid_has_buffered(tid));
+
+ bf = bf_first;
+ bf->bf_lastbf = bf_prev;
+
+ if (bf == bf_prev) {
+ al = get_frame_info(bf->bf_mpdu)->framelen;
+ bf->bf_state.bf_type = BUF_AMPDU;
+ } else {
+ TX_STAT_INC(txq->axq_qnum, a_aggr);
+ }
*aggr_len = al;
- return status;
+ return closed;
#undef PADBYTES
}
}
static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf,
- struct ath_tx_info *info, int len)
+ struct ath_tx_info *info, int len, bool rts)
{
struct ath_hw *ah = sc->sc_ah;
struct sk_buff *skb;
const struct ieee80211_rate *rate;
struct ieee80211_hdr *hdr;
struct ath_frame_info *fi = get_frame_info(bf->bf_mpdu);
+ u32 rts_thresh = sc->hw->wiphy->rts_threshold;
int i;
u8 rix = 0;
rix = rates[i].idx;
info->rates[i].Tries = rates[i].count;
- if (rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
+ /*
+ * Handle RTS threshold for unaggregated HT frames.
+ */
+ if (bf_isampdu(bf) && !bf_isaggr(bf) &&
+ (rates[i].flags & IEEE80211_TX_RC_MCS) &&
+ unlikely(rts_thresh != (u32) -1)) {
+ if (!rts_thresh || (len > rts_thresh))
+ rts = true;
+ }
+
+ if (rts || rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) {
info->rates[i].RateFlags |= ATH9K_RATESERIES_RTS_CTS;
info->flags |= ATH9K_TXDESC_RTSENA;
} else if (rates[i].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
struct ath_hw *ah = sc->sc_ah;
struct ath_buf *bf_first = NULL;
struct ath_tx_info info;
+ u32 rts_thresh = sc->hw->wiphy->rts_threshold;
+ bool rts = false;
memset(&info, 0, sizeof(info));
info.is_first = true;
info.flags |= (u32) bf->bf_state.bfs_paprd <<
ATH9K_TXDESC_PAPRD_S;
- ath_buf_set_rate(sc, bf, &info, len);
+ /*
+ * mac80211 doesn't handle RTS threshold for HT because
+ * the decision has to be taken based on AMPDU length
+ * and aggregation is done entirely inside ath9k.
+ * Set the RTS/CTS flag for the first subframe based
+ * on the threshold.
+ */
+ if (aggr && (bf == bf_first) &&
+ unlikely(rts_thresh != (u32) -1)) {
+ /*
+ * "len" is the size of the entire AMPDU.
+ */
+ if (!rts_thresh || (len > rts_thresh))
+ rts = true;
+ }
+ ath_buf_set_rate(sc, bf, &info, len, rts);
}
info.buf_addr[0] = bf->bf_buf_addr;
}
}
-static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
- struct ath_atx_tid *tid)
+static void
+ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_atx_tid *tid, struct list_head *bf_q,
+ struct ath_buf *bf_first, struct sk_buff_head *tid_q)
{
- struct ath_buf *bf;
- enum ATH_AGGR_STATUS status;
- struct ieee80211_tx_info *tx_info;
- struct list_head bf_q;
- int aggr_len;
+ struct ath_buf *bf = bf_first, *bf_prev = NULL;
+ struct sk_buff *skb;
+ int nframes = 0;
do {
- if (skb_queue_empty(&tid->buf_q))
- return;
+ struct ieee80211_tx_info *tx_info;
+ skb = bf->bf_mpdu;
- INIT_LIST_HEAD(&bf_q);
+ nframes++;
+ __skb_unlink(skb, tid_q);
+ list_add_tail(&bf->list, bf_q);
+ if (bf_prev)
+ bf_prev->bf_next = bf;
+ bf_prev = bf;
- status = ath_tx_form_aggr(sc, txq, tid, &bf_q, &aggr_len);
+ if (nframes >= 2)
+ break;
- /*
- * no frames picked up to be aggregated;
- * block-ack window is not open.
- */
- if (list_empty(&bf_q))
+ bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+ if (!bf)
break;
- bf = list_first_entry(&bf_q, struct ath_buf, list);
- bf->bf_lastbf = list_entry(bf_q.prev, struct ath_buf, list);
tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+ break;
- if (tid->ac->clear_ps_filter) {
- tid->ac->clear_ps_filter = false;
- tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
- } else {
- tx_info->flags &= ~IEEE80211_TX_CTL_CLEAR_PS_FILT;
- }
+ ath_set_rates(tid->an->vif, tid->an->sta, bf);
+ } while (1);
+}
- /* if only one frame, send as non-aggregate */
- if (bf == bf->bf_lastbf) {
- aggr_len = get_frame_info(bf->bf_mpdu)->framelen;
- bf->bf_state.bf_type = BUF_AMPDU;
- } else {
- TX_STAT_INC(txq->axq_qnum, a_aggr);
- }
+static bool ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_atx_tid *tid, bool *stop)
+{
+ struct ath_buf *bf;
+ struct ieee80211_tx_info *tx_info;
+ struct sk_buff_head *tid_q;
+ struct list_head bf_q;
+ int aggr_len = 0;
+ bool aggr, last = true;
+
+ if (!ath_tid_has_buffered(tid))
+ return false;
+
+ INIT_LIST_HEAD(&bf_q);
+
+ bf = ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+ if (!bf)
+ return false;
+
+ tx_info = IEEE80211_SKB_CB(bf->bf_mpdu);
+ aggr = !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
+ if ((aggr && txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) ||
+ (!aggr && txq->axq_depth >= ATH_NON_AGGR_MIN_QDEPTH)) {
+ *stop = true;
+ return false;
+ }
- ath_tx_fill_desc(sc, bf, txq, aggr_len);
- ath_tx_txqaddbuf(sc, txq, &bf_q, false);
- } while (txq->axq_ampdu_depth < ATH_AGGR_MIN_QDEPTH &&
- status != ATH_AGGR_BAW_CLOSED);
+ ath_set_rates(tid->an->vif, tid->an->sta, bf);
+ if (aggr)
+ last = ath_tx_form_aggr(sc, txq, tid, &bf_q, bf,
+ tid_q, &aggr_len);
+ else
+ ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q);
+
+ if (list_empty(&bf_q))
+ return false;
+
+ if (tid->ac->clear_ps_filter || tid->an->no_ps_filter) {
+ tid->ac->clear_ps_filter = false;
+ tx_info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
+ }
+
+ ath_tx_fill_desc(sc, bf, txq, aggr_len);
+ ath_tx_txqaddbuf(sc, txq, &bf_q, false);
+ return true;
}
int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
an->mpdudensity = density;
}
+ /* force sequence number allocation for pending frames */
+ ath_tx_tid_change_state(sc, txtid);
+
txtid->active = true;
txtid->paused = true;
*ssn = txtid->seq_start = txtid->seq_next;
ath_txq_lock(sc, txq);
txtid->active = false;
- txtid->paused = true;
+ txtid->paused = false;
ath_tx_flush_tid(sc, txtid);
+ ath_tx_tid_change_state(sc, txtid);
ath_txq_unlock_complete(sc, txq);
}
ath_txq_lock(sc, txq);
- buffered = !skb_queue_empty(&tid->buf_q);
+ buffered = ath_tid_has_buffered(tid);
tid->sched = false;
list_del(&tid->list);
ath_txq_lock(sc, txq);
ac->clear_ps_filter = true;
- if (!skb_queue_empty(&tid->buf_q) && !tid->paused) {
+ if (!tid->paused && ath_tid_has_buffered(tid)) {
ath_tx_queue_tid(txq, tid);
ath_txq_schedule(sc, txq);
}
tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
tid->paused = false;
- if (!skb_queue_empty(&tid->buf_q)) {
+ if (ath_tid_has_buffered(tid)) {
ath_tx_queue_tid(txq, tid);
ath_txq_schedule(sc, txq);
}
struct ieee80211_tx_info *info;
struct list_head bf_q;
struct ath_buf *bf_tail = NULL, *bf;
+ struct sk_buff_head *tid_q;
int sent = 0;
int i;
continue;
ath_txq_lock(sc, tid->ac->txq);
- while (!skb_queue_empty(&tid->buf_q) && nframes > 0) {
- bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
+ while (nframes > 0) {
+ bf = ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q);
if (!bf)
break;
- __skb_unlink(bf->bf_mpdu, &tid->buf_q);
+ __skb_unlink(bf->bf_mpdu, tid_q);
list_add_tail(&bf->list, &bf_q);
ath_set_rates(tid->an->vif, tid->an->sta, bf);
- ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
+ ath_tx_addto_baw(sc, tid, bf);
bf->bf_state.bf_type &= ~BUF_AGGR;
if (bf_tail)
bf_tail->bf_next = bf;
sent++;
TX_STAT_INC(txq->axq_qnum, a_queued_hw);
- if (skb_queue_empty(&tid->buf_q))
+ if (an->sta && !ath_tid_has_buffered(tid))
ieee80211_sta_set_buffered(an->sta, i, false);
}
ath_txq_unlock_complete(sc, tid->ac->txq);
while (!list_empty(list)) {
bf = list_first_entry(list, struct ath_buf, list);
- if (bf->bf_stale) {
+ if (bf->bf_state.stale) {
list_del(&bf->list);
ath_tx_return_buffer(sc, bf);
*/
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
{
- struct ath_atx_ac *ac, *ac_tmp, *last_ac;
+ 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) ||
- list_empty(&txq->axq_acq) ||
- txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
+ list_empty(&txq->axq_acq))
return;
rcu_read_lock();
- ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
+ while (!list_empty(&txq->axq_acq)) {
+ bool stop = false;
- list_for_each_entry_safe(ac, ac_tmp, &txq->axq_acq, list) {
+ ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
list_del(&ac->list);
ac->sched = false;
while (!list_empty(&ac->tid_q)) {
+
tid = list_first_entry(&ac->tid_q, struct ath_atx_tid,
list);
list_del(&tid->list);
if (tid->paused)
continue;
- ath_tx_sched_aggr(sc, txq, tid);
+ if (ath_tx_sched_aggr(sc, txq, tid, &stop))
+ sent = true;
/*
* add tid to round-robin queue if more frames
* are pending for the tid
*/
- if (!skb_queue_empty(&tid->buf_q))
+ if (ath_tid_has_buffered(tid))
ath_tx_queue_tid(txq, tid);
- if (tid == last_tid ||
- txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
+ if (stop || tid == last_tid)
break;
}
list_add_tail(&ac->list, &txq->axq_acq);
}
- if (ac == last_ac ||
- txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH)
+ if (stop)
break;
+
+ if (ac == last_ac) {
+ if (!sent)
+ break;
+
+ sent = false;
+ last_ac = list_entry(txq->axq_acq.prev,
+ struct ath_atx_ac, list);
+ }
}
rcu_read_unlock();
}
}
-static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_txq *txq,
- struct ath_atx_tid *tid, struct sk_buff *skb,
- struct ath_tx_control *txctl)
-{
- struct ath_frame_info *fi = get_frame_info(skb);
- struct list_head bf_head;
- struct ath_buf *bf;
-
- /*
- * Do not queue to h/w when any of the following conditions is true:
- * - there are pending frames in software queue
- * - the TID is currently paused for ADDBA/BAR request
- * - seqno is not within block-ack window
- * - h/w queue depth exceeds low water mark
- */
- if ((!skb_queue_empty(&tid->buf_q) || tid->paused ||
- !BAW_WITHIN(tid->seq_start, tid->baw_size, tid->seq_next) ||
- txq->axq_ampdu_depth >= ATH_AGGR_MIN_QDEPTH) &&
- txq != sc->tx.uapsdq) {
- /*
- * Add this frame to software queue for scheduling later
- * for aggregation.
- */
- TX_STAT_INC(txq->axq_qnum, a_queued_sw);
- __skb_queue_tail(&tid->buf_q, skb);
- if (!txctl->an || !txctl->an->sleeping)
- ath_tx_queue_tid(txq, tid);
- return;
- }
-
- bf = ath_tx_setup_buffer(sc, txq, tid, skb);
- if (!bf) {
- ath_txq_skb_done(sc, txq, skb);
- ieee80211_free_txskb(sc->hw, skb);
- return;
- }
-
- ath_set_rates(tid->an->vif, tid->an->sta, bf);
- bf->bf_state.bf_type = BUF_AMPDU;
- INIT_LIST_HEAD(&bf_head);
- list_add(&bf->list, &bf_head);
-
- /* Add sub-frame to BAW */
- ath_tx_addto_baw(sc, tid, bf->bf_state.seqno);
-
- /* Queue to h/w without aggregation */
- TX_STAT_INC(txq->axq_qnum, a_queued_hw);
- bf->bf_lastbf = bf;
- ath_tx_fill_desc(sc, bf, txq, fi->framelen);
- ath_tx_txqaddbuf(sc, txq, &bf_head, false);
-}
-
static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq,
struct ath_atx_tid *tid, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = txctl->sta;
struct ieee80211_vif *vif = info->control.vif;
+ struct ath_vif *avp;
struct ath_softc *sc = hw->priv;
int frmlen = skb->len + FCS_LEN;
int padpos, padsize;
/* NOTE: sta can be NULL according to net/mac80211.h */
if (sta)
txctl->an = (struct ath_node *)sta->drv_priv;
+ else if (vif && ieee80211_is_data(hdr->frame_control)) {
+ avp = (void *)vif->drv_priv;
+ txctl->an = &avp->mcast_node;
+ }
if (info->control.hw_key)
frmlen += info->control.hw_key->icv_len;
struct ath_txq *txq = txctl->txq;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf;
- u8 tidno;
int q;
int ret;
ath_txq_unlock(sc, txq);
txq = sc->tx.uapsdq;
ath_txq_lock(sc, txq);
- }
-
- if (txctl->an && ieee80211_is_data_qos(hdr->frame_control)) {
- tidno = ieee80211_get_qos_ctl(hdr)[0] &
- IEEE80211_QOS_CTL_TID_MASK;
- tid = ATH_AN_2_TID(txctl->an, tidno);
+ } 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_AMPDU) && tid) {
+ if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
+ tid->ac->clear_ps_filter = true;
+
/*
- * Try aggregation if it's a unicast data frame
- * and the destination is HT capable.
+ * Add this frame to software queue for scheduling later
+ * for aggregation.
*/
- ath_tx_send_ampdu(sc, txq, tid, skb, txctl);
+ TX_STAT_INC(txq->axq_qnum, a_queued_sw);
+ __skb_queue_tail(&tid->buf_q, skb);
+ if (!txctl->an->sleeping)
+ ath_tx_queue_tid(txq, tid);
+
+ ath_txq_schedule(sc, txq);
goto out;
}
bf->bf_lastbf = bf;
ath_set_rates(vif, NULL, bf);
- ath_buf_set_rate(sc, bf, &info, fi->framelen);
+ ath_buf_set_rate(sc, bf, &info, fi->framelen, false);
duration += info.rates[0].PktDuration;
if (bf_tail)
bf_tail->bf_next = bf;
if (list_empty(&txq->axq_q)) {
txq->axq_link = NULL;
- if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT)
- ath_txq_schedule(sc, txq);
+ ath_txq_schedule(sc, txq);
break;
}
bf = list_first_entry(&txq->axq_q, struct ath_buf, list);
* it with the STALE flag.
*/
bf_held = NULL;
- if (bf->bf_stale) {
+ if (bf->bf_state.stale) {
bf_held = bf;
if (list_is_last(&bf_held->list, &txq->axq_q))
break;
* however leave the last descriptor back as the holding
* descriptor for hw.
*/
- lastbf->bf_stale = true;
+ lastbf->bf_state.stale = true;
INIT_LIST_HEAD(&bf_head);
if (!list_is_singular(&lastbf->list))
list_cut_position(&bf_head,
if (ts.qid == sc->beacon.beaconq) {
sc->beacon.tx_processed = true;
sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
+
+ ath9k_csa_is_finished(sc);
continue;
}
}
bf = list_first_entry(fifo_list, struct ath_buf, list);
- if (bf->bf_stale) {
+ if (bf->bf_state.stale) {
list_del(&bf->list);
ath_tx_return_buffer(sc, bf);
bf = list_first_entry(fifo_list, struct ath_buf, list);
ath_tx_txqaddbuf(sc, txq, &bf_q, true);
}
} else {
- lastbf->bf_stale = true;
+ lastbf->bf_state.stale = true;
if (bf != lastbf)
list_cut_position(&bf_head, fifo_list,
lastbf->list.prev);
tid->paused = false;
tid->active = false;
__skb_queue_head_init(&tid->buf_q);
+ __skb_queue_head_init(&tid->retry_q);
acno = TID_TO_WME_AC(tidno);
tid->ac = &an->ac[acno];
}
for (acno = 0, ac = &an->ac[acno];
acno < IEEE80211_NUM_ACS; acno++, ac++) {
ac->sched = false;
+ ac->clear_ps_filter = true;
ac->txq = sc->tx.txq_map[acno];
INIT_LIST_HEAD(&ac->tid_q);
}
wil6210-y += debug.o
wil6210-$(CONFIG_WIL6210_TRACING) += trace.o
-ifeq (, $(findstring -W,$(EXTRA_CFLAGS)))
- subdir-ccflags-y += -Werror
-endif
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
if ((i % 64) == 0 && (i != 0))
seq_printf(s, "\n");
seq_printf(s, "%s", (d->dma.status & BIT(0)) ?
- "S" : (vring->ctx[i] ? "H" : "h"));
+ "S" : (vring->ctx[i].skb ? "H" : "h"));
}
seq_printf(s, "\n");
}
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];
+ struct sk_buff *skb = vring->ctx[dbg_txdesc_index].skb;
seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index);
seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n",
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;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
wdev->netdev = ndev;
#endif /* !CONFIG_WIL6210_TRACING || defined(__CHECKER__) */
DECLARE_EVENT_CLASS(wil6210_wmi,
- TP_PROTO(u16 id, void *buf, u16 buf_len),
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
- TP_ARGS(id, buf, buf_len),
+ TP_ARGS(wmi, buf, buf_len),
TP_STRUCT__entry(
+ __field(u8, mid)
__field(u16, id)
+ __field(u32, timestamp)
__field(u16, buf_len)
__dynamic_array(u8, buf, buf_len)
),
TP_fast_assign(
- __entry->id = id;
+ __entry->mid = wmi->mid;
+ __entry->id = le16_to_cpu(wmi->id);
+ __entry->timestamp = le32_to_cpu(wmi->timestamp);
__entry->buf_len = buf_len;
memcpy(__get_dynamic_array(buf), buf, buf_len);
),
TP_printk(
- "id 0x%04x len %d",
- __entry->id, __entry->buf_len
+ "MID %d id 0x%04x len %d timestamp %d",
+ __entry->mid, __entry->id, __entry->buf_len, __entry->timestamp
)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_cmd,
- TP_PROTO(u16 id, void *buf, u16 buf_len),
- TP_ARGS(id, buf, buf_len)
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+ TP_ARGS(wmi, buf, buf_len)
);
DEFINE_EVENT(wil6210_wmi, wil6210_wmi_event,
- TP_PROTO(u16 id, void *buf, u16 buf_len),
- TP_ARGS(id, buf, buf_len)
+ TP_PROTO(struct wil6210_mbox_hdr_wmi *wmi, void *buf, u16 buf_len),
+ TP_ARGS(wmi, buf, buf_len)
);
#define WIL6210_MSG_MAX (200)
#include <net/ieee80211_radiotap.h>
#include <linux/if_arp.h>
#include <linux/moduleparam.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ipv6.h>
#include "wil6210.h"
#include "wmi.h"
vring->swhead = 0;
vring->swtail = 0;
- vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL);
+ vring->ctx = kcalloc(vring->size, sizeof(vring->ctx[0]), GFP_KERNEL);
if (!vring->ctx) {
vring->va = NULL;
return -ENOMEM;
while (!wil_vring_is_empty(vring)) {
dma_addr_t pa;
- struct sk_buff *skb;
u16 dmalen;
+ struct wil_ctx *ctx;
if (tx) {
struct vring_tx_desc dd, *d = ⅆ
volatile struct vring_tx_desc *_d =
&vring->va[vring->swtail].tx;
+ ctx = &vring->ctx[vring->swtail];
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
- skb = vring->ctx[vring->swtail];
- if (skb) {
- dma_unmap_single(dev, pa, dmalen,
- DMA_TO_DEVICE);
- dev_kfree_skb_any(skb);
- vring->ctx[vring->swtail] = NULL;
- } else {
+ 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);
}
+ if (ctx->skb)
+ dev_kfree_skb_any(ctx->skb);
vring->swtail = wil_vring_next_tail(vring);
} else { /* rx */
struct vring_rx_desc dd, *d = ⅆ
volatile struct vring_rx_desc *_d =
- &vring->va[vring->swtail].rx;
+ &vring->va[vring->swhead].rx;
+ ctx = &vring->ctx[vring->swhead];
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
dmalen = le16_to_cpu(d->dma.length);
- skb = vring->ctx[vring->swhead];
dma_unmap_single(dev, pa, dmalen, DMA_FROM_DEVICE);
- kfree_skb(skb);
+ kfree_skb(ctx->skb);
wil_vring_advance_head(vring, 1);
}
}
d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */
d->dma.length = cpu_to_le16(sz);
*_d = *d;
- vring->ctx[i] = skb;
+ vring->ctx[i].skb = skb;
return 0;
}
return NULL;
}
- skb = vring->ctx[vring->swhead];
+ skb = vring->ctx[vring->swhead].skb;
d = wil_skb_rxdesc(skb);
*d = *_d;
pa = wil_desc_addr(&d->dma.addr);
- vring->ctx[vring->swhead] = NULL;
+ vring->ctx[vring->swhead].skb = NULL;
wil_vring_advance_head(vring, 1);
dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE);
return NULL;
}
+ /* L4 IDENT is on when HW calculated checksum, check status
+ * and in case of error drop the packet
+ * higher stack layers will handle retransmission (if required)
+ */
+ if (d->dma.status & RX_DMA_STATUS_L4_IDENT) {
+ /* L4 protocol identified, csum calculated */
+ if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0)
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ /* If HW reports bad checksum, let IP stack re-check it
+ * For example, HW don't understand Microsoft IP stack that
+ * mis-calculates TCP checksum - if it should be 0x0,
+ * it writes 0xffff in violation of RFC 1624
+ */
+ }
+
ds_bits = wil_rxdesc_ds_bits(d);
if (ds_bits == 1) {
/*
return 0;
}
+static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
+ struct vring_tx_desc *d,
+ struct sk_buff *skb)
+{
+ int protocol;
+
+ if (skb->ip_summed != CHECKSUM_PARTIAL)
+ return 0;
+
+ switch (skb->protocol) {
+ case cpu_to_be16(ETH_P_IP):
+ protocol = ip_hdr(skb)->protocol;
+ break;
+ case cpu_to_be16(ETH_P_IPV6):
+ protocol = ipv6_hdr(skb)->nexthdr;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (protocol) {
+ case IPPROTO_TCP:
+ d->dma.d0 |= (2 << DMA_CFG_DESC_TX_0_L4_TYPE_POS);
+ /* L4 header len: TCP header length */
+ d->dma.d0 |=
+ (tcp_hdrlen(skb) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+ break;
+ case IPPROTO_UDP:
+ /* L4 header len: UDP header length */
+ d->dma.d0 |=
+ (sizeof(struct udphdr) & DMA_CFG_DESC_TX_0_L4_LENGTH_MSK);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ 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 */
+ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS);
+
+ return 0;
+}
+
static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
struct sk_buff *skb)
{
u32 swhead = vring->swhead;
int avail = wil_vring_avail_tx(vring);
int nr_frags = skb_shinfo(skb)->nr_frags;
- uint f;
+ uint f = 0;
int vring_index = vring - wil->vring_tx;
uint i = swhead;
dma_addr_t pa;
return -EINVAL;
/* 1-st segment */
wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index);
+ /* Process TCP/UDP checksum offloading */
+ if (wil_tx_desc_offload_cksum_set(wil, d, skb)) {
+ wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n",
+ vring_index);
+ goto dma_error;
+ }
+
d->mac.d[2] |= ((nr_frags + 1) <<
MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS);
if (nr_frags)
*_d = *d;
/* middle segments */
- for (f = 0; f < nr_frags; f++) {
+ for (; f < nr_frags; f++) {
const struct skb_frag_struct *frag =
&skb_shinfo(skb)->frags[f];
int len = skb_frag_size(frag);
if (unlikely(dma_mapping_error(dev, pa)))
goto dma_error;
wil_tx_desc_map(d, pa, len, vring_index);
- vring->ctx[i] = NULL;
+ vring->ctx[i].mapped_as_page = 1;
*_d = *d;
}
/* for the last seg only */
d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS);
*_d = *d;
+ /* hold reference to skb
+ * to prevent skb release before accounting
+ * in case of immediate "tx done"
+ */
+ vring->ctx[i].skb = skb_get(skb);
+
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags);
iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail));
- /* hold reference to skb
- * to prevent skb release before accounting
- * in case of immediate "tx done"
- */
- vring->ctx[i] = skb_get(skb);
return 0;
dma_error:
/* unmap what we have mapped */
- /* Note: increment @f to operate with positive index */
- for (f++; f > 0; f--) {
+ 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;
+ ctx = &vring->ctx[i];
_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 (vring->ctx[i])
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
- else
+ 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 (ctx->skb)
+ dev_kfree_skb_any(ctx->skb);
+
+ memset(ctx, 0, sizeof(*ctx));
}
return -EINVAL;
&vring->va[vring->swtail].tx;
struct vring_tx_desc dd, *d = ⅆ
dma_addr_t pa;
- struct sk_buff *skb;
u16 dmalen;
+ struct wil_ctx *ctx = &vring->ctx[vring->swtail];
+ struct sk_buff *skb = ctx->skb;
*d = *_d;
(const void *)d, sizeof(*d), false);
pa = wil_desc_addr(&d->dma.addr);
- skb = vring->ctx[vring->swtail];
+ 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_errors++;
}
- dma_unmap_single(dev, pa, dmalen, DMA_TO_DEVICE);
dev_kfree_skb_any(skb);
- vring->ctx[vring->swtail] = NULL;
- } else {
- dma_unmap_page(dev, pa, dmalen, DMA_TO_DEVICE);
}
- d->dma.addr.addr_low = 0;
- d->dma.addr.addr_high = 0;
- d->dma.length = 0;
- d->dma.status = TX_DMA_STATUS_DU;
+ 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++;
}
#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30
#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2
-#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000
+#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 /* L4 type: 0-UDP, 2-TCP */
+
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_POS 0
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_LEN 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_MAC_LEN_MSK 0x7F /* MAC hdr len */
+
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS 7
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_LEN 1
+#define DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_MSK 0x80 /* 1-IPv4, 0-IPv6 */
#define TX_DMA_STATUS_DU BIT(0)
#define RX_DMA_D0_CMD_DMA_IT BIT(10)
+/* Error field, offload bits */
+#define RX_DMA_ERROR_L3_ERR BIT(4)
+#define RX_DMA_ERROR_L4_ERR BIT(5)
+
+
+/* Status field */
#define RX_DMA_STATUS_DU BIT(0)
#define RX_DMA_STATUS_ERROR BIT(2)
+
+#define RX_DMA_STATUS_L3_IDENT BIT(4)
+#define RX_DMA_STATUS_L4_IDENT BIT(5)
#define RX_DMA_STATUS_PHY_INFO BIT(6)
struct vring_rx_dma {
/* max. value for wil6210_mbox_hdr.len */
#define MAX_MBOXITEM_SIZE (240)
+/**
+ * struct wil6210_mbox_hdr_wmi - WMI header
+ *
+ * @mid: MAC ID
+ * 00 - default, created by FW
+ * 01..0f - WiFi ports, driver to create
+ * 10..fe - debug
+ * ff - broadcast
+ * @id: command/event ID
+ * @timestamp: FW fills for events, free-running msec timer
+ */
struct wil6210_mbox_hdr_wmi {
- u8 reserved0[2];
+ u8 mid;
+ u8 reserved;
__le16 id;
- __le16 info1; /* bits [0..3] - device_id, rest - unused */
- u8 reserved1[2];
+ __le32 timestamp;
} __packed;
struct pending_wmi_event {
} __packed event;
};
+/**
+ * struct wil_ctx - software context for Vring descriptor
+ */
+struct wil_ctx {
+ struct sk_buff *skb;
+ u8 mapped_as_page:1;
+};
+
union vring_desc;
struct vring {
u32 swtail;
u32 swhead;
u32 hwtail; /* write here to inform hw */
- void **ctx; /* void *ctx[size] - software context */
+ struct wil_ctx *ctx; /* ctx[size] - software context */
};
enum { /* for wil6210_priv.status */
.len = cpu_to_le16(sizeof(cmd.wmi) + len),
},
.wmi = {
+ .mid = 0,
.id = cpu_to_le16(cmdid),
- .info1 = 0,
},
};
struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx;
iowrite32(r->head = next_head, wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, tx.head));
- trace_wil6210_wmi_cmd(cmdid, buf, len);
+ trace_wil6210_wmi_cmd(&cmd.wmi, buf, len);
/* interrupt to FW */
iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT);
hdr.flags);
if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) &&
(len >= sizeof(struct wil6210_mbox_hdr_wmi))) {
- u16 id = le16_to_cpu(evt->event.wmi.id);
- wil_dbg_wmi(wil, "WMI event 0x%04x\n", id);
- trace_wil6210_wmi_event(id, &evt->event.wmi, len);
+ struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi;
+ u16 id = le16_to_cpu(wmi->id);
+ u32 tstamp = le32_to_cpu(wmi->timestamp);
+ wil_dbg_wmi(wil, "WMI event 0x%04x MID %d @%d msec\n",
+ id, wmi->mid, tstamp);
+ trace_wil6210_wmi_event(wmi, &wmi[1],
+ len - sizeof(*wmi));
}
wil_hex_dump_wmi("evt ", DUMP_PREFIX_OFFSET, 16, 1,
&evt->event.hdr, sizeof(hdr) + len, true);
cmd.sniffer_cfg.phy_support =
cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL)
? WMI_SNIFFER_CP : WMI_SNIFFER_DP);
+ } else {
+ /* Initialize offload (in non-sniffer mode).
+ * Linux IP stack always calculates IP checksum
+ * HW always calculate TCP/UDP checksum
+ */
+ cmd.l3_l4_ctrl |= (1 << L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS);
}
/* typical time for secure PCP is 840ms */
rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd),
b43_maskset32(dev, B43_MMIO_MACCTL, ~B43_MACCTL_PSM_RUN,
B43_MACCTL_PSM_JMP0);
+ switch (dev->dev->bus_type) {
+#ifdef CONFIG_B43_BCMA
+ case B43_BUS_BCMA:
+ bcma_core_pci_down(dev->dev->bdev->bus);
+ break;
+#endif
+#ifdef CONFIG_B43_SSB
+ case B43_BUS_SSB:
+ /* TODO */
+ break;
+#endif
+ }
+
b43_dma_free(dev);
b43_pio_free(dev);
b43_chip_exit(dev);
case B43_BUS_BCMA:
bcma_core_pci_irq_ctl(&dev->dev->bdev->bus->drv_pci[0],
dev->dev->bdev, true);
+ bcma_core_pci_up(dev->dev->bdev->bus);
break;
#endif
#ifdef CONFIG_B43_SSB
uint flags, u8 *buf, uint nbytes)
{
struct sk_buff *mypkt;
+ struct sk_buff_head pktq;
int err;
mypkt = brcmu_pkt_buf_get_skb(nbytes);
}
memcpy(mypkt->data, buf, nbytes);
- err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, mypkt);
+ __skb_queue_head_init(&pktq);
+ __skb_queue_tail(&pktq, mypkt);
+ err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq);
+ __skb_dequeue_tail(&pktq);
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 *pkt)
+ uint flags, struct sk_buff_head *pktq)
{
uint width;
int err = 0;
- struct sk_buff_head pkt_list;
brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
- fn, addr, pkt->len);
+ fn, addr, pktq->qlen);
width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
brcmf_sdio_addrprep(sdiodev, width, &addr);
- skb_queue_head_init(&pkt_list);
- skb_queue_tail(&pkt_list, pkt);
- err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, &pkt_list);
- skb_dequeue_tail(&pkt_list);
+ err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq);
return err;
}
sdiodev->bus_if = bus_if;
bus_if->bus_priv.sdio = sdiodev;
- bus_if->align = BRCMF_SDALIGN;
dev_set_drvdata(&func->dev, bus_if);
dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
sdiodev->dev = &sdiodev->func[1]->dev;
#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_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.
uint needed; /* bytes needed (optional) */
};
+/**
+ * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
+ *
+ * @pktslots: dynamic allocated array for ordering AMPDU packets.
+ * @flow_id: AMPDU flow identifier.
+ * @cur_idx: last AMPDU index from firmware.
+ * @exp_idx: expected next AMPDU index.
+ * @max_idx: maximum amount of packets per AMPDU.
+ * @pend_pkts: number of packets currently in @pktslots.
+ */
+struct brcmf_ampdu_rx_reorder {
+ struct sk_buff **pktslots;
+ u8 flow_id;
+ u8 cur_idx;
+ u8 exp_idx;
+ u8 max_idx;
+ u8 pend_pkts;
+};
+
/* Forward decls for struct brcmf_pub (see below) */
struct brcmf_proto; /* device communication protocol info */
struct brcmf_cfg80211_dev; /* cfg80211 device info */
struct brcmf_fweh_info fweh;
- bool fw_signals;
struct brcmf_fws_info *fws;
- spinlock_t fws_spinlock;
+
+ struct brcmf_ampdu_rx_reorder
+ *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS];
#ifdef DEBUG
struct dentry *dbgfs_dir;
#endif
wait_queue_head_t pend_8021x_wait;
};
+struct brcmf_skb_reorder_data {
+ u8 *reorder;
+};
extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev);
*
* @init: prepare for communication with dongle.
* @stop: clear pending frames, disable data flow.
- * @txdata: send a data frame to the dongle (callee disposes skb).
+ * @txdata: send a data frame to the dongle. When the data
+ * has been transferred, the common driver must be
+ * notified using brcmf_txcomplete(). The common
+ * driver calls this function with interrupts
+ * disabled.
* @txctl: transmit a control request message to dongle.
* @rxctl: receive a control response message from dongle.
* @gettxq: obtain a reference of bus transmit queue (optional).
* @maxctl: maximum size for rxctl request message.
* @tx_realloc: number of tx packets realloced for headroom.
* @dstats: dongle-based statistical data.
- * @align: alignment requirement for the bus.
* @dcmd_list: bus/device specific dongle initialization commands.
* @chip: device identifier of the dongle chip.
* @chiprev: revision of the dongle chip.
enum brcmf_bus_state state;
uint maxctl;
unsigned long tx_realloc;
- u8 align;
u32 chip;
u32 chiprev;
struct list_head dcmd_list;
#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */
+/* AMPDU rx reordering definitions */
+#define BRCMF_RXREORDER_FLOWID_OFFSET 0
+#define BRCMF_RXREORDER_MAXIDX_OFFSET 2
+#define BRCMF_RXREORDER_FLAGS_OFFSET 4
+#define BRCMF_RXREORDER_CURIDX_OFFSET 6
+#define BRCMF_RXREORDER_EXPIDX_OFFSET 8
+
+#define BRCMF_RXREORDER_DEL_FLOW 0x01
+#define BRCMF_RXREORDER_FLUSH_ALL 0x02
+#define BRCMF_RXREORDER_CURIDX_VALID 0x04
+#define BRCMF_RXREORDER_EXPIDX_VALID 0x08
+#define BRCMF_RXREORDER_NEW_HOLE 0x10
+
/* Error bits */
int brcmf_msg_level;
module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
- int i;
brcmf_dbg(TRACE, "Enter\n");
- if (brcmf_fws_fc_active(drvr->fws)) {
- brcmf_fws_bus_blocked(drvr, state);
+ brcmf_fws_bus_blocked(drvr, state);
+}
+
+static void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+{
+ skb->dev = ifp->ndev;
+ skb->protocol = eth_type_trans(skb, skb->dev);
+
+ if (skb->pkt_type == PACKET_MULTICAST)
+ ifp->stats.multicast++;
+
+ /* Process special event packets */
+ brcmf_fweh_process_skb(ifp->drvr, skb);
+
+ if (!(ifp->ndev->flags & IFF_UP)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+
+ ifp->stats.rx_bytes += skb->len;
+ ifp->stats.rx_packets++;
+
+ brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol));
+ if (in_interrupt())
+ netif_rx(skb);
+ else
+ /* If the receive is not processed inside an ISR,
+ * the softirqd must be woken explicitly to service
+ * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
+ */
+ netif_rx_ni(skb);
+}
+
+static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi,
+ u8 start, u8 end,
+ struct sk_buff_head *skb_list)
+{
+ /* initialize return list */
+ __skb_queue_head_init(skb_list);
+
+ if (rfi->pend_pkts == 0) {
+ brcmf_dbg(INFO, "no packets in reorder queue\n");
+ return;
+ }
+
+ do {
+ if (rfi->pktslots[start]) {
+ __skb_queue_tail(skb_list, rfi->pktslots[start]);
+ rfi->pktslots[start] = NULL;
+ }
+ start++;
+ if (start > rfi->max_idx)
+ start = 0;
+ } while (start != end);
+ rfi->pend_pkts -= skb_queue_len(skb_list);
+}
+
+static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data,
+ struct sk_buff *pkt)
+{
+ u8 flow_id, max_idx, cur_idx, exp_idx, end_idx;
+ struct brcmf_ampdu_rx_reorder *rfi;
+ struct sk_buff_head reorder_list;
+ struct sk_buff *pnext;
+ u8 flags;
+ u32 buf_size;
+
+ flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET];
+ flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET];
+
+ /* validate flags and flow id */
+ if (flags == 0xFF) {
+ brcmf_err("invalid flags...so ignore this packet\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ rfi = ifp->drvr->reorder_flows[flow_id];
+ if (flags & BRCMF_RXREORDER_DEL_FLOW) {
+ brcmf_dbg(INFO, "flow-%d: delete\n",
+ flow_id);
+
+ if (rfi == NULL) {
+ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ flow_id);
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx,
+ &reorder_list);
+ /* add the last packet */
+ __skb_queue_tail(&reorder_list, pkt);
+ kfree(rfi);
+ ifp->drvr->reorder_flows[flow_id] = NULL;
+ goto netif_rx;
+ }
+ /* from here on we need a flow reorder instance */
+ if (rfi == NULL) {
+ buf_size = sizeof(*rfi);
+ max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+
+ buf_size += (max_idx + 1) * sizeof(pkt);
+
+ /* allocate space for flow reorder info */
+ brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n",
+ flow_id, max_idx);
+ rfi = kzalloc(buf_size, GFP_ATOMIC);
+ if (rfi == NULL) {
+ brcmf_err("failed to alloc buffer\n");
+ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+ ifp->drvr->reorder_flows[flow_id] = rfi;
+ rfi->pktslots = (struct sk_buff **)(rfi+1);
+ rfi->max_idx = max_idx;
+ }
+ if (flags & BRCMF_RXREORDER_NEW_HOLE) {
+ if (rfi->pend_pkts) {
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx,
+ rfi->exp_idx,
+ &reorder_list);
+ WARN_ON(rfi->pend_pkts);
+ } else {
+ __skb_queue_head_init(&reorder_list);
+ }
+ rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+ rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET];
+ rfi->pktslots[rfi->cur_idx] = pkt;
+ rfi->pend_pkts++;
+ brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n",
+ flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts);
+ } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) {
+ cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET];
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) {
+ /* still in the current hole */
+ /* enqueue the current on the buffer chain */
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ rfi->cur_idx = cur_idx;
+ brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ /* can return now as there is no reorder
+ * list to process.
+ */
+ return;
+ }
+ if (rfi->exp_idx == cur_idx) {
+ if (rfi->pktslots[cur_idx] != NULL) {
+ brcmf_dbg(INFO, "error buffer pending..free it\n");
+ brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]);
+ rfi->pktslots[cur_idx] = NULL;
+ }
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+
+ /* got the expected one. flush from current to expected
+ * and update expected
+ */
+ brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n",
+ flow_id, cur_idx, exp_idx, rfi->pend_pkts);
+
+ rfi->cur_idx = cur_idx;
+ rfi->exp_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx,
+ &reorder_list);
+ brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n",
+ flow_id, skb_queue_len(&reorder_list),
+ rfi->pend_pkts);
+ } else {
+ u8 end_idx;
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n",
+ flow_id, flags, rfi->cur_idx, rfi->exp_idx,
+ cur_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ /* flush pkts first */
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+
+ if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) {
+ __skb_queue_tail(&reorder_list, pkt);
+ } else {
+ rfi->pktslots[cur_idx] = pkt;
+ rfi->pend_pkts++;
+ }
+ rfi->exp_idx = exp_idx;
+ rfi->cur_idx = cur_idx;
+ }
} else {
- for (i = 0; i < BRCMF_MAX_IFS; i++)
- brcmf_txflowblock_if(drvr->iflist[i],
- BRCMF_NETIF_STOP_REASON_BLOCK_BUS,
- state);
+ /* explicity window move updating the expected index */
+ exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET];
+
+ brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n",
+ flow_id, flags, rfi->exp_idx, exp_idx);
+ if (flags & BRCMF_RXREORDER_FLUSH_ALL)
+ end_idx = rfi->exp_idx;
+ else
+ end_idx = exp_idx;
+
+ brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx,
+ &reorder_list);
+ __skb_queue_tail(&reorder_list, pkt);
+ /* set the new expected idx */
+ rfi->exp_idx = exp_idx;
+ }
+netif_rx:
+ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ __skb_unlink(pkt, &reorder_list);
+ brcmf_netif_rx(ifp, pkt);
}
}
struct brcmf_if *ifp;
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
+ struct brcmf_skb_reorder_data *rd;
u8 ifidx;
int ret;
- brcmf_dbg(DATA, "Enter\n");
+ 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);
/* process and remove protocol-specific header */
- ret = brcmf_proto_hdrpull(drvr, drvr->fw_signals, &ifidx, skb);
+ ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb);
ifp = drvr->iflist[ifidx];
if (ret || !ifp || !ifp->ndev) {
continue;
}
- skb->dev = ifp->ndev;
- skb->protocol = eth_type_trans(skb, skb->dev);
-
- if (skb->pkt_type == PACKET_MULTICAST)
- ifp->stats.multicast++;
-
- /* Process special event packets */
- brcmf_fweh_process_skb(drvr, skb);
-
- if (!(ifp->ndev->flags & IFF_UP)) {
- brcmu_pkt_buf_free_skb(skb);
- continue;
- }
-
- ifp->stats.rx_bytes += skb->len;
- ifp->stats.rx_packets++;
-
- if (in_interrupt())
- netif_rx(skb);
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ if (rd->reorder)
+ brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
else
- /* If the receive is not processed inside an ISR,
- * the softirqd must be woken explicitly to service the
- * NET_RX_SOFTIRQ. This is handled by netif_rx_ni().
- */
- netif_rx_ni(skb);
+ brcmf_netif_rx(ifp, skb);
}
}
if (ret < 0)
goto fail;
- drvr->fw_signals = true;
ret = brcmf_fws_init(drvr);
if (ret < 0)
goto fail;
#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */
#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */
-/* HW frame tag */
-#define SDPCM_FRAMETAG_LEN 4 /* 2 bytes len, 2 bytes check val */
-
-/* Total length of frame header for dongle protocol */
-#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
-#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN)
-
/*
* Software allocation of To SB Mailbox resources
*/
/* Current protocol version */
#define SDPCM_PROT_VERSION 4
-/* SW frame header */
-#define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff)
-
-#define SDPCM_CHANNEL_MASK 0x00000f00
-#define SDPCM_CHANNEL_SHIFT 8
-#define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f)
-
-#define SDPCM_NEXTLEN_OFFSET 2
-
-/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
-#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */
-#define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
-#define SDPCM_DOFFSET_MASK 0xff000000
-#define SDPCM_DOFFSET_SHIFT 24
-#define SDPCM_FCMASK_OFFSET 4 /* Flow control */
-#define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
-#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */
-#define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
-
-#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */
-
-/* logical channel numbers */
-#define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */
-#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */
-#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */
-#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */
-#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */
-
-#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */
-
-#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
-
/*
* Shared structure between dongle and the host.
* The structure contains pointers to trap or assert information.
__le32 brpt_addr;
};
-/* SDIO read frame info */
-struct brcmf_sdio_read {
+/* dongle SDIO bus specific header info */
+struct brcmf_sdio_hdrinfo {
u8 seq_num;
u8 channel;
u16 len;
u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
u8 rx_seq; /* Receive sequence number (expected) */
- struct brcmf_sdio_read cur_read;
+ struct brcmf_sdio_hdrinfo cur_read;
/* info of current read frame */
bool rxskip; /* Skip receive (awaiting NAK ACK) */
bool rxpending; /* Data frame pending in dongle */
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 */
};
/* clkstate */
#ifdef DEBUG
static int qcount[NUMPRIO];
-static int tx_packets[NUMPRIO];
#endif /* DEBUG */
#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */
}
}
-static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
- struct brcmf_sdio_read *rd,
- enum brcmf_sdio_frmtype type)
+/**
+ * brcmfmac sdio bus specific header
+ * This is the lowest layer header wrapped on the packets transmitted between
+ * host and WiFi dongle which contains information needed for SDIO core and
+ * firmware
+ *
+ * It consists of 2 parts: hw 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
+ * software header - 8 bytes
+ * Byte 0: Rx/Tx sequence number
+ * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
+ * Byte 2: Length of next data frame, reserved for Tx
+ * Byte 3: Data offset
+ * Byte 4: Flow control bits, reserved for Tx
+ * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet
+ * Byte 6~7: Reserved
+ */
+#define SDPCM_HWHDR_LEN 4
+#define SDPCM_SWHDR_LEN 8
+#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
+/* software header */
+#define SDPCM_SEQ_MASK 0x000000ff
+#define SDPCM_SEQ_WRAP 256
+#define SDPCM_CHANNEL_MASK 0x00000f00
+#define SDPCM_CHANNEL_SHIFT 8
+#define SDPCM_CONTROL_CHANNEL 0 /* Control */
+#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */
+#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */
+#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */
+#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */
+#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
+#define SDPCM_NEXTLEN_MASK 0x00ff0000
+#define SDPCM_NEXTLEN_SHIFT 16
+#define SDPCM_DOFFSET_MASK 0xff000000
+#define SDPCM_DOFFSET_SHIFT 24
+#define SDPCM_FCMASK_MASK 0x000000ff
+#define SDPCM_WINDOW_MASK 0x0000ff00
+#define SDPCM_WINDOW_SHIFT 8
+
+static inline u8 brcmf_sdio_getdatoffset(u8 *swheader)
+{
+ u32 hdrvalue;
+ hdrvalue = *(u32 *)swheader;
+ return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+}
+
+static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *rd,
+ enum brcmf_sdio_frmtype type)
{
u16 len, checksum;
u8 rx_seq, fc, tx_seq_max;
+ u32 swheader;
- /*
- * 4 bytes hardware header (frame tag)
- * Byte 0~1: Frame length
- * Byte 2~3: Checksum, bit-wise inverse of frame length
- */
+ /* hw header */
len = get_unaligned_le16(header);
checksum = get_unaligned_le16(header + sizeof(u16));
/* All zero means no more to read */
}
rd->len = len;
- /*
- * 8 bytes hardware header
- * Byte 0: Rx sequence number
- * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag
- * Byte 2: Length of next data frame
- * Byte 3: Data offset
- * Byte 4: Flow control bits
- * Byte 5: Maximum Sequence number allow for Tx
- * Byte 6~7: Reserved
- */
- if (type == BRCMF_SDIO_FT_SUPER &&
- SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) {
+ /* software header */
+ header += SDPCM_HWHDR_LEN;
+ swheader = le32_to_cpu(*(__le32 *)header);
+ if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) {
brcmf_err("Glom descriptor found in superframe head\n");
rd->len = 0;
return -EINVAL;
}
- rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]);
- rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]);
+ rx_seq = (u8)(swheader & SDPCM_SEQ_MASK);
+ rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT;
if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
type != BRCMF_SDIO_FT_SUPER) {
brcmf_err("HW header length too long\n");
rd->len = 0;
return -EINVAL;
}
- rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+ rd->dat_offset = brcmf_sdio_getdatoffset(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++;
/* no need to check the reset for subframe */
if (type == BRCMF_SDIO_FT_SUB)
return 0;
- rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+ rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT;
if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
/* only warm for NON glom packet */
if (rd->channel != SDPCM_GLOM_CHANNEL)
brcmf_err("seq %d: next length error\n", rx_seq);
rd->len_nxtfrm = 0;
}
- fc = SDPCM_FCMASK_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+ swheader = le32_to_cpu(*(__le32 *)(header + 4));
+ fc = swheader & SDPCM_FCMASK_MASK;
if (bus->flowcontrol != fc) {
if (~bus->flowcontrol & fc)
bus->sdcnt.fc_xoff++;
bus->sdcnt.fc_rcvd++;
bus->flowcontrol = fc;
}
- tx_seq_max = SDPCM_WINDOW_VALUE(&header[SDPCM_FRAMETAG_LEN]);
+ tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT;
if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) {
brcmf_err("seq %d: max tx seq number error\n", rx_seq);
tx_seq_max = bus->tx_seq + 2;
return 0;
}
+static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)
+{
+ *(__le16 *)header = cpu_to_le16(frm_length);
+ *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length);
+}
+
+static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *hd_info)
+{
+ u32 sw_header;
+
+ 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;
+}
+
static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
{
u16 dlen, totlen;
u8 *dptr, num = 0;
-
+ u32 align = 0;
u16 sublen;
struct sk_buff *pfirst, *pnext;
int errcode;
u8 doff, sfdoff;
- struct brcmf_sdio_read rd_new;
+ struct brcmf_sdio_hdrinfo rd_new;
/* If packets, issue read(s) and send up packet chain */
/* Return sequence numbers consumed? */
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;
pnext = NULL;
break;
}
- if (sublen % BRCMF_SDALIGN) {
+ if (sublen % align) {
brcmf_err("sublen %d not multiple of %d\n",
- sublen, BRCMF_SDALIGN);
+ sublen, align);
}
totlen += sublen;
}
/* Allocate/chain packet for next subframe */
- pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
+ pnext = brcmu_pkt_buf_get_skb(sublen + align);
if (pnext == NULL) {
brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",
num, sublen);
skb_queue_tail(&bus->glom, pnext);
/* Adhere to start alignment requirements */
- pkt_align(pnext, sublen, BRCMF_SDALIGN);
+ pkt_align(pnext, sublen, align);
}
/* If all allocations succeeded, save packet chain
rd_new.seq_num = rxseq;
rd_new.len = dlen;
sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdio_hdparser(bus, pfirst->data, &rd_new,
- BRCMF_SDIO_FT_SUPER);
+ errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new,
+ BRCMF_SDIO_FT_SUPER);
sdio_release_host(bus->sdiodev->func[1]);
bus->cur_read.len = rd_new.len_nxtfrm << 4;
rd_new.len = pnext->len;
rd_new.seq_num = rxseq++;
sdio_claim_host(bus->sdiodev->func[1]);
- errcode = brcmf_sdio_hdparser(bus, pnext->data, &rd_new,
- BRCMF_SDIO_FT_SUB);
+ errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new,
+ BRCMF_SDIO_FT_SUB);
sdio_release_host(bus->sdiodev->func[1]);
brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
pnext->data, 32, "subframe:\n");
skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
dptr = (u8 *) (pfirst->data);
sublen = get_unaligned_le16(dptr);
- doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+ doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]);
brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
dptr, pfirst->len,
uint rxleft = 0; /* Remaining number of frames allowed */
int ret; /* Return code from calls */
uint rxcount = 0; /* Total frames read */
- struct brcmf_sdio_read *rd = &bus->cur_read, rd_new;
+ struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new;
u8 head_read = 0;
brcmf_dbg(TRACE, "Enter\n");
bus->rxhdr, SDPCM_HDRLEN,
"RxHdr:\n");
- if (brcmf_sdio_hdparser(bus, bus->rxhdr, rd,
- BRCMF_SDIO_FT_NORMAL)) {
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd,
+ BRCMF_SDIO_FT_NORMAL)) {
sdio_release_host(bus->sdiodev->func[1]);
if (!bus->rxpending)
break;
memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
rd_new.seq_num = rd->seq_num;
sdio_claim_host(bus->sdiodev->func[1]);
- if (brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new,
- BRCMF_SDIO_FT_NORMAL)) {
+ if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new,
+ BRCMF_SDIO_FT_NORMAL)) {
rd->len = 0;
brcmu_pkt_buf_free_skb(pkt);
}
/* Save superframe descriptor and allocate packet frame */
if (rd->channel == SDPCM_GLOM_CHANNEL) {
- if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+ if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) {
brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
rd->len);
brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
return;
}
+/* flag marking a dummy skb added for DMA alignment requirement */
+#define DUMMY_SKB_FLAG 0x10000
+/* bit mask of data length chopped from the previous packet */
+#define DUMMY_SKB_CHOP_LEN_MASK 0xffff
+/**
+ * brcmf_sdio_txpkt_prep - packet preparation for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ * @chan: virtual channel to transmit the packet
+ *
+ * Processes to be applied to the packet
+ * - Align data buffer pointer
+ * - Align data buffer length
+ * - Prepare header
+ * Return: negative value if there is error
+ */
+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;
+ 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);
+
+ /* 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;
+ }
+ 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");
+
+ return 0;
+}
+
+/**
+ * brcmf_sdio_txpkt_postp - packet post processing for transmit
+ * @bus: brcmf_sdio structure pointer
+ * @pktq: packet list pointer
+ *
+ * Processes to be applied to the packet
+ * - Remove head padding
+ * - Remove tail padding
+ */
+static void
+brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
+{
+ u8 *hdr;
+ u32 dat_offset;
+ u32 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;
+ 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;
+ dat_offset = le32_to_cpu(*(__le32 *)hdr);
+ dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>
+ SDPCM_DOFFSET_SHIFT;
+ skb_pull(pkt_next, dat_offset);
+ }
+ }
+}
+
/* 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)
{
int ret;
- u8 *frame;
- u16 len, pad = 0;
- u32 swheader;
int i;
+ struct sk_buff_head localq;
brcmf_dbg(TRACE, "Enter\n");
- frame = (u8 *) (pkt->data);
-
- /* Add alignment padding, allocate new packet if needed */
- pad = ((unsigned long)frame % BRCMF_SDALIGN);
- if (pad) {
- if (skb_headroom(pkt) < pad) {
- brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
- skb_headroom(pkt), pad);
- bus->sdiodev->bus_if->tx_realloc++;
- ret = skb_cow(pkt, BRCMF_SDALIGN);
- if (ret)
- goto done;
- pad = ((unsigned long)frame % BRCMF_SDALIGN);
- }
- skb_push(pkt, pad);
- frame = (u8 *) (pkt->data);
- memset(frame, 0, pad + SDPCM_HDRLEN);
- }
- /* precondition: pad < BRCMF_SDALIGN */
-
- /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
- len = (u16) (pkt->len);
- *(__le16 *) frame = cpu_to_le16(len);
- *(((__le16 *) frame) + 1) = cpu_to_le16(~len);
-
- /* Software tag: channel, sequence number, data offset */
- swheader =
- ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
- (((pad +
- SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
-
- *(((__le32 *) frame) + 1) = cpu_to_le32(swheader);
- *(((__le32 *) frame) + 2) = 0;
-
-#ifdef DEBUG
- tx_packets[pkt->priority]++;
-#endif
-
- brcmf_dbg_hex_dump(BRCMF_BYTES_ON() &&
- ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
- (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)),
- frame, len, "Tx Frame:\n");
- brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
- ((BRCMF_CTL_ON() &&
- chan == SDPCM_CONTROL_CHANNEL) ||
- (BRCMF_DATA_ON() &&
- chan != SDPCM_CONTROL_CHANNEL))) &&
- BRCMF_HDRS_ON(),
- frame, min_t(u16, len, 16), "TxHdr:\n");
-
- /* Raise len to next SDIO block to eliminate tail command */
- 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);
- }
-
- /* Some controllers have trouble with odd bytes -- round to even */
- if (len & (ALIGNMENT - 1))
- len = roundup(len, ALIGNMENT);
+ __skb_queue_head_init(&localq);
+ __skb_queue_tail(&localq, pkt);
+ ret = brcmf_sdio_txpkt_prep(bus, &localq, 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, pkt);
+ SDIO_FUNC_2, F2SYNC, &localq);
bus->sdcnt.f2txdata++;
if (ret < 0) {
}
sdio_release_host(bus->sdiodev->func[1]);
if (ret == 0)
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
done:
- /* restore pkt buffer pointer before calling tx complete routine */
- skb_pull(pkt, SDPCM_HDRLEN + pad);
+ brcmf_sdio_txpkt_postp(bus, &localq);
+ __skb_dequeue_tail(&localq);
brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0);
return ret;
}
u32 intstatus = 0;
int ret = 0, prec_out;
uint cnt = 0;
- uint datalen;
u8 tx_prec_map;
brcmf_dbg(TRACE, "Enter\n");
break;
}
spin_unlock_bh(&bus->txqlock);
- datalen = pkt->len - SDPCM_HDRLEN;
ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL);
}
} else {
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
}
sdio_release_host(bus->sdiodev->func[1]);
bus->ctrl_frame_stat = false;
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;
/* Add space for the header */
- skb_push(pkt, SDPCM_HDRLEN);
+ skb_push(pkt, bus->tx_hdrlen);
/* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
prec = prio2prec((pkt->priority & PRIOMASK));
bus->sdcnt.fcqueued++;
/* Priority based enq */
- spin_lock_bh(&bus->txqlock);
+ spin_lock_irqsave(&bus->txqlock, flags);
if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
- skb_pull(pkt, SDPCM_HDRLEN);
- brcmf_txcomplete(bus->sdiodev->dev, pkt, false);
+ skb_pull(pkt, bus->tx_hdrlen);
brcmf_err("out of bus->txq !!!\n");
ret = -ENOSR;
} else {
bus->txoff = true;
brcmf_txflowblock(bus->sdiodev->dev, true);
}
- spin_unlock_bh(&bus->txqlock);
+ spin_unlock_irqrestore(&bus->txqlock, flags);
#ifdef DEBUG
if (pktq_plen(&bus->txq, prec) > qcount[prec])
return ret;
}
- bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+ bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;
return ret;
}
{
u8 *frame;
u16 len;
- u32 swheader;
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};
brcmf_dbg(TRACE, "Enter\n");
/* Back the pointer to make a room for bus header */
- frame = msg - SDPCM_HDRLEN;
- len = (msglen += SDPCM_HDRLEN);
+ frame = msg - bus->tx_hdrlen;
+ len = (msglen += bus->tx_hdrlen);
/* Add alignment padding (optional for ctl frames) */
doff = ((unsigned long)frame % BRCMF_SDALIGN);
frame -= doff;
len += doff;
msglen += doff;
- memset(frame, 0, doff + SDPCM_HDRLEN);
+ memset(frame, 0, doff + bus->tx_hdrlen);
}
/* precondition: doff < BRCMF_SDALIGN */
- doff += SDPCM_HDRLEN;
+ doff += bus->tx_hdrlen;
/* Round send length to next SDIO block */
if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
brcmf_sdbrcm_bus_sleep(bus, false, false);
sdio_release_host(bus->sdiodev->func[1]);
- /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
- *(__le16 *) frame = cpu_to_le16((u16) msglen);
- *(((__le16 *) frame) + 1) = cpu_to_le16(~msglen);
-
- /* Software tag: channel, sequence number, data offset */
- swheader =
- ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
- SDPCM_CHANNEL_MASK)
- | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
- SDPCM_DOFFSET_MASK);
- put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
- put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+ hd_info.len = (u16)msglen;
+ hd_info.channel = SDPCM_CONTROL_CHANNEL;
+ hd_info.dat_offset = doff;
+ brcmf_sdio_hdpack(bus, frame, &hd_info);
if (!data_ok(bus)) {
brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
struct brcmf_sdio *bus;
struct brcmf_bus_dcmd *dlst;
u32 dngl_txglom;
- u32 dngl_txglomalign;
+ u32 txglomalign = 0;
u8 idx;
brcmf_dbg(TRACE, "Enter\n");
bus->txbound = BRCMF_TXBOUND;
bus->rxbound = BRCMF_RXBOUND;
bus->txminmax = BRCMF_TXMINMAX;
- bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+ bus->tx_seq = SDPCM_SEQ_WRAP - 1;
INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);
bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");
bus->sdiodev->bus_if->chip = bus->ci->chip;
bus->sdiodev->bus_if->chiprev = bus->ci->chiprev;
- /* Attach to the brcmf/OS/network interface */
- ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev);
+ /* default sdio bus header length for tx packet */
+ 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);
if (ret != 0) {
brcmf_err("brcmf_attach failed\n");
goto fail;
dlst->param_len = sizeof(u32);
} else {
/* otherwise, set txglomalign */
- dngl_txglomalign = bus->sdiodev->bus_if->align;
+ 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 *)&dngl_txglomalign;
+ dlst->param = (char *)&txglomalign;
dlst->param_len = sizeof(u32);
}
list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list);
ifevent->action, ifevent->ifidx, ifevent->bssidx,
ifevent->flags, ifevent->role);
+ if (ifevent->flags & BRCMF_E_IF_FLAG_NOIF) {
+ brcmf_dbg(EVENT, "event can be ignored\n");
+ return;
+ }
if (ifevent->ifidx >= BRCMF_MAX_IFS) {
brcmf_err("invalid interface index: %u\n",
ifevent->ifidx);
__le32 enable;
};
+/**
+ * struct tdls_iovar - common structure for tdls iovars.
+ *
+ * @ea: ether address of peer station.
+ * @mode: mode value depending on specific tdls iovar.
+ * @chanspec: channel specification.
+ * @pad: unused (for future use).
+ */
+struct brcmf_tdls_iovar_le {
+ u8 ea[ETH_ALEN]; /* Station address */
+ u8 mode; /* mode: depends on iovar */
+ __le16 chanspec;
+ __le32 pad; /* future */
+};
+
+enum brcmf_tdls_manual_ep_ops {
+ BRCMF_TDLS_MANUAL_EP_CREATE = 1,
+ BRCMF_TDLS_MANUAL_EP_DELETE = 3,
+ BRCMF_TDLS_MANUAL_EP_DISCOVERY = 6
+};
+
#endif /* FWIL_TYPES_H_ */
struct brcmf_fws_info {
struct brcmf_pub *drvr;
+ spinlock_t spinlock;
+ ulong flags;
struct brcmf_fws_stats stats;
struct brcmf_fws_hanger hanger;
enum brcmf_fws_fcmode fcmode;
+ bool fw_signals;
bool bcmc_credit_check;
struct brcmf_fws_macdesc_table desc;
struct workqueue_struct *fws_wq;
}
#undef BRCMF_FWS_TLV_DEF
+static void brcmf_fws_lock(struct brcmf_fws_info *fws)
+ __acquires(&fws->spinlock)
+{
+ spin_lock_irqsave(&fws->spinlock, fws->flags);
+}
+
+static void brcmf_fws_unlock(struct brcmf_fws_info *fws)
+ __releases(&fws->spinlock)
+{
+ spin_unlock_irqrestore(&fws->spinlock, fws->flags);
+}
+
static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
{
u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
skcb->state = BRCMF_FWS_SKBSTATE_TIM;
bus = fws->drvr->bus_if;
err = brcmf_fws_hdrpush(fws, skb);
- if (err == 0)
+ if (err == 0) {
+ brcmf_fws_unlock(fws);
err = brcmf_bus_txdata(bus, skb);
+ brcmf_fws_lock(fws);
+ }
if (err)
brcmu_pkt_buf_free_skb(skb);
return true;
return 0;
}
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_lock(drvr, flags) \
-do { \
- flags = 0; \
- spin_lock_irqsave(&((drvr)->fws_spinlock), (flags)); \
-} while (0)
-
-/* using macro so sparse checking does not complain
- * about locking imbalance.
- */
-#define brcmf_fws_unlock(drvr, flags) \
- spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags))
-
static
int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data)
{
struct brcmf_fws_mac_descriptor *entry, *existing;
- ulong flags;
u8 mac_handle;
u8 ifidx;
u8 *addr;
if (entry->occupied) {
brcmf_dbg(TRACE, "deleting %s mac %pM\n",
entry->name, addr);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
brcmf_fws_macdesc_cleanup(fws, entry, -1);
brcmf_fws_macdesc_deinit(entry);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
} else
fws->stats.mac_update_failed++;
return 0;
existing = brcmf_fws_macdesc_lookup(fws, addr);
if (IS_ERR(existing)) {
if (!entry->occupied) {
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
entry->mac_handle = mac_handle;
brcmf_fws_macdesc_init(entry, addr, ifidx);
brcmf_fws_macdesc_set_name(fws, entry);
brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
brcmf_dbg(TRACE, "add %s mac %pM\n", entry->name, addr);
} else {
fws->stats.mac_update_failed++;
} else {
if (entry != existing) {
brcmf_dbg(TRACE, "copy mac %s\n", existing->name);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
memcpy(entry, existing,
offsetof(struct brcmf_fws_mac_descriptor, psq));
entry->mac_handle = mac_handle;
brcmf_fws_macdesc_deinit(existing);
brcmf_fws_macdesc_set_name(fws, entry);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
brcmf_dbg(TRACE, "relocate %s mac %pM\n", entry->name,
addr);
} else {
u8 type, u8 *data)
{
struct brcmf_fws_mac_descriptor *entry;
- ulong flags;
u8 mac_handle;
int ret;
fws->stats.mac_ps_update_failed++;
return -ESRCH;
}
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
/* a state update should wipe old credits */
entry->requested_credit = 0;
entry->requested_packet = 0;
brcmf_fws_tim_update(fws, entry, BRCMF_FWS_FIFO_AC_VO, true);
ret = BRCMF_FWS_RET_OK_NOSCHEDULE;
}
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return ret;
}
u8 type, u8 *data)
{
struct brcmf_fws_mac_descriptor *entry;
- ulong flags;
u8 ifidx;
int ret;
brcmf_dbg(TRACE, "%s (%d): %s\n", brcmf_fws_get_tlv_name(type), type,
entry->name);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
switch (type) {
case BRCMF_FWS_TYPE_INTERFACE_OPEN:
entry->state = BRCMF_FWS_STATE_OPEN;
break;
default:
ret = -EINVAL;
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
goto fail;
}
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return ret;
fail:
u8 *data)
{
struct brcmf_fws_mac_descriptor *entry;
- ulong flags;
entry = &fws->desc.nodes[data[1] & 0x1F];
if (!entry->occupied) {
brcmf_dbg(TRACE, "%s (%d): %s cnt %d bmp %d\n",
brcmf_fws_get_tlv_name(type), type, entry->name,
data[0], data[2]);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT)
entry->requested_credit = data[0];
else
entry->requested_packet = data[0];
entry->ac_bitmap = data[2];
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_SCHEDULE;
}
static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws)
{
/* only schedule dequeue when there are credits for delayed traffic */
- if (fws->fifo_credit_map & fws->fifo_delay_map)
+ if ((fws->fifo_credit_map & fws->fifo_delay_map) ||
+ (!brcmf_fws_fc_active(fws) && fws->fifo_delay_map))
queue_work(fws->fws_wq, &fws->fws_dequeue_work);
}
static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws,
u8 *data)
{
- ulong flags;
int i;
if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) {
}
brcmf_dbg(DATA, "enter: data %pM\n", data);
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++)
brcmf_fws_return_credits(fws, i, data[i]);
brcmf_dbg(DATA, "map: credit %x delay %x\n", fws->fifo_credit_map,
fws->fifo_delay_map);
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_SCHEDULE;
}
static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data)
{
- ulong lflags;
__le32 status_le;
u32 status;
u32 hslot;
hslot = brcmf_txstatus_get_field(status, HSLOT);
genbit = brcmf_txstatus_get_field(status, GENERATION);
- brcmf_fws_lock(fws->drvr, lflags);
+ brcmf_fws_lock(fws);
brcmf_fws_txs_process(fws, flags, hslot, genbit);
- brcmf_fws_unlock(fws->drvr, lflags);
+ brcmf_fws_unlock(fws);
return BRCMF_FWS_RET_OK_NOSCHEDULE;
}
{
struct brcmf_fws_info *fws = ifp->drvr->fws;
int i;
- ulong flags;
u8 *credits = data;
if (e->datalen < BRCMF_FWS_FIFO_COUNT) {
fws->creditmap_received = true;
brcmf_dbg(TRACE, "enter: credits %pM\n", credits);
- brcmf_fws_lock(ifp->drvr, flags);
+ brcmf_fws_lock(fws);
for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) {
if (*credits)
fws->fifo_credit_map |= 1 << i;
fws->fifo_credit[i] = *credits++;
}
brcmf_fws_schedule_deq(fws);
- brcmf_fws_unlock(ifp->drvr, flags);
+ brcmf_fws_unlock(fws);
return 0;
}
void *data)
{
struct brcmf_fws_info *fws = ifp->drvr->fws;
- ulong flags;
- brcmf_fws_lock(ifp->drvr, flags);
+ brcmf_fws_lock(fws);
if (fws)
fws->bcmc_credit_check = true;
- brcmf_fws_unlock(ifp->drvr, flags);
+ brcmf_fws_unlock(fws);
return 0;
}
int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
struct sk_buff *skb)
{
+ struct brcmf_skb_reorder_data *rd;
struct brcmf_fws_info *fws = drvr->fws;
u8 *signal_data;
s16 data_len;
WARN_ON(signal_len > skb->len);
+ if (!signal_len)
+ return 0;
/* if flow control disabled, skip to packet data and leave */
- if (!signal_len || !drvr->fw_signals) {
+ if (!fws->fw_signals) {
skb_pull(skb, signal_len);
return 0;
}
err = BRCMF_FWS_RET_OK_NOSCHEDULE;
switch (type) {
- case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
case BRCMF_FWS_TYPE_COMP_TXSTATUS:
break;
+ case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS:
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+ rd->reorder = data;
+ break;
case BRCMF_FWS_TYPE_MACDESC_ADD:
case BRCMF_FWS_TYPE_MACDESC_DEL:
brcmf_fws_macdesc_indicate(fws, type, data);
return PTR_ERR(entry);
brcmf_fws_precommit_skb(fws, fifo, skb);
+ entry->transit_count++;
+ if (entry->suppressed)
+ entry->suppr_transit_count++;
+ brcmf_fws_unlock(fws);
rc = brcmf_bus_txdata(bus, 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);
if (rc < 0) {
+ entry->transit_count--;
+ if (entry->suppressed)
+ entry->suppr_transit_count--;
brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
goto rollback;
}
- entry->transit_count++;
- if (entry->suppressed)
- entry->suppr_transit_count++;
fws->stats.pkt2bus++;
fws->stats.send_pkts[fifo]++;
if (brcmf_skb_if_flags_get_field(skb, REQUESTED))
struct brcmf_fws_info *fws = drvr->fws;
struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
struct ethhdr *eh = (struct ethhdr *)(skb->data);
- ulong flags;
int fifo = BRCMF_FWS_FIFO_BCMC;
bool multicast = is_multicast_ether_addr(eh->h_dest);
bool pae = eh->h_proto == htons(ETH_P_PAE);
+ brcmf_dbg(DATA, "tx proto=0x%X\n", ntohs(eh->h_proto));
/* determine the priority */
if (!skb->priority)
skb->priority = cfg80211_classify8021d(skb);
if (pae)
atomic_inc(&ifp->pend_8021x_cnt);
- if (!brcmf_fws_fc_active(fws)) {
- /* If the protocol uses a data header, apply it */
- brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb);
-
- /* Use bus module to send data frame */
- return brcmf_bus_txdata(drvr->bus_if, skb);
- }
-
/* set control buffer information */
skcb->if_flags = 0;
skcb->state = BRCMF_FWS_SKBSTATE_NEW;
if (!multicast)
fifo = brcmf_fws_prio2fifo[skb->priority];
- brcmf_fws_lock(drvr, flags);
+ brcmf_fws_lock(fws);
if (fifo != BRCMF_FWS_FIFO_AC_BE && fifo < BRCMF_FWS_FIFO_BCMC)
fws->borrow_defer_timestamp = jiffies +
BRCMF_FWS_BORROW_DEFER_PERIOD;
}
brcmu_pkt_buf_free_skb(skb);
}
- brcmf_fws_unlock(drvr, flags);
+ brcmf_fws_unlock(fws);
return 0;
}
struct brcmf_fws_info *fws = ifp->drvr->fws;
struct brcmf_fws_mac_descriptor *entry;
- if (!ifp->ndev || !ifp->drvr->fw_signals)
+ if (!ifp->ndev)
return;
entry = &fws->desc.iface[ifp->ifidx];
void brcmf_fws_del_interface(struct brcmf_if *ifp)
{
struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc;
- ulong flags;
if (!entry)
return;
- brcmf_fws_lock(ifp->drvr, flags);
+ brcmf_fws_lock(ifp->drvr->fws);
ifp->fws_desc = NULL;
brcmf_dbg(TRACE, "deleting %s\n", entry->name);
brcmf_fws_macdesc_deinit(entry);
brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx);
- brcmf_fws_unlock(ifp->drvr, flags);
+ brcmf_fws_unlock(ifp->drvr->fws);
}
static void brcmf_fws_dequeue_worker(struct work_struct *worker)
{
struct brcmf_fws_info *fws;
+ struct brcmf_pub *drvr;
struct sk_buff *skb;
- ulong flags;
int fifo;
+ u32 hslot;
+ u32 ifidx;
+ int ret;
fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
+ drvr = fws->drvr;
- brcmf_fws_lock(fws->drvr, flags);
+ brcmf_fws_lock(fws);
for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
fifo--) {
+ if (!brcmf_fws_fc_active(fws)) {
+ while ((skb = brcmf_fws_deq(fws, fifo)) != NULL) {
+ hslot = brcmf_skb_htod_tag_get_field(skb,
+ HSLOT);
+ brcmf_fws_hanger_poppkt(&fws->hanger, hslot,
+ &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 */
+ brcmf_fws_unlock(fws);
+ ret = brcmf_bus_txdata(drvr->bus_if, skb);
+ brcmf_fws_lock(fws);
+ if (ret < 0)
+ brcmf_txfinalize(drvr, skb, false);
+ if (fws->bus_flow_blocked)
+ break;
+ }
+ continue;
+ }
while ((fws->fifo_credit[fifo]) || ((!fws->bcmc_credit_check) &&
(fifo == BRCMF_FWS_FIFO_BCMC))) {
skb = brcmf_fws_deq(fws, fifo);
}
}
}
- brcmf_fws_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
}
int brcmf_fws_init(struct brcmf_pub *drvr)
{
+ struct brcmf_fws_info *fws;
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
int rc;
- if (!drvr->fw_signals)
- return 0;
-
- spin_lock_init(&drvr->fws_spinlock);
-
drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL);
if (!drvr->fws) {
rc = -ENOMEM;
goto fail;
}
+ fws = drvr->fws;
+
+ spin_lock_init(&fws->spinlock);
+
/* set linkage back */
- drvr->fws->drvr = drvr;
- drvr->fws->fcmode = fcmode;
+ fws->drvr = drvr;
+ fws->fcmode = fcmode;
- drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
- if (drvr->fws->fws_wq == NULL) {
+ fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq");
+ if (fws->fws_wq == NULL) {
brcmf_err("workqueue creation failed\n");
rc = -EBADF;
goto fail;
}
- INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
+ INIT_WORK(&fws->fws_dequeue_work, brcmf_fws_dequeue_worker);
/* enable firmware signalling if fcmode active */
- if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE)
+ if (fws->fcmode != BRCMF_FWS_FCMODE_NONE)
tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS |
BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS |
- BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE;
+ BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE |
+ BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE;
rc = brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP,
brcmf_fws_notify_credit_map);
goto fail;
}
- /* setting the iovar may fail if feature is unsupported
+ /* Setting the iovar may fail if feature is unsupported
* so leave the rc as is so driver initialization can
- * continue.
+ * continue. Set mode back to none indicating not enabled.
*/
+ fws->fw_signals = true;
if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) {
brcmf_err("failed to set bdcv2 tlv signaling\n");
- goto fail_event;
+ fws->fcmode = BRCMF_FWS_FCMODE_NONE;
+ fws->fw_signals = false;
}
- brcmf_fws_hanger_init(&drvr->fws->hanger);
- brcmf_fws_macdesc_init(&drvr->fws->desc.other, NULL, 0);
- brcmf_fws_macdesc_set_name(drvr->fws, &drvr->fws->desc.other);
- brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
+ if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1))
+ brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n");
+
+ brcmf_fws_hanger_init(&fws->hanger);
+ brcmf_fws_macdesc_init(&fws->desc.other, NULL, 0);
+ brcmf_fws_macdesc_set_name(fws, &fws->desc.other);
+ brcmu_pktq_init(&fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT,
BRCMF_FWS_PSQ_LEN);
/* create debugfs file for statistics */
- brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats);
+ brcmf_debugfs_create_fws_stats(drvr, &fws->stats);
brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n",
- drvr->fw_signals ? "enabled" : "disabled", tlv);
+ fws->fw_signals ? "enabled" : "disabled", tlv);
return 0;
-fail_event:
- brcmf_fweh_unregister(drvr, BRCMF_E_BCMC_CREDIT_SUPPORT);
- brcmf_fweh_unregister(drvr, BRCMF_E_FIFO_CREDIT_MAP);
fail:
brcmf_fws_deinit(drvr);
return rc;
void brcmf_fws_deinit(struct brcmf_pub *drvr)
{
struct brcmf_fws_info *fws = drvr->fws;
- ulong flags;
if (!fws)
return;
- /* disable firmware signalling entirely
- * to avoid using the workqueue.
- */
- drvr->fw_signals = false;
-
if (drvr->fws->fws_wq)
destroy_workqueue(drvr->fws->fws_wq);
/* cleanup */
- brcmf_fws_lock(drvr, flags);
+ brcmf_fws_lock(fws);
brcmf_fws_cleanup(fws, -1);
drvr->fws = NULL;
- brcmf_fws_unlock(drvr, flags);
+ brcmf_fws_unlock(fws);
/* free top structure */
kfree(fws);
bool brcmf_fws_fc_active(struct brcmf_fws_info *fws)
{
- if (!fws)
+ if (!fws->creditmap_received)
return false;
return fws->fcmode != BRCMF_FWS_FCMODE_NONE;
void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb)
{
- ulong flags;
u32 hslot;
if (brcmf_skbcb(skb)->state == BRCMF_FWS_SKBSTATE_TIM) {
brcmu_pkt_buf_free_skb(skb);
return;
}
- brcmf_fws_lock(fws->drvr, flags);
+ 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_unlock(fws->drvr, flags);
+ brcmf_fws_unlock(fws);
}
void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked)
*/
extern int
brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
- uint flags, struct sk_buff *pkt);
+ 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);
return 0;
fail:
- brcmf_txcomplete(dev, skb, false);
return ret;
}
}
#ifdef CONFIG_NL80211_TESTMODE
-static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
+static int brcmf_cfg80211_testmode(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ void *data, int len)
{
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = cfg_to_ndev(cfg);
clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
}
+static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
+{
+ int ret;
+
+ switch (oper) {
+ case NL80211_TDLS_DISCOVERY_REQ:
+ ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
+ break;
+ case NL80211_TDLS_SETUP:
+ ret = BRCMF_TDLS_MANUAL_EP_CREATE;
+ break;
+ case NL80211_TDLS_TEARDOWN:
+ ret = BRCMF_TDLS_MANUAL_EP_DELETE;
+ break;
+ default:
+ brcmf_err("unsupported operation: %d\n", oper);
+ ret = -EOPNOTSUPP;
+ }
+ return ret;
+}
+
+static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
+ struct net_device *ndev, u8 *peer,
+ enum nl80211_tdls_operation oper)
+{
+ struct brcmf_if *ifp;
+ struct brcmf_tdls_iovar_le info;
+ int ret = 0;
+
+ ret = brcmf_convert_nl80211_tdls_oper(oper);
+ if (ret < 0)
+ return ret;
+
+ ifp = netdev_priv(ndev);
+ memset(&info, 0, sizeof(info));
+ info.mode = (u8)ret;
+ if (peer)
+ memcpy(info.ea, peer, ETH_ALEN);
+
+ ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
+ &info, sizeof(info));
+ if (ret < 0)
+ brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
+
+ return ret;
+}
+
static struct cfg80211_ops wl_cfg80211_ops = {
.add_virtual_intf = brcmf_cfg80211_add_iface,
.del_virtual_intf = brcmf_cfg80211_del_iface,
.stop_p2p_device = brcmf_p2p_stop_device,
.crit_proto_start = brcmf_cfg80211_crit_proto_start,
.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
-#ifdef CONFIG_NL80211_TESTMODE
- .testmode_cmd = brcmf_cfg80211_testmode
-#endif
+ .tdls_oper = brcmf_cfg80211_tdls_oper,
+ CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
};
static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type)
wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
WIPHY_FLAG_OFFCHAN_TX |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
+ WIPHY_FLAG_SUPPORTS_TDLS;
wiphy->mgmt_stypes = brcmf_txrx_stypes;
wiphy->max_remain_on_channel_duration = 5000;
brcmf_wiphy_pno_params(wiphy);
goto cfg80211_p2p_attach_out;
}
+ err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
+ if (err) {
+ brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
+ wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
+ }
+
err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
&io_type);
if (err) {
return mode == BCMA_CLKMODE_FAST;
}
-void ai_pci_up(struct si_pub *sih)
-{
- struct si_info *sii;
-
- sii = container_of(sih, struct si_info, pub);
-
- if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI)
- bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], true);
-}
-
-/* Unconfigure and/or apply various WARs when going down */
-void ai_pci_down(struct si_pub *sih)
-{
- struct si_info *sii;
-
- sii = container_of(sih, struct si_info, pub);
-
- if (sii->icbus->hosttype == BCMA_HOSTTYPE_PCI)
- bcma_core_pci_extend_L1timer(&sii->icbus->drv_pci[0], false);
-}
-
/* Enable BT-COEX & Ex-PA for 4313 */
void ai_epa_4313war(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);
-extern void ai_pci_down(struct si_pub *sih);
-extern void ai_pci_up(struct si_pub *sih);
-
/* Enable Ex-PA for 4313 */
extern void ai_epa_4313war(struct si_pub *sih);
}
} else if (txs->phyerr) {
update_rate = false;
- brcms_err(wlc->hw->d11core,
- "%s: ampdu tx phy error (0x%x)\n",
- __func__, txs->phyerr);
+ brcms_dbg_ht(wlc->hw->d11core,
+ "%s: ampdu tx phy error (0x%x)\n",
+ __func__, txs->phyerr);
}
}
/*
* post receive buffers
- * return false is refill failed completely and ring is empty this will stall
- * the rx dma and user might want to call rxfill again asap. This unlikely
- * happens on memory-rich NIC, but often on memory-constrained dongle
+ * Return false if refill failed completely or dma mapping failed. The ring
+ * is empty, which will stall the rx dma and user might want to call rxfill
+ * again asap. This is unlikely to happen on a memory-rich NIC, but often on
+ * memory-constrained dongle.
*/
bool dma_rxfill(struct dma_pub *pub)
{
pa = dma_map_single(di->dmadev, p->data, di->rxbufsize,
DMA_FROM_DEVICE);
+ if (dma_mapping_error(di->dmadev, pa))
+ return false;
/* save the free packet pointer */
di->rxp[rxout] = p;
/* get physical address of buffer start */
pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE);
-
+ /* if mapping failed, free skb */
+ if (dma_mapping_error(di->dmadev, pa)) {
+ brcmu_pkt_buf_free_skb(p);
+ return;
+ }
/* With a DMA segment list, Descriptor table is filled
* using the segment list instead of looping over
* buffers in multi-chain DMA. Therefore, EOF for SGLIST
mcl = le16_to_cpu(txh->MacTxControlLow);
if (txs->phyerr)
- brcms_err(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
- txs->phyerr, txh->MainRates);
+ brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
+ txs->phyerr, txh->MainRates);
if (txs->frameid != le16_to_cpu(txh->TxFrameID)) {
brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n");
wlc->band->phyrev = wlc_hw->band->phyrev;
wlc->band->radioid = wlc_hw->band->radioid;
wlc->band->radiorev = wlc_hw->band->radiorev;
-
+ brcms_dbg_info(core, "wl%d: phy %u/%u radio %x/%u\n", unit,
+ wlc->band->phytype, wlc->band->phyrev,
+ wlc->band->radioid, wlc->band->radiorev);
/* default contention windows size limits */
wlc_hw->band->CWmin = APHY_CWMIN;
wlc_hw->band->CWmax = PHY_CWMAX;
brcms_c_coredisable(wlc_hw);
/* Match driver "down" state */
- ai_pci_down(wlc_hw->sih);
+ bcma_core_pci_down(wlc_hw->d11core->bus);
/* turn off pll and xtal to match driver "down" state */
brcms_b_xtal(wlc_hw, OFF);
*/
if (brcms_b_radio_read_hwdisabled(wlc_hw)) {
/* put SB PCI in down state again */
- ai_pci_down(wlc_hw->sih);
+ bcma_core_pci_down(wlc_hw->d11core->bus);
brcms_b_xtal(wlc_hw, OFF);
return -ENOMEDIUM;
}
- ai_pci_up(wlc_hw->sih);
+ bcma_core_pci_up(wlc_hw->d11core->bus);
/* reset the d11 core */
brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
/* turn off primary xtal and pll */
if (!wlc_hw->noreset) {
- ai_pci_down(wlc_hw->sih);
+ bcma_core_pci_down(wlc_hw->d11core->bus);
brcms_b_xtal(wlc_hw, OFF);
}
}
gain0_15 = ((biq1 & 0xf) << 12) |
((tia & 0xf) << 8) |
((lna2 & 0x3) << 6) |
- ((lna2 &
- 0x3) << 4) | ((lna1 & 0x3) << 2) | ((lna1 & 0x3) << 0);
+ ((lna2 & 0x3) << 4) |
+ ((lna1 & 0x3) << 2) |
+ ((lna1 & 0x3) << 0);
mod_phy_reg(pi, 0x4b6, (0xffff << 0), gain0_15 << 0);
mod_phy_reg(pi, 0x4b7, (0xf << 0), gain16_19 << 0);
return (iq_est.i_pwr + iq_est.q_pwr) / nsamples;
}
+static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,
+ u16 tia_gain, u16 lna2_gain)
+{
+ u32 i_thresh_l, q_thresh_l;
+ u32 i_thresh_h, q_thresh_h;
+ struct lcnphy_iq_est iq_est_h, iq_est_l;
+
+ wlc_lcnphy_set_rx_gain_by_distribution(pi, 0, 0, 0, biq1_gain, tia_gain,
+ lna2_gain, 0);
+
+ wlc_lcnphy_rx_gain_override_enable(pi, true);
+ wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0);
+ udelay(500);
+ write_radio_reg(pi, RADIO_2064_REG112, 0);
+ if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))
+ return false;
+
+ wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0);
+ udelay(500);
+ write_radio_reg(pi, RADIO_2064_REG112, 0);
+ if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))
+ return false;
+
+ i_thresh_l = (iq_est_l.i_pwr << 1);
+ i_thresh_h = (iq_est_l.i_pwr << 2) + iq_est_l.i_pwr;
+
+ q_thresh_l = (iq_est_l.q_pwr << 1);
+ q_thresh_h = (iq_est_l.q_pwr << 2) + iq_est_l.q_pwr;
+ if ((iq_est_h.i_pwr > i_thresh_l) &&
+ (iq_est_h.i_pwr < i_thresh_h) &&
+ (iq_est_h.q_pwr > q_thresh_l) &&
+ (iq_est_h.q_pwr < q_thresh_h))
+ return true;
+
+ return false;
+}
+
static bool
wlc_lcnphy_rx_iq_cal(struct brcms_phy *pi,
const struct lcnphy_rx_iqcomp *iqcomp,
RFOverrideVal0_old, rfoverride2_old, rfoverride2val_old,
rfoverride3_old, rfoverride3val_old, rfoverride4_old,
rfoverride4val_old, afectrlovr_old, afectrlovrval_old;
- int tia_gain;
- u32 received_power, rx_pwr_threshold;
+ int tia_gain, lna2_gain, biq1_gain;
+ bool set_gain;
u16 old_sslpnCalibClkEnCtrl, old_sslpnRxFeClkEnCtrl;
u16 values_to_save[11];
s16 *ptr;
goto cal_done;
}
- if (module == 1) {
+ WARN_ON(module != 1);
+ tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
- tx_pwr_ctrl = wlc_lcnphy_get_tx_pwr_ctrl(pi);
- wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF);
+ for (i = 0; i < 11; i++)
+ values_to_save[i] =
+ read_radio_reg(pi, rxiq_cal_rf_reg[i]);
+ Core1TxControl_old = read_phy_reg(pi, 0x631);
+
+ or_phy_reg(pi, 0x631, 0x0015);
+
+ RFOverride0_old = read_phy_reg(pi, 0x44c);
+ RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
+ rfoverride2_old = read_phy_reg(pi, 0x4b0);
+ rfoverride2val_old = read_phy_reg(pi, 0x4b1);
+ rfoverride3_old = read_phy_reg(pi, 0x4f9);
+ rfoverride3val_old = read_phy_reg(pi, 0x4fa);
+ rfoverride4_old = read_phy_reg(pi, 0x938);
+ rfoverride4val_old = read_phy_reg(pi, 0x939);
+ afectrlovr_old = read_phy_reg(pi, 0x43b);
+ afectrlovrval_old = read_phy_reg(pi, 0x43c);
+ old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
+ old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
- for (i = 0; i < 11; i++)
- values_to_save[i] =
- read_radio_reg(pi, rxiq_cal_rf_reg[i]);
- Core1TxControl_old = read_phy_reg(pi, 0x631);
-
- or_phy_reg(pi, 0x631, 0x0015);
-
- RFOverride0_old = read_phy_reg(pi, 0x44c);
- RFOverrideVal0_old = read_phy_reg(pi, 0x44d);
- rfoverride2_old = read_phy_reg(pi, 0x4b0);
- rfoverride2val_old = read_phy_reg(pi, 0x4b1);
- rfoverride3_old = read_phy_reg(pi, 0x4f9);
- rfoverride3val_old = read_phy_reg(pi, 0x4fa);
- rfoverride4_old = read_phy_reg(pi, 0x938);
- rfoverride4val_old = read_phy_reg(pi, 0x939);
- afectrlovr_old = read_phy_reg(pi, 0x43b);
- afectrlovrval_old = read_phy_reg(pi, 0x43c);
- old_sslpnCalibClkEnCtrl = read_phy_reg(pi, 0x6da);
- old_sslpnRxFeClkEnCtrl = read_phy_reg(pi, 0x6db);
-
- tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
- if (tx_gain_override_old) {
- wlc_lcnphy_get_tx_gain(pi, &old_gains);
- tx_gain_index_old = pi_lcn->lcnphy_current_index;
- }
+ tx_gain_override_old = wlc_lcnphy_tx_gain_override_enabled(pi);
+ if (tx_gain_override_old) {
+ wlc_lcnphy_get_tx_gain(pi, &old_gains);
+ tx_gain_index_old = pi_lcn->lcnphy_current_index;
+ }
- wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
+ wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_idx);
- mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
+ mod_phy_reg(pi, 0x4f9, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x4fa, (0x1 << 0), 0 << 0);
- mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
+ mod_phy_reg(pi, 0x43b, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x43c, (0x1 << 1), 0 << 1);
- write_radio_reg(pi, RADIO_2064_REG116, 0x06);
- write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
- write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
- write_radio_reg(pi, RADIO_2064_REG098, 0x03);
- write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
- mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
- write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
- write_radio_reg(pi, RADIO_2064_REG114, 0x01);
- write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
- write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
-
- mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
- mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
- mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
- mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
- mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
- mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
- mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
- mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
-
- mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
- mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
-
- wlc_lcnphy_start_tx_tone(pi, 2000, 120, 0);
- write_phy_reg(pi, 0x6da, 0xffff);
- or_phy_reg(pi, 0x6db, 0x3);
- wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
- wlc_lcnphy_rx_gain_override_enable(pi, true);
-
- tia_gain = 8;
- rx_pwr_threshold = 950;
- while (tia_gain > 0) {
- tia_gain -= 1;
- wlc_lcnphy_set_rx_gain_by_distribution(pi,
- 0, 0, 2, 2,
- (u16)
- tia_gain, 1, 0);
- udelay(500);
+ write_radio_reg(pi, RADIO_2064_REG116, 0x06);
+ write_radio_reg(pi, RADIO_2064_REG12C, 0x07);
+ write_radio_reg(pi, RADIO_2064_REG06A, 0xd3);
+ write_radio_reg(pi, RADIO_2064_REG098, 0x03);
+ write_radio_reg(pi, RADIO_2064_REG00B, 0x7);
+ mod_radio_reg(pi, RADIO_2064_REG113, 1 << 4, 1 << 4);
+ write_radio_reg(pi, RADIO_2064_REG01D, 0x01);
+ write_radio_reg(pi, RADIO_2064_REG114, 0x01);
+ write_radio_reg(pi, RADIO_2064_REG02E, 0x10);
+ write_radio_reg(pi, RADIO_2064_REG12A, 0x08);
+
+ mod_phy_reg(pi, 0x938, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x939, (0x1 << 0), 0 << 0);
+ mod_phy_reg(pi, 0x938, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x939, (0x1 << 1), 1 << 1);
+ mod_phy_reg(pi, 0x938, (0x1 << 2), 1 << 2);
+ mod_phy_reg(pi, 0x939, (0x1 << 2), 1 << 2);
+ mod_phy_reg(pi, 0x938, (0x1 << 3), 1 << 3);
+ mod_phy_reg(pi, 0x939, (0x1 << 3), 1 << 3);
+ mod_phy_reg(pi, 0x938, (0x1 << 5), 1 << 5);
+ mod_phy_reg(pi, 0x939, (0x1 << 5), 0 << 5);
- received_power =
- wlc_lcnphy_measure_digital_power(pi, 2000);
- if (received_power < rx_pwr_threshold)
- break;
- }
- result = wlc_lcnphy_calc_rx_iq_comp(pi, 0xffff);
+ mod_phy_reg(pi, 0x43b, (0x1 << 0), 1 << 0);
+ mod_phy_reg(pi, 0x43c, (0x1 << 0), 0 << 0);
- wlc_lcnphy_stop_tx_tone(pi);
+ write_phy_reg(pi, 0x6da, 0xffff);
+ or_phy_reg(pi, 0x6db, 0x3);
+
+ wlc_lcnphy_set_trsw_override(pi, tx_switch, rx_switch);
+ for (lna2_gain = 3; lna2_gain >= 0; lna2_gain--) {
+ for (tia_gain = 4; tia_gain >= 0; tia_gain--) {
+ for (biq1_gain = 6; biq1_gain >= 0; biq1_gain--) {
+ set_gain = wlc_lcnphy_rx_iq_cal_gain(pi,
+ (u16)
+ biq1_gain,
+ (u16)
+ tia_gain,
+ (u16)
+ lna2_gain);
+ if (!set_gain)
+ continue;
+
+ result = wlc_lcnphy_calc_rx_iq_comp(pi, 1024);
+ goto stop_tone;
+ }
+ }
+ }
- write_phy_reg(pi, 0x631, Core1TxControl_old);
+stop_tone:
+ wlc_lcnphy_stop_tx_tone(pi);
- write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
- write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
- write_phy_reg(pi, 0x4b0, rfoverride2_old);
- write_phy_reg(pi, 0x4b1, rfoverride2val_old);
- write_phy_reg(pi, 0x4f9, rfoverride3_old);
- write_phy_reg(pi, 0x4fa, rfoverride3val_old);
- write_phy_reg(pi, 0x938, rfoverride4_old);
- write_phy_reg(pi, 0x939, rfoverride4val_old);
- write_phy_reg(pi, 0x43b, afectrlovr_old);
- write_phy_reg(pi, 0x43c, afectrlovrval_old);
- write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
- write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
+ write_phy_reg(pi, 0x631, Core1TxControl_old);
+
+ write_phy_reg(pi, 0x44c, RFOverrideVal0_old);
+ write_phy_reg(pi, 0x44d, RFOverrideVal0_old);
+ write_phy_reg(pi, 0x4b0, rfoverride2_old);
+ write_phy_reg(pi, 0x4b1, rfoverride2val_old);
+ write_phy_reg(pi, 0x4f9, rfoverride3_old);
+ write_phy_reg(pi, 0x4fa, rfoverride3val_old);
+ write_phy_reg(pi, 0x938, rfoverride4_old);
+ write_phy_reg(pi, 0x939, rfoverride4val_old);
+ write_phy_reg(pi, 0x43b, afectrlovr_old);
+ write_phy_reg(pi, 0x43c, afectrlovrval_old);
+ write_phy_reg(pi, 0x6da, old_sslpnCalibClkEnCtrl);
+ write_phy_reg(pi, 0x6db, old_sslpnRxFeClkEnCtrl);
- wlc_lcnphy_clear_trsw_override(pi);
+ wlc_lcnphy_clear_trsw_override(pi);
- mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
+ mod_phy_reg(pi, 0x44c, (0x1 << 2), 0 << 2);
- for (i = 0; i < 11; i++)
- write_radio_reg(pi, rxiq_cal_rf_reg[i],
- values_to_save[i]);
+ for (i = 0; i < 11; i++)
+ write_radio_reg(pi, rxiq_cal_rf_reg[i],
+ values_to_save[i]);
- if (tx_gain_override_old)
- wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
- else
- wlc_lcnphy_disable_tx_gain_override(pi);
+ if (tx_gain_override_old)
+ wlc_lcnphy_set_tx_pwr_by_index(pi, tx_gain_index_old);
+ else
+ wlc_lcnphy_disable_tx_gain_override(pi);
- wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
- wlc_lcnphy_rx_gain_override_enable(pi, false);
- }
+ wlc_lcnphy_set_tx_pwr_ctrl(pi, tx_pwr_ctrl);
+ wlc_lcnphy_rx_gain_override_enable(pi, false);
cal_done:
kfree(ptr);
write_radio_reg(pi, RADIO_2064_REG038, 3);
write_radio_reg(pi, RADIO_2064_REG091, 7);
}
+
+ if (!(pi->sh->boardflags & BFL_FEM)) {
+ static const u8 reg038[14] = {
+ 0xd, 0xe, 0xd, 0xd, 0xd, 0xc, 0xa,
+ 0xb, 0xb, 0x3, 0x3, 0x2, 0x0, 0x0
+ };
+
+ write_radio_reg(pi, RADIO_2064_REG02A, 0xf);
+ write_radio_reg(pi, RADIO_2064_REG091, 0x3);
+ write_radio_reg(pi, RADIO_2064_REG038, 0x3);
+
+ write_radio_reg(pi, RADIO_2064_REG038, reg038[channel - 1]);
+ }
}
static int
} else {
mod_radio_reg(pi, RADIO_2064_REG03A, 1, 0x1);
mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG11A, 0x4, 1<<2);
+ mod_radio_reg(pi, RADIO_2064_REG036, 0x10, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG11A, 0x10, 1<<4);
+ mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x77);
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, 0xe<<1);
+ mod_radio_reg(pi, RADIO_2064_REG112, 0x80, 1<<7);
+ mod_radio_reg(pi, RADIO_2064_REG005, 0x7, 1<<1);
+ mod_radio_reg(pi, RADIO_2064_REG029, 0xf0, 0<<4);
}
} else {
mod_phy_reg(pi, 0x4d9, (0x1 << 2), (0x1) << 2);
(auxpga_vmid_temp << 0) | (auxpga_gain_temp << 12));
mod_radio_reg(pi, RADIO_2064_REG082, (1 << 5), (1 << 5));
+ mod_radio_reg(pi, RADIO_2064_REG07C, (1 << 0), (1 << 0));
}
static void wlc_lcnphy_tssi_setup(struct brcms_phy *pi)
{
struct phytbl_info tab;
u32 rfseq, ind;
+ enum lcnphy_tssi_mode mode;
+ u8 tssi_sel;
+ if (pi->sh->boardflags & BFL_FEM) {
+ tssi_sel = 0x1;
+ mode = LCNPHY_TSSI_EXT;
+ } else {
+ tssi_sel = 0xe;
+ mode = LCNPHY_TSSI_POST_PA;
+ }
tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
tab.tbl_width = 32;
tab.tbl_ptr = &ind;
mod_phy_reg(pi, 0x503, (0x1 << 4), (1) << 4);
- wlc_lcnphy_set_tssi_mux(pi, LCNPHY_TSSI_EXT);
+ wlc_lcnphy_set_tssi_mux(pi, mode);
mod_phy_reg(pi, 0x4a4, (0x1 << 14), (0) << 14);
mod_phy_reg(pi, 0x4a4, (0x1 << 15), (1) << 15);
mod_phy_reg(pi, 0x49a, (0x1ff << 0), (0xff) << 0);
if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
- mod_radio_reg(pi, RADIO_2064_REG028, 0xf, 0xe);
+ mod_radio_reg(pi, RADIO_2064_REG028, 0xf, tssi_sel);
mod_radio_reg(pi, RADIO_2064_REG086, 0x4, 0x4);
} else {
+ mod_radio_reg(pi, RADIO_2064_REG028, 0x1e, tssi_sel << 1);
mod_radio_reg(pi, RADIO_2064_REG03A, 0x1, 1);
mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 1 << 3);
}
mod_phy_reg(pi, 0x4d7, (0xf << 8), (0) << 8);
+ mod_radio_reg(pi, RADIO_2064_REG035, 0xff, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG036, 0x3, 0x0);
+ mod_radio_reg(pi, RADIO_2064_REG11A, 0x8, 0x8);
+
wlc_lcnphy_pwrctrl_rssiparams(pi);
}
read_radio_reg(pi, RADIO_2064_REG007) & 1;
u16 SAVE_jtag_auxpga = read_radio_reg(pi, RADIO_2064_REG0FF) & 0x10;
u16 SAVE_iqadc_aux_en = read_radio_reg(pi, RADIO_2064_REG11F) & 4;
+ u8 SAVE_bbmult = wlc_lcnphy_get_bbmult(pi);
+
idleTssi = read_phy_reg(pi, 0x4ab);
suspend = (0 == (bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) &
MCTL_EN_MAC));
mod_radio_reg(pi, RADIO_2064_REG0FF, 0x10, 1 << 4);
mod_radio_reg(pi, RADIO_2064_REG11F, 0x4, 1 << 2);
wlc_lcnphy_tssi_setup(pi);
+
+ mod_phy_reg(pi, 0x4d7, (0x1 << 0), (1 << 0));
+ mod_phy_reg(pi, 0x4d7, (0x1 << 6), (1 << 6));
+
+ wlc_lcnphy_set_bbmult(pi, 0x0);
+
wlc_phy_do_dummy_tx(pi, true, OFF);
idleTssi = ((read_phy_reg(pi, 0x4ab) & (0x1ff << 0))
>> 0);
mod_phy_reg(pi, 0x44c, (0x1 << 12), (0) << 12);
+ wlc_lcnphy_set_bbmult(pi, SAVE_bbmult);
wlc_lcnphy_set_tx_gain_override(pi, tx_gain_override_old);
wlc_lcnphy_set_tx_gain(pi, &old_gains);
wlc_lcnphy_set_tx_pwr_ctrl(pi, SAVE_txpwrctrl);
wlc_lcnphy_write_table(pi, &tab);
tab.tbl_offset++;
}
+ mod_phy_reg(pi, 0x4d0, (0x1 << 0), (0) << 0);
+ mod_phy_reg(pi, 0x4d3, (0xff << 0), (0) << 0);
+ mod_phy_reg(pi, 0x4d3, (0xff << 8), (0) << 8);
+ mod_phy_reg(pi, 0x4d0, (0x1 << 4), (0) << 4);
+ mod_phy_reg(pi, 0x4d0, (0x1 << 2), (0) << 2);
mod_phy_reg(pi, 0x410, (0x1 << 7), (0) << 7);
target_gains.pad_gain = 21;
target_gains.dac_gain = 0;
wlc_lcnphy_set_tx_gain(pi, &target_gains);
- wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
if (LCNREV_IS(pi->pubpi.phy_rev, 1) || pi_lcn->lcnphy_hw_iqcal_en) {
lcnphy_recal ? LCNPHY_CAL_RECAL :
LCNPHY_CAL_FULL), false);
} else {
+ wlc_lcnphy_set_tx_pwr_by_index(pi, 16);
wlc_lcnphy_tx_iqlo_soft_cal_full(pi);
}
u16 pa_gain;
u16 gm_gain;
- if (CHSPEC_IS5G(pi->radio_chanspec))
- pa_gain = 0x70;
- else
- pa_gain = 0x70;
-
if (pi->sh->boardflags & BFL_FEM)
pa_gain = 0x10;
+ else
+ pa_gain = 0x60;
tab.tbl_id = LCNPHY_TBL_ID_TXPWRCTL;
tab.tbl_width = 32;
tab.tbl_len = 1;
tab.tbl_ptr = &val;
+ /* fixed gm_gain value for iPA */
+ gm_gain = 15;
for (j = 0; j < 128; j++) {
- gm_gain = gain_table[j].gm;
+ if (pi->sh->boardflags & BFL_FEM)
+ gm_gain = gain_table[j].gm;
val = (((u32) pa_gain << 24) |
(gain_table[j].pad << 16) |
(gain_table[j].pga << 8) | gm_gain);
write_phy_reg(pi, 0x4ea, 0x4688);
- mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
+ if (pi->sh->boardflags & BFL_FEM)
+ mod_phy_reg(pi, 0x4eb, (0x7 << 0), 2 << 0);
+ else
+ mod_phy_reg(pi, 0x4eb, (0x7 << 0), 3 << 0);
mod_phy_reg(pi, 0x4eb, (0x7 << 6), 0 << 6);
wlc_lcnphy_rcal(pi);
wlc_lcnphy_rc_cal(pi);
+
+ if (!(pi->sh->boardflags & BFL_FEM)) {
+ write_radio_reg(pi, RADIO_2064_REG032, 0x6f);
+ write_radio_reg(pi, RADIO_2064_REG033, 0x19);
+ write_radio_reg(pi, RADIO_2064_REG039, 0xe);
+ }
+
}
static void wlc_lcnphy_radio_init(struct brcms_phy *pi)
uint idx;
u8 phybw40;
struct phytbl_info tab;
+ const struct phytbl_info *tb;
u32 val;
phybw40 = CHSPEC_IS40(pi->radio_chanspec);
wlc_lcnphy_write_table(pi, &tab);
}
- tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
- tab.tbl_width = 16;
- tab.tbl_ptr = &val;
- tab.tbl_len = 1;
-
- val = 114;
- tab.tbl_offset = 0;
- wlc_lcnphy_write_table(pi, &tab);
+ if (!(pi->sh->boardflags & BFL_FEM)) {
+ tab.tbl_id = LCNPHY_TBL_ID_RFSEQ;
+ tab.tbl_width = 16;
+ tab.tbl_ptr = &val;
+ tab.tbl_len = 1;
- val = 130;
- tab.tbl_offset = 1;
- wlc_lcnphy_write_table(pi, &tab);
+ val = 150;
+ tab.tbl_offset = 0;
+ wlc_lcnphy_write_table(pi, &tab);
- val = 6;
- tab.tbl_offset = 8;
- wlc_lcnphy_write_table(pi, &tab);
+ val = 220;
+ tab.tbl_offset = 1;
+ wlc_lcnphy_write_table(pi, &tab);
+ }
if (CHSPEC_IS2G(pi->radio_chanspec)) {
if (pi->sh->boardflags & BFL_FEM)
}
if (LCNREV_IS(pi->pubpi.phy_rev, 2)) {
- const struct phytbl_info *tb;
int l;
if (CHSPEC_IS2G(pi->radio_chanspec)) {
wlc_lcnphy_write_table(pi, &tb[idx]);
}
- if ((pi->sh->boardflags & BFL_FEM)
- && !(pi->sh->boardflags & BFL_FEM_BT))
- wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313_epa);
- else if (pi->sh->boardflags & BFL_FEM_BT) {
- if (pi->sh->boardrev < 0x1250)
- wlc_lcnphy_write_table(
- pi,
- &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa);
+ if (pi->sh->boardflags & BFL_FEM) {
+ if (pi->sh->boardflags & BFL_FEM_BT) {
+ if (pi->sh->boardrev < 0x1250)
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
+ else
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250;
+ } else {
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_epa;
+ }
+ } else {
+ if (pi->sh->boardflags & BFL_FEM_BT)
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
else
- wlc_lcnphy_write_table(
- pi,
- &dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250);
- } else
- wlc_lcnphy_write_table(pi, &dot11lcn_sw_ctrl_tbl_info_4313);
-
+ tb = &dot11lcn_sw_ctrl_tbl_info_4313;
+ }
+ wlc_lcnphy_write_table(pi, tb);
wlc_lcnphy_load_rfpower(pi);
wlc_lcnphy_clear_papd_comptable(pi);
wlc_lcnphy_load_tx_iir_filter(pi, true, 3);
mod_phy_reg(pi, 0x4eb, (0x7 << 3), (1) << 3);
+ if (wlc_lcnphy_tssi_based_pwr_ctrl_enabled(pi))
+ wlc_lcnphy_tssi_setup(pi);
}
void wlc_phy_detach_lcnphy(struct brcms_phy *pi)
if (!wlc_phy_txpwr_srom_read_lcnphy(pi))
return false;
- if ((pi->sh->boardflags & BFL_FEM) &&
- (LCNREV_IS(pi->pubpi.phy_rev, 1))) {
+ if (LCNREV_IS(pi->pubpi.phy_rev, 1)) {
if (pi_lcn->lcnphy_tempsense_option == 3) {
pi->hwpwrctrl = true;
pi->hwpwrctrl_capable = true;
const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[] = {
{&dot11lcn_gain_tbl_rev0,
- sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18,
+ ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18,
0, 32}
,
{&dot11lcn_aux_gain_idx_tbl_rev0,
- sizeof(dot11lcn_aux_gain_idx_tbl_rev0) /
- sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16}
,
{&dot11lcn_gain_idx_tbl_rev0,
- sizeof(dot11lcn_gain_idx_tbl_rev0) /
- sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32}
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32}
,
};
static const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev1[] = {
{&dot11lcn_gain_tbl_rev1,
- sizeof(dot11lcn_gain_tbl_rev1) / sizeof(dot11lcn_gain_tbl_rev1[0]), 18,
+ ARRAY_SIZE(dot11lcn_gain_tbl_rev1), 18,
0, 32}
,
{&dot11lcn_aux_gain_idx_tbl_rev0,
- sizeof(dot11lcn_aux_gain_idx_tbl_rev0) /
- sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16}
,
{&dot11lcn_gain_idx_tbl_rev0,
- sizeof(dot11lcn_gain_idx_tbl_rev0) /
- sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32}
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32}
,
};
const struct phytbl_info dot11lcnphytbl_rx_gain_info_2G_rev2[] = {
{&dot11lcn_gain_tbl_2G,
- sizeof(dot11lcn_gain_tbl_2G) / sizeof(dot11lcn_gain_tbl_2G[0]), 18, 0,
+ ARRAY_SIZE(dot11lcn_gain_tbl_2G), 18, 0,
32}
,
{&dot11lcn_aux_gain_idx_tbl_2G,
- sizeof(dot11lcn_aux_gain_idx_tbl_2G) /
- sizeof(dot11lcn_aux_gain_idx_tbl_2G[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_2G), 14, 0, 16}
,
{&dot11lcn_gain_idx_tbl_2G,
- sizeof(dot11lcn_gain_idx_tbl_2G) / sizeof(dot11lcn_gain_idx_tbl_2G[0]),
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_2G),
13, 0, 32}
,
{&dot11lcn_gain_val_tbl_2G,
- sizeof(dot11lcn_gain_val_tbl_2G) / sizeof(dot11lcn_gain_val_tbl_2G[0]),
+ ARRAY_SIZE(dot11lcn_gain_val_tbl_2G),
17, 0, 8}
};
const struct phytbl_info dot11lcnphytbl_rx_gain_info_5G_rev2[] = {
{&dot11lcn_gain_tbl_5G,
- sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0,
+ ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0,
32}
,
{&dot11lcn_aux_gain_idx_tbl_5G,
- sizeof(dot11lcn_aux_gain_idx_tbl_5G) /
- sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16}
,
{&dot11lcn_gain_idx_tbl_5G,
- sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]),
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G),
13, 0, 32}
,
{&dot11lcn_gain_val_tbl_5G,
- sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]),
+ ARRAY_SIZE(dot11lcn_gain_val_tbl_5G),
17, 0, 8}
};
const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_2G_rev2[] = {
{&dot11lcn_gain_tbl_extlna_2G,
- sizeof(dot11lcn_gain_tbl_extlna_2G) /
- sizeof(dot11lcn_gain_tbl_extlna_2G[0]), 18, 0, 32}
+ ARRAY_SIZE(dot11lcn_gain_tbl_extlna_2G), 18, 0, 32}
,
{&dot11lcn_aux_gain_idx_tbl_extlna_2G,
- sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G) /
- sizeof(dot11lcn_aux_gain_idx_tbl_extlna_2G[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_extlna_2G), 14, 0, 16}
,
{&dot11lcn_gain_idx_tbl_extlna_2G,
- sizeof(dot11lcn_gain_idx_tbl_extlna_2G) /
- sizeof(dot11lcn_gain_idx_tbl_extlna_2G[0]), 13, 0, 32}
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_extlna_2G), 13, 0, 32}
,
{&dot11lcn_gain_val_tbl_extlna_2G,
- sizeof(dot11lcn_gain_val_tbl_extlna_2G) /
- sizeof(dot11lcn_gain_val_tbl_extlna_2G[0]), 17, 0, 8}
+ ARRAY_SIZE(dot11lcn_gain_val_tbl_extlna_2G), 17, 0, 8}
};
const struct phytbl_info dot11lcnphytbl_rx_gain_info_extlna_5G_rev2[] = {
{&dot11lcn_gain_tbl_5G,
- sizeof(dot11lcn_gain_tbl_5G) / sizeof(dot11lcn_gain_tbl_5G[0]), 18, 0,
+ ARRAY_SIZE(dot11lcn_gain_tbl_5G), 18, 0,
32}
,
{&dot11lcn_aux_gain_idx_tbl_5G,
- sizeof(dot11lcn_aux_gain_idx_tbl_5G) /
- sizeof(dot11lcn_aux_gain_idx_tbl_5G[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_5G), 14, 0, 16}
,
{&dot11lcn_gain_idx_tbl_5G,
- sizeof(dot11lcn_gain_idx_tbl_5G) / sizeof(dot11lcn_gain_idx_tbl_5G[0]),
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_5G),
13, 0, 32}
,
{&dot11lcn_gain_val_tbl_5G,
- sizeof(dot11lcn_gain_val_tbl_5G) / sizeof(dot11lcn_gain_val_tbl_5G[0]),
+ ARRAY_SIZE(dot11lcn_gain_val_tbl_5G),
17, 0, 8}
};
const u32 dot11lcnphytbl_rx_gain_info_sz_rev0 =
- sizeof(dot11lcnphytbl_rx_gain_info_rev0) /
- sizeof(dot11lcnphytbl_rx_gain_info_rev0[0]);
+ ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_rev0);
const u32 dot11lcnphytbl_rx_gain_info_2G_rev2_sz =
- sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2) /
- sizeof(dot11lcnphytbl_rx_gain_info_2G_rev2[0]);
+ ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_2G_rev2);
const u32 dot11lcnphytbl_rx_gain_info_5G_rev2_sz =
- sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2) /
- sizeof(dot11lcnphytbl_rx_gain_info_5G_rev2[0]);
+ ARRAY_SIZE(dot11lcnphytbl_rx_gain_info_5G_rev2);
static const u16 dot11lcn_min_sig_sq_tbl_rev0[] = {
0x014d,
0x0005,
};
+static const u16 dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo[] = {
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+ 0x0005,
+ 0x0006,
+ 0x0009,
+ 0x000a,
+};
+
static const u16 dot11lcn_sw_ctrl_tbl_rev0[] = {
0x0004,
0x0004,
const struct phytbl_info dot11lcnphytbl_info_rev0[] = {
{&dot11lcn_min_sig_sq_tbl_rev0,
- sizeof(dot11lcn_min_sig_sq_tbl_rev0) /
- sizeof(dot11lcn_min_sig_sq_tbl_rev0[0]), 2, 0, 16}
+ ARRAY_SIZE(dot11lcn_min_sig_sq_tbl_rev0), 2, 0, 16}
,
{&dot11lcn_noise_scale_tbl_rev0,
- sizeof(dot11lcn_noise_scale_tbl_rev0) /
- sizeof(dot11lcn_noise_scale_tbl_rev0[0]), 1, 0, 16}
+ ARRAY_SIZE(dot11lcn_noise_scale_tbl_rev0), 1, 0, 16}
,
{&dot11lcn_fltr_ctrl_tbl_rev0,
- sizeof(dot11lcn_fltr_ctrl_tbl_rev0) /
- sizeof(dot11lcn_fltr_ctrl_tbl_rev0[0]), 11, 0, 32}
+ ARRAY_SIZE(dot11lcn_fltr_ctrl_tbl_rev0), 11, 0, 32}
,
{&dot11lcn_ps_ctrl_tbl_rev0,
- sizeof(dot11lcn_ps_ctrl_tbl_rev0) /
- sizeof(dot11lcn_ps_ctrl_tbl_rev0[0]), 12, 0, 32}
+ ARRAY_SIZE(dot11lcn_ps_ctrl_tbl_rev0), 12, 0, 32}
,
{&dot11lcn_gain_idx_tbl_rev0,
- sizeof(dot11lcn_gain_idx_tbl_rev0) /
- sizeof(dot11lcn_gain_idx_tbl_rev0[0]), 13, 0, 32}
+ ARRAY_SIZE(dot11lcn_gain_idx_tbl_rev0), 13, 0, 32}
,
{&dot11lcn_aux_gain_idx_tbl_rev0,
- sizeof(dot11lcn_aux_gain_idx_tbl_rev0) /
- sizeof(dot11lcn_aux_gain_idx_tbl_rev0[0]), 14, 0, 16}
+ ARRAY_SIZE(dot11lcn_aux_gain_idx_tbl_rev0), 14, 0, 16}
,
{&dot11lcn_sw_ctrl_tbl_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_rev0[0]), 15, 0, 16}
+ ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_rev0), 15, 0, 16}
,
{&dot11lcn_nf_table_rev0,
- sizeof(dot11lcn_nf_table_rev0) / sizeof(dot11lcn_nf_table_rev0[0]), 16,
+ ARRAY_SIZE(dot11lcn_nf_table_rev0), 16,
0, 8}
,
{&dot11lcn_gain_val_tbl_rev0,
- sizeof(dot11lcn_gain_val_tbl_rev0) /
- sizeof(dot11lcn_gain_val_tbl_rev0[0]), 17, 0, 8}
+ ARRAY_SIZE(dot11lcn_gain_val_tbl_rev0), 17, 0, 8}
,
{&dot11lcn_gain_tbl_rev0,
- sizeof(dot11lcn_gain_tbl_rev0) / sizeof(dot11lcn_gain_tbl_rev0[0]), 18,
+ ARRAY_SIZE(dot11lcn_gain_tbl_rev0), 18,
0, 32}
,
{&dot11lcn_spur_tbl_rev0,
- sizeof(dot11lcn_spur_tbl_rev0) / sizeof(dot11lcn_spur_tbl_rev0[0]), 20,
+ ARRAY_SIZE(dot11lcn_spur_tbl_rev0), 20,
0, 8}
,
{&dot11lcn_unsup_mcs_tbl_rev0,
- sizeof(dot11lcn_unsup_mcs_tbl_rev0) /
- sizeof(dot11lcn_unsup_mcs_tbl_rev0[0]), 23, 0, 16}
+ ARRAY_SIZE(dot11lcn_unsup_mcs_tbl_rev0), 23, 0, 16}
,
{&dot11lcn_iq_local_tbl_rev0,
- sizeof(dot11lcn_iq_local_tbl_rev0) /
- sizeof(dot11lcn_iq_local_tbl_rev0[0]), 0, 0, 16}
+ ARRAY_SIZE(dot11lcn_iq_local_tbl_rev0), 0, 0, 16}
,
{&dot11lcn_papd_compdelta_tbl_rev0,
- sizeof(dot11lcn_papd_compdelta_tbl_rev0) /
- sizeof(dot11lcn_papd_compdelta_tbl_rev0[0]), 24, 0, 32}
+ ARRAY_SIZE(dot11lcn_papd_compdelta_tbl_rev0), 24, 0, 32}
,
};
const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313 = {
&dot11lcn_sw_ctrl_tbl_4313_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_rev0[0]), 15, 0, 16
+ ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_rev0), 15, 0, 16
+};
+
+const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa = {
+ &dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo,
+ ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_ipa_rev0_combo), 15, 0, 16
};
const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa = {
&dot11lcn_sw_ctrl_tbl_4313_epa_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0[0]), 15, 0, 16
+ ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0), 15, 0, 16
};
const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa = {
&dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo[0]), 15, 0, 16
+ ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_epa_rev0_combo), 15, 0, 16
};
const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa_p250 = {
&dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0,
- sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0) /
- sizeof(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0[0]), 15, 0, 16
+ ARRAY_SIZE(dot11lcn_sw_ctrl_tbl_4313_bt_epa_p250_rev0), 15, 0, 16
};
const u32 dot11lcnphytbl_info_sz_rev0 =
- sizeof(dot11lcnphytbl_info_rev0) / sizeof(dot11lcnphytbl_info_rev0[0]);
+ ARRAY_SIZE(dot11lcnphytbl_info_rev0);
const struct lcnphy_tx_gain_tbl_entry
dot11lcnphy_2GHz_extPA_gaintable_rev0[128] = {
};
const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_2GHz_gaintable_rev0[128] = {
- {7, 0, 31, 0, 72},
- {7, 0, 31, 0, 70},
- {7, 0, 31, 0, 68},
- {7, 0, 30, 0, 67},
- {7, 0, 29, 0, 68},
- {7, 0, 28, 0, 68},
- {7, 0, 27, 0, 69},
- {7, 0, 26, 0, 70},
- {7, 0, 25, 0, 70},
- {7, 0, 24, 0, 71},
- {7, 0, 23, 0, 72},
- {7, 0, 23, 0, 70},
- {7, 0, 22, 0, 71},
- {7, 0, 21, 0, 72},
- {7, 0, 21, 0, 70},
- {7, 0, 21, 0, 68},
- {7, 0, 21, 0, 66},
- {7, 0, 21, 0, 64},
- {7, 0, 21, 0, 63},
- {7, 0, 20, 0, 64},
- {7, 0, 19, 0, 65},
- {7, 0, 19, 0, 64},
- {7, 0, 18, 0, 65},
- {7, 0, 18, 0, 64},
- {7, 0, 17, 0, 65},
- {7, 0, 17, 0, 64},
- {7, 0, 16, 0, 65},
- {7, 0, 16, 0, 64},
- {7, 0, 16, 0, 62},
- {7, 0, 16, 0, 60},
- {7, 0, 16, 0, 58},
- {7, 0, 15, 0, 61},
- {7, 0, 15, 0, 59},
- {7, 0, 14, 0, 61},
- {7, 0, 14, 0, 60},
- {7, 0, 14, 0, 58},
- {7, 0, 13, 0, 60},
- {7, 0, 13, 0, 59},
- {7, 0, 12, 0, 62},
- {7, 0, 12, 0, 60},
- {7, 0, 12, 0, 58},
- {7, 0, 11, 0, 62},
- {7, 0, 11, 0, 60},
- {7, 0, 11, 0, 59},
- {7, 0, 11, 0, 57},
- {7, 0, 10, 0, 61},
- {7, 0, 10, 0, 59},
- {7, 0, 10, 0, 57},
- {7, 0, 9, 0, 62},
- {7, 0, 9, 0, 60},
- {7, 0, 9, 0, 58},
- {7, 0, 9, 0, 57},
- {7, 0, 8, 0, 62},
- {7, 0, 8, 0, 60},
- {7, 0, 8, 0, 58},
- {7, 0, 8, 0, 57},
- {7, 0, 8, 0, 55},
- {7, 0, 7, 0, 61},
+ {15, 0, 31, 0, 72},
+ {15, 0, 31, 0, 70},
+ {15, 0, 31, 0, 68},
+ {15, 0, 30, 0, 68},
+ {15, 0, 29, 0, 69},
+ {15, 0, 28, 0, 69},
+ {15, 0, 27, 0, 70},
+ {15, 0, 26, 0, 70},
+ {15, 0, 25, 0, 71},
+ {15, 0, 24, 0, 72},
+ {15, 0, 23, 0, 73},
+ {15, 0, 23, 0, 71},
+ {15, 0, 22, 0, 72},
+ {15, 0, 21, 0, 73},
+ {15, 0, 21, 0, 71},
+ {15, 0, 21, 0, 69},
+ {15, 0, 21, 0, 67},
+ {15, 0, 21, 0, 65},
+ {15, 0, 21, 0, 63},
+ {15, 0, 20, 0, 65},
+ {15, 0, 19, 0, 66},
+ {15, 0, 19, 0, 64},
+ {15, 0, 18, 0, 66},
+ {15, 0, 18, 0, 64},
+ {15, 0, 17, 0, 66},
+ {15, 0, 17, 0, 64},
+ {15, 0, 16, 0, 66},
+ {15, 0, 16, 0, 64},
+ {15, 0, 16, 0, 62},
+ {15, 0, 16, 0, 61},
+ {15, 0, 16, 0, 59},
+ {15, 0, 15, 0, 61},
+ {15, 0, 15, 0, 59},
+ {15, 0, 14, 0, 62},
+ {15, 0, 14, 0, 60},
+ {15, 0, 14, 0, 58},
+ {15, 0, 13, 0, 61},
+ {15, 0, 13, 0, 59},
+ {15, 0, 12, 0, 62},
+ {15, 0, 12, 0, 61},
+ {15, 0, 12, 0, 59},
+ {15, 0, 11, 0, 62},
+ {15, 0, 11, 0, 61},
+ {15, 0, 11, 0, 59},
+ {15, 0, 11, 0, 57},
+ {15, 0, 10, 0, 61},
+ {15, 0, 10, 0, 59},
+ {15, 0, 10, 0, 58},
+ {15, 0, 9, 0, 62},
+ {15, 0, 9, 0, 61},
+ {15, 0, 9, 0, 59},
+ {15, 0, 9, 0, 57},
+ {15, 0, 8, 0, 62},
+ {15, 0, 8, 0, 61},
+ {15, 0, 8, 0, 59},
+ {15, 0, 8, 0, 57},
+ {15, 0, 8, 0, 56},
+ {15, 0, 8, 0, 54},
+ {15, 0, 8, 0, 53},
+ {15, 0, 8, 0, 51},
+ {15, 0, 8, 0, 50},
+ {7, 0, 7, 0, 69},
+ {7, 0, 7, 0, 67},
+ {7, 0, 7, 0, 65},
+ {7, 0, 7, 0, 64},
+ {7, 0, 7, 0, 62},
{7, 0, 7, 0, 60},
{7, 0, 7, 0, 58},
- {7, 0, 7, 0, 56},
+ {7, 0, 7, 0, 57},
{7, 0, 7, 0, 55},
{7, 0, 6, 0, 62},
- {7, 0, 6, 0, 60},
- {7, 0, 6, 0, 58},
+ {7, 0, 6, 0, 61},
+ {7, 0, 6, 0, 59},
{7, 0, 6, 0, 57},
- {7, 0, 6, 0, 55},
+ {7, 0, 6, 0, 56},
{7, 0, 6, 0, 54},
- {7, 0, 6, 0, 52},
+ {7, 0, 6, 0, 53},
{7, 0, 5, 0, 61},
- {7, 0, 5, 0, 59},
- {7, 0, 5, 0, 57},
+ {7, 0, 5, 0, 60},
+ {7, 0, 5, 0, 58},
{7, 0, 5, 0, 56},
- {7, 0, 5, 0, 54},
+ {7, 0, 5, 0, 55},
{7, 0, 5, 0, 53},
- {7, 0, 5, 0, 51},
- {7, 0, 4, 0, 62},
- {7, 0, 4, 0, 60},
- {7, 0, 4, 0, 58},
+ {7, 0, 5, 0, 52},
+ {7, 0, 5, 0, 50},
+ {7, 0, 5, 0, 49},
+ {7, 0, 5, 0, 47},
{7, 0, 4, 0, 57},
- {7, 0, 4, 0, 55},
+ {7, 0, 4, 0, 56},
{7, 0, 4, 0, 54},
- {7, 0, 4, 0, 52},
+ {7, 0, 4, 0, 53},
{7, 0, 4, 0, 51},
- {7, 0, 4, 0, 49},
+ {7, 0, 4, 0, 50},
{7, 0, 4, 0, 48},
+ {7, 0, 4, 0, 47},
{7, 0, 4, 0, 46},
- {7, 0, 3, 0, 60},
- {7, 0, 3, 0, 58},
- {7, 0, 3, 0, 57},
- {7, 0, 3, 0, 55},
- {7, 0, 3, 0, 54},
- {7, 0, 3, 0, 52},
+ {7, 0, 4, 0, 44},
+ {7, 0, 4, 0, 43},
+ {7, 0, 4, 0, 42},
+ {7, 0, 4, 0, 41},
+ {7, 0, 4, 0, 40},
{7, 0, 3, 0, 51},
- {7, 0, 3, 0, 49},
+ {7, 0, 3, 0, 50},
{7, 0, 3, 0, 48},
+ {7, 0, 3, 0, 47},
{7, 0, 3, 0, 46},
- {7, 0, 3, 0, 45},
{7, 0, 3, 0, 44},
{7, 0, 3, 0, 43},
+ {7, 0, 3, 0, 42},
{7, 0, 3, 0, 41},
- {7, 0, 2, 0, 61},
- {7, 0, 2, 0, 59},
- {7, 0, 2, 0, 57},
- {7, 0, 2, 0, 56},
- {7, 0, 2, 0, 54},
- {7, 0, 2, 0, 53},
- {7, 0, 2, 0, 51},
- {7, 0, 2, 0, 50},
- {7, 0, 2, 0, 48},
- {7, 0, 2, 0, 47},
- {7, 0, 2, 0, 46},
- {7, 0, 2, 0, 44},
- {7, 0, 2, 0, 43},
- {7, 0, 2, 0, 42},
- {7, 0, 2, 0, 41},
- {7, 0, 2, 0, 39},
- {7, 0, 2, 0, 38},
- {7, 0, 2, 0, 37},
- {7, 0, 2, 0, 36},
- {7, 0, 2, 0, 35},
- {7, 0, 2, 0, 34},
- {7, 0, 2, 0, 33},
- {7, 0, 2, 0, 32},
- {7, 0, 1, 0, 63},
- {7, 0, 1, 0, 61},
- {7, 0, 1, 0, 59},
- {7, 0, 1, 0, 57},
+ {3, 0, 3, 0, 56},
+ {3, 0, 3, 0, 54},
+ {3, 0, 3, 0, 53},
+ {3, 0, 3, 0, 51},
+ {3, 0, 3, 0, 50},
+ {3, 0, 3, 0, 48},
+ {3, 0, 3, 0, 47},
+ {3, 0, 3, 0, 46},
+ {3, 0, 3, 0, 44},
+ {3, 0, 3, 0, 43},
+ {3, 0, 3, 0, 42},
+ {3, 0, 3, 0, 41},
+ {3, 0, 3, 0, 39},
+ {3, 0, 3, 0, 38},
+ {3, 0, 3, 0, 37},
+ {3, 0, 3, 0, 36},
+ {3, 0, 3, 0, 35},
+ {3, 0, 3, 0, 34},
};
const struct lcnphy_tx_gain_tbl_entry dot11lcnphy_5GHz_gaintable_rev0[128] = {
extern const struct phytbl_info dot11lcnphytbl_rx_gain_info_rev0[];
extern const u32 dot11lcnphytbl_rx_gain_info_sz_rev0;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313;
+extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_ipa;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_epa_combo;
extern const struct phytbl_info dot11lcn_sw_ctrl_tbl_info_4313_bt_epa;
/* the MSDU shall be terminated. Overrides the global */
/* dot11MaxTransmitMsduLifeTime setting [optional] */
/* Device will set the default value if this is 0. */
- u32 expire_time;
+ __le32 expire_time;
/* WSM_HT_TX_... */
__le32 ht_tx_parameters;
if (local->no_pri) {
printk(KERN_DEBUG "%s: could not set interface UP - no PRI "
"f/w\n", dev->name);
- return 1;
+ return -ENODEV;
}
if ((local->func->card_present && !local->func->card_present(local)) ||
printk(KERN_WARNING "%s: could not enable MAC port\n",
dev->name);
prism2_close(dev);
- return 1;
+ return -ENODEV;
}
if (!local->dev_enabled)
prism2_callback(local, PRISM2_CALLBACK_ENABLE);
*/
static void
il3945_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *il_sta)
{
}
}
}
+#define SMALL_PACKET_SIZE 256
+
static void
il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
struct ieee80211_rx_status *stats)
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)IL_RX_DATA(pkt);
struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt);
struct il3945_rx_frame_end *rx_end = IL_RX_END(pkt);
- u16 len = le16_to_cpu(rx_hdr->len);
+ u32 len = le16_to_cpu(rx_hdr->len);
struct sk_buff *skb;
__le16 fc = hdr->frame_control;
+ u32 fraglen = PAGE_SIZE << il->hw_params.rx_page_order;
/* We received data from the HW, so stop the watchdog */
- if (unlikely
- (len + IL39_RX_FRAME_SIZE >
- PAGE_SIZE << il->hw_params.rx_page_order)) {
+ if (unlikely(len + IL39_RX_FRAME_SIZE > fraglen)) {
D_DROP("Corruption detected!\n");
return;
}
D_INFO("Woke queues - frame received on passive channel\n");
}
- skb = dev_alloc_skb(128);
+ skb = dev_alloc_skb(SMALL_PACKET_SIZE);
if (!skb) {
IL_ERR("dev_alloc_skb failed\n");
return;
}
if (!il3945_mod_params.sw_crypto)
- il_set_decrypted_flag(il, (struct ieee80211_hdr *)rxb_addr(rxb),
+ il_set_decrypted_flag(il, (struct ieee80211_hdr *)pkt,
le32_to_cpu(rx_end->status), stats);
- skb_add_rx_frag(skb, 0, rxb->page,
- (void *)rx_hdr->payload - (void *)pkt, len,
- len);
-
+ /* If frame is small enough to fit into skb->head, copy it
+ * and do not consume a full page
+ */
+ if (len <= SMALL_PACKET_SIZE) {
+ memcpy(skb_put(skb, len), rx_hdr->payload, len);
+ } else {
+ skb_add_rx_frag(skb, 0, rxb->page,
+ (void *)rx_hdr->payload - (void *)pkt, len,
+ fraglen);
+ il->alloc_rxb_page--;
+ rxb->page = NULL;
+ }
il_update_stats(il, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx(il->hw, skb);
- il->alloc_rxb_page--;
- rxb->page = NULL;
}
#define IL_DELAY_NEXT_SCAN_AFTER_ASSOC (HZ*6)
return decrypt_out;
}
+#define SMALL_PACKET_SIZE 256
+
static void
il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
- u16 len, u32 ampdu_status, struct il_rx_buf *rxb,
+ u32 len, u32 ampdu_status, struct il_rx_buf *rxb,
struct ieee80211_rx_status *stats)
{
struct sk_buff *skb;
il_set_decrypted_flag(il, hdr, ampdu_status, stats))
return;
- skb = dev_alloc_skb(128);
+ skb = dev_alloc_skb(SMALL_PACKET_SIZE);
if (!skb) {
IL_ERR("dev_alloc_skb failed\n");
return;
}
- skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len,
- len);
+ if (len <= SMALL_PACKET_SIZE) {
+ memcpy(skb_put(skb, len), hdr, len);
+ } else {
+ skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb),
+ len, PAGE_SIZE << il->hw_params.rx_page_order);
+ il->alloc_rxb_page--;
+ rxb->page = NULL;
+ }
il_update_stats(il, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx(il->hw, skb);
- il->alloc_rxb_page--;
- rxb->page = NULL;
}
/* Called for N_RX (legacy ABG frames), or
*/
static void
il4965_rs_rate_init_stub(void *il_r, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *il_sta)
{
}
Intel Wireless WiFi Link 6150BGN 2 Adapter
Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN)
Intel 2000 Series Wi-Fi Adapters
+ Intel 7260 Wi-Fi Adapter
+ Intel 3160 Wi-Fi Adapter
This driver uses the kernel's mac80211 subsystem.
depends on IWLWIFI
default IWLWIFI
help
- This is the driver supporting the DVM firmware which is
- currently the only firmware available for existing devices.
+ This is the driver that supports the DVM firmware which is
+ used by most existing devices (with the exception of 7260
+ and 3160).
config IWLMVM
tristate "Intel Wireless WiFi MVM Firmware support"
depends on IWLWIFI
help
- This is the driver supporting the MVM firmware which is
- currently only available for 7000 series devices.
-
- Say yes if you have such a device.
+ This is the driver that supports the MVM firmware which is
+ currently only available for 7260 and 3160 devices.
# don't call it _MODULE -- will confuse Kconfig/fixdep/...
config IWLWIFI_OPMODE_MODULAR
If unsure, say Y so we can help you better when problems
occur.
endmenu
-
-config IWLWIFI_P2P
- def_bool y
- bool "iwlwifi experimental P2P support"
- depends on IWLWIFI
- help
- This option enables experimental P2P support for some devices
- based on microcode support. Since P2P support is still under
- development, this option may even enable it for some devices
- now that turn out to not support it in the future due to
- microcode restrictions.
-
- To determine if your microcode supports the experimental P2P
- offered by this option, check if the driver advertises AP
- support when it is loaded.
-
- Say Y only if you want to experiment with P2P.
#define STATUS_CHANNEL_SWITCH_PENDING 11
#define STATUS_SCAN_COMPLETE 12
#define STATUS_POWER_PMI 13
-#define STATUS_SCAN_ROC_EXPIRED 14
struct iwl_ucode_capabilities;
/* scan */
void iwlagn_post_scan(struct iwl_priv *priv);
-void iwlagn_disable_roc(struct iwl_priv *priv);
int iwl_force_rf_reset(struct iwl_priv *priv, bool external);
void iwl_init_scan_params(struct iwl_priv *priv);
int iwl_scan_cancel(struct iwl_priv *priv);
enum iwl_scan_type scan_type,
enum ieee80211_band band);
-void iwl_scan_roc_expired(struct iwl_priv *priv);
-void iwl_scan_offchannel_skb(struct iwl_priv *priv);
-void iwl_scan_offchannel_skb_status(struct iwl_priv *priv);
-
/* For faster active scanning, scan will move to the next channel if fewer than
* PLCP_QUIET_THRESH packets are heard on this channel within
* ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell
} while (0)
/* file operation */
-#define DEBUGFS_READ_FUNC(name) \
-static ssize_t iwl_dbgfs_##name##_read(struct file *file, \
- char __user *user_buf, \
- size_t count, loff_t *ppos);
-
-#define DEBUGFS_WRITE_FUNC(name) \
-static ssize_t iwl_dbgfs_##name##_write(struct file *file, \
- const char __user *user_buf, \
- size_t count, loff_t *ppos);
-
-
#define DEBUGFS_READ_FILE_OPS(name) \
- DEBUGFS_READ_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
.open = simple_open, \
};
#define DEBUGFS_WRITE_FILE_OPS(name) \
- DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.open = simple_open, \
#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
- DEBUGFS_READ_FUNC(name); \
- DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.read = iwl_dbgfs_##name##_read, \
enum iwl_scan_type {
IWL_SCAN_NORMAL,
IWL_SCAN_RADIO_RESET,
- IWL_SCAN_ROC,
};
/**
struct reply_tx_error_statistics reply_tx_stats;
struct reply_agg_tx_error_statistics reply_agg_tx_stats;
- /* remain-on-channel offload support */
- struct ieee80211_channel *hw_roc_channel;
- struct delayed_work hw_roc_disable_work;
- int hw_roc_duration;
- bool hw_roc_setup, hw_roc_start_notified;
-
/* bt coex */
u8 bt_enable_flag;
u8 bt_status;
},
};
-static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = {
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_GO) |
- BIT(NL80211_IFTYPE_AP),
- },
-};
-
-static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = {
- {
- .max = 2,
- .types = BIT(NL80211_IFTYPE_STATION),
- },
- {
- .max = 1,
- .types = BIT(NL80211_IFTYPE_P2P_CLIENT),
- },
-};
-
static const struct ieee80211_iface_combination
iwlagn_iface_combinations_dualmode[] = {
{ .num_different_channels = 1,
},
};
-static const struct ieee80211_iface_combination
-iwlagn_iface_combinations_p2p[] = {
- { .num_different_channels = 1,
- .max_interfaces = 2,
- .beacon_int_infra_match = true,
- .limits = iwlagn_p2p_sta_go_limits,
- .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits),
- },
- { .num_different_channels = 1,
- .max_interfaces = 2,
- .limits = iwlagn_p2p_2sta_limits,
- .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits),
- },
-};
-
/*
* Not a mac80211 entry point function, but it fits in with all the
* other mac80211 functions grouped here.
BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2);
- if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) {
- hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p;
- hw->wiphy->n_iface_combinations =
- ARRAY_SIZE(iwlagn_iface_combinations_p2p);
- } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
+ if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) {
hw->wiphy->iface_combinations =
iwlagn_iface_combinations_dualmode;
hw->wiphy->n_iface_combinations =
ARRAY_SIZE(iwlagn_iface_combinations_dualmode);
}
- hw->wiphy->max_remain_on_channel_duration = 500;
-
hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY |
WIPHY_FLAG_DISABLE_BEACON_HINTS |
WIPHY_FLAG_IBSS_RSN;
IWL_DEBUG_MAC80211(priv, "leave\n");
}
-static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_channel *channel,
- int duration,
- enum ieee80211_roc_type type)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
- int err = 0;
-
- if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
- return -EOPNOTSUPP;
-
- if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)))
- return -EOPNOTSUPP;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
-
- if (test_bit(STATUS_SCAN_HW, &priv->status)) {
- /* mac80211 should not scan while ROC or ROC while scanning */
- if (WARN_ON_ONCE(priv->scan_type != IWL_SCAN_RADIO_RESET)) {
- err = -EBUSY;
- goto out;
- }
-
- iwl_scan_cancel_timeout(priv, 100);
-
- if (test_bit(STATUS_SCAN_HW, &priv->status)) {
- err = -EBUSY;
- goto out;
- }
- }
-
- priv->hw_roc_channel = channel;
- /* convert from ms to TU */
- priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024);
- priv->hw_roc_start_notified = false;
- cancel_delayed_work(&priv->hw_roc_disable_work);
-
- if (!ctx->is_active) {
- static const struct iwl_qos_info default_qos_data = {
- .def_qos_parm = {
- .ac[0] = {
- .cw_min = cpu_to_le16(3),
- .cw_max = cpu_to_le16(7),
- .aifsn = 2,
- .edca_txop = cpu_to_le16(1504),
- },
- .ac[1] = {
- .cw_min = cpu_to_le16(7),
- .cw_max = cpu_to_le16(15),
- .aifsn = 2,
- .edca_txop = cpu_to_le16(3008),
- },
- .ac[2] = {
- .cw_min = cpu_to_le16(15),
- .cw_max = cpu_to_le16(1023),
- .aifsn = 3,
- },
- .ac[3] = {
- .cw_min = cpu_to_le16(15),
- .cw_max = cpu_to_le16(1023),
- .aifsn = 7,
- },
- },
- };
-
- ctx->is_active = true;
- ctx->qos_data = default_qos_data;
- ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
- memcpy(ctx->staging.node_addr,
- priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
- ETH_ALEN);
- memcpy(ctx->staging.bssid_addr,
- priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr,
- ETH_ALEN);
- err = iwlagn_commit_rxon(priv, ctx);
- if (err)
- goto out;
- ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK |
- RXON_FILTER_PROMISC_MSK |
- RXON_FILTER_CTL2HOST_MSK;
-
- err = iwlagn_commit_rxon(priv, ctx);
- if (err) {
- iwlagn_disable_roc(priv);
- goto out;
- }
- priv->hw_roc_setup = true;
- }
-
- err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band);
- if (err)
- iwlagn_disable_roc(priv);
-
- out:
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return err;
-}
-
-static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw)
-{
- struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
-
- if (!(priv->valid_contexts & BIT(IWL_RXON_CTX_PAN)))
- return -EOPNOTSUPP;
-
- IWL_DEBUG_MAC80211(priv, "enter\n");
- mutex_lock(&priv->mutex);
- iwl_scan_cancel_timeout(priv, priv->hw_roc_duration);
- iwlagn_disable_roc(priv);
- mutex_unlock(&priv->mutex);
- IWL_DEBUG_MAC80211(priv, "leave\n");
-
- return 0;
-}
-
static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
enum ieee80211_rssi_event rssi_event)
IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n",
viftype, vif->addr);
- cancel_delayed_work_sync(&priv->hw_roc_disable_work);
-
mutex_lock(&priv->mutex);
- iwlagn_disable_roc(priv);
-
if (!iwl_is_ready_rf(priv)) {
IWL_WARN(priv, "Try to add interface when device not ready\n");
err = -EINVAL;
.channel_switch = iwlagn_mac_channel_switch,
.flush = iwlagn_mac_flush,
.tx_last_beacon = iwlagn_mac_tx_last_beacon,
- .remain_on_channel = iwlagn_mac_remain_on_channel,
- .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel,
.rssi_callback = iwlagn_mac_rssi_callback,
.set_tim = iwlagn_mac_set_tim,
};
priv->contexts[IWL_RXON_CTX_PAN].interface_modes =
BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP);
- if (ucode_flags & IWL_UCODE_TLV_FLAGS_P2P)
- priv->contexts[IWL_RXON_CTX_PAN].interface_modes |=
- BIT(NL80211_IFTYPE_P2P_CLIENT) |
- BIT(NL80211_IFTYPE_P2P_GO);
-
priv->contexts[IWL_RXON_CTX_PAN].ap_devtype = RXON_DEV_TYPE_CP;
priv->contexts[IWL_RXON_CTX_PAN].station_devtype = RXON_DEV_TYPE_2STA;
priv->contexts[IWL_RXON_CTX_PAN].unused_devtype = RXON_DEV_TYPE_P2P;
iwl_scan_cancel_timeout(priv, 200);
- /*
- * If active, scanning won't cancel it, so say it expired.
- * No race since we hold the mutex here and a new one
- * can't come in at this time.
- */
- if (priv->ucode_loaded && priv->cur_ucode != IWL_UCODE_INIT)
- ieee80211_remain_on_channel_expired(priv->hw);
-
exit_pending =
test_and_set_bit(STATUS_EXIT_PENDING, &priv->status);
}
}
-
-
-
-void iwlagn_disable_roc(struct iwl_priv *priv)
-{
- struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN];
-
- lockdep_assert_held(&priv->mutex);
-
- if (!priv->hw_roc_setup)
- return;
-
- ctx->staging.dev_type = RXON_DEV_TYPE_P2P;
- ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
-
- priv->hw_roc_channel = NULL;
-
- memset(ctx->staging.node_addr, 0, ETH_ALEN);
-
- iwlagn_commit_rxon(priv, ctx);
-
- ctx->is_active = false;
- priv->hw_roc_setup = false;
-}
-
-static void iwlagn_disable_roc_work(struct work_struct *work)
-{
- struct iwl_priv *priv = container_of(work, struct iwl_priv,
- hw_roc_disable_work.work);
-
- mutex_lock(&priv->mutex);
- iwlagn_disable_roc(priv);
- mutex_unlock(&priv->mutex);
-}
-
/*****************************************************************************
*
* driver setup and teardown
INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
INIT_WORK(&priv->bt_full_concurrency, iwl_bg_bt_full_concurrency);
INIT_WORK(&priv->bt_runtime_config, iwl_bg_bt_runtime_config);
- INIT_DELAYED_WORK(&priv->hw_roc_disable_work,
- iwlagn_disable_roc_work);
iwl_setup_scan_deferred_work(priv);
cancel_work_sync(&priv->bt_full_concurrency);
cancel_work_sync(&priv->bt_runtime_config);
- cancel_delayed_work_sync(&priv->hw_roc_disable_work);
del_timer_sync(&priv->statistics_periodic);
del_timer_sync(&priv->ucode_trace);
#else
IWL_INFO(priv, "CONFIG_IWLWIFI_DEVICE_TRACING disabled\n");
#endif
-
-#ifdef CONFIG_IWLWIFI_P2P
- IWL_INFO(priv, "CONFIG_IWLWIFI_P2P enabled\n");
-#else
- IWL_INFO(priv, "CONFIG_IWLWIFI_P2P disabled\n");
-#endif
}
static int iwl_eeprom_init_hw_params(struct iwl_priv *priv)
ucode_flags = fw->ucode_capa.flags;
-#ifndef CONFIG_IWLWIFI_P2P
- ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
-#endif
-
if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) {
priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN;
trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM;
* if not PAN, then don't support P2P -- might be a uCode
* packaging bug or due to the eeprom check above
*/
- ucode_flags &= ~IWL_UCODE_TLV_FLAGS_P2P;
priv->sta_key_max_num = STA_KEY_MAX_NUM;
trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM;
lq_sta->flush_timer = 0;
lq_sta->supp_rates = sta->supp_rates[sband->band];
- 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]);
IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
sta_id);
* station is added we ignore it.
*/
static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta)
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *priv_sta)
{
}
static struct rate_control_ops rs_ops = {
cmd.slots[0].type = 0; /* BSS */
cmd.slots[1].type = 1; /* PAN */
- if (priv->hw_roc_setup) {
- /* both contexts must be used for this to happen */
- slot1 = IWL_MIN_SLOT_TIME;
- slot0 = 3000;
- } else if (ctx_bss->vif && ctx_pan->vif) {
+ if (ctx_bss->vif && ctx_pan->vif) {
int bcnint = ctx_pan->beacon_int;
int dtim = ctx_pan->vif->bss_conf.dtim_period ?: 1;
ieee80211_scan_completed(priv->hw, aborted);
}
- if (priv->scan_type == IWL_SCAN_ROC)
- iwl_scan_roc_expired(priv);
-
priv->scan_type = IWL_SCAN_NORMAL;
priv->scan_vif = NULL;
priv->scan_request = NULL;
goto out_settings;
}
- if (priv->scan_type == IWL_SCAN_ROC)
- iwl_scan_roc_expired(priv);
-
if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) {
int err;
le32_to_cpu(notif->tsf_low),
notif->status, notif->beacon_timer);
- if (priv->scan_type == IWL_SCAN_ROC &&
- !priv->hw_roc_start_notified) {
- ieee80211_ready_on_channel(priv->hw);
- priv->hw_roc_start_notified = true;
- }
-
return 0;
}
scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH;
scan->quiet_time = IWL_ACTIVE_QUIET_TIME;
- if (priv->scan_type != IWL_SCAN_ROC &&
- iwl_is_any_associated(priv)) {
+ if (iwl_is_any_associated(priv)) {
u16 interval = 0;
u32 extra;
u32 suspend_time = 100;
IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
switch (priv->scan_type) {
- case IWL_SCAN_ROC:
- WARN_ON(1);
- break;
case IWL_SCAN_RADIO_RESET:
interval = 0;
break;
scan->suspend_time = cpu_to_le32(scan_suspend_time);
IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n",
scan_suspend_time, interval);
- } else if (priv->scan_type == IWL_SCAN_ROC) {
- scan->suspend_time = 0;
- scan->max_out_time = 0;
- scan->quiet_time = 0;
- scan->quiet_plcp_th = 0;
}
switch (priv->scan_type) {
} else
IWL_DEBUG_SCAN(priv, "Start passive scan.\n");
break;
- case IWL_SCAN_ROC:
- IWL_DEBUG_SCAN(priv, "Start ROC scan.\n");
- break;
}
scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK;
scan_cmd_size - sizeof(*scan));
break;
case IWL_SCAN_RADIO_RESET:
- case IWL_SCAN_ROC:
/* use bcast addr, will not be transmitted but must be valid */
cmd_len = iwl_fill_probe_req(
(struct ieee80211_mgmt *)scan->data,
is_active, n_probes,
(void *)&scan->data[cmd_len]);
break;
- case IWL_SCAN_ROC: {
- struct iwl_scan_channel *scan_ch;
- int n_chan, i;
- u16 dwell;
-
- dwell = iwl_limit_dwell(priv, priv->hw_roc_duration);
- n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell);
-
- scan->channel_count = n_chan;
-
- scan_ch = (void *)&scan->data[cmd_len];
-
- for (i = 0; i < n_chan; i++) {
- scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
- scan_ch->channel =
- cpu_to_le16(priv->hw_roc_channel->hw_value);
-
- if (i == n_chan - 1)
- dwell = priv->hw_roc_duration - i * dwell;
-
- scan_ch->active_dwell =
- scan_ch->passive_dwell = cpu_to_le16(dwell);
-
- /* Set txpower levels to defaults */
- scan_ch->dsp_atten = 110;
-
- /* NOTE: if we were doing 6Mb OFDM for scans we'd use
- * power level:
- * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3;
- */
- if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ)
- scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
- else
- scan_ch->tx_gain = ((1 << 5) | (5 << 3));
-
- scan_ch++;
- }
- }
-
- break;
}
if (scan->channel_count == 0) {
IWL_DEBUG_SCAN(priv, "Starting %sscan...\n",
scan_type == IWL_SCAN_NORMAL ? "" :
- scan_type == IWL_SCAN_ROC ? "remain-on-channel " :
"internal short ");
set_bit(STATUS_SCANNING, &priv->status);
mutex_unlock(&priv->mutex);
}
}
-
-void iwl_scan_roc_expired(struct iwl_priv *priv)
-{
- /*
- * The status bit should be set here, to prevent a race
- * where the atomic_read returns 1, but before the execution continues
- * iwl_scan_offchannel_skb_status() checks if the status bit is set
- */
- set_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status);
-
- if (atomic_read(&priv->num_aux_in_flight) == 0) {
- ieee80211_remain_on_channel_expired(priv->hw);
- priv->hw_roc_channel = NULL;
- schedule_delayed_work(&priv->hw_roc_disable_work,
- 10 * HZ);
-
- clear_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status);
- } else {
- IWL_DEBUG_SCAN(priv, "ROC done with %d frames in aux\n",
- atomic_read(&priv->num_aux_in_flight));
- }
-}
-
-void iwl_scan_offchannel_skb(struct iwl_priv *priv)
-{
- WARN_ON(!priv->hw_roc_start_notified);
- atomic_inc(&priv->num_aux_in_flight);
-}
-
-void iwl_scan_offchannel_skb_status(struct iwl_priv *priv)
-{
- if (atomic_dec_return(&priv->num_aux_in_flight) == 0 &&
- test_bit(STATUS_SCAN_ROC_EXPIRED, &priv->status)) {
- IWL_DEBUG_SCAN(priv, "0 aux frames. Calling ROC expired\n");
- iwl_scan_roc_expired(priv);
- }
-}
priv->lib->bt_params->advanced_bt_coexist &&
(ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) ||
ieee80211_is_reassoc_req(fc) ||
- skb->protocol == cpu_to_be16(ETH_P_PAE)))
+ info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
tx_flags |= TX_CMD_FLG_IGNORE_BT;
if (sta_priv && sta_priv->client && !is_agg)
atomic_inc(&sta_priv->pending_frames);
- if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
- iwl_scan_offchannel_skb(priv);
-
return 0;
drop_unlock_sta:
struct sk_buff *skb;
struct iwl_rxon_context *ctx;
bool is_agg = (txq_id >= IWLAGN_FIRST_AMPDU_QUEUE);
- bool is_offchannel_skb;
tid = (tx_resp->ra_tid & IWLAGN_TX_RES_TID_MSK) >>
IWLAGN_TX_RES_TID_POS;
__skb_queue_head_init(&skbs);
- is_offchannel_skb = false;
-
if (tx_resp->frame_count == 1) {
u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl);
next_reclaimed = IEEE80211_SEQ_TO_SN(next_reclaimed + 0x10);
if (!is_agg)
iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1);
- is_offchannel_skb =
- (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN);
freed++;
}
if (!is_agg && freed != 1)
IWL_ERR(priv, "Q: %d, freed %d\n", txq_id, freed);
- /*
- * An offchannel frame can be send only on the AUX queue, where
- * there is no aggregation (and reordering) so it only is single
- * skb is expected to be processed.
- */
- if (is_offchannel_skb && freed != 1)
- IWL_ERR(priv, "OFFCHANNEL SKB freed %d\n", freed);
-
IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x)\n", txq_id,
iwl_get_tx_fail_reason(status), status);
ieee80211_tx_status_ni(priv->hw, skb);
}
- if (is_offchannel_skb)
- iwl_scan_offchannel_skb_status(priv);
-
return 0;
}
.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 iwl7000_ht_params = {
.nvm_calib_ver = IWL7260_TX_POWER_VERSION,
};
+const struct iwl_cfg iwl7260_2ac_cfg_high_temp = {
+ .name = "Intel(R) Dual Band Wireless AC 7260",
+ .fw_name_pre = IWL7260_FW_PRE,
+ IWL_DEVICE_7000,
+ .ht_params = &iwl7000_ht_params,
+ .nvm_ver = IWL7260_NVM_VERSION,
+ .nvm_calib_ver = IWL7260_TX_POWER_VERSION,
+ .high_temp = true,
+};
+
const struct iwl_cfg iwl7260_2n_cfg = {
.name = "Intel(R) Dual Band Wireless N 7260",
.fw_name_pre = IWL7260_FW_PRE,
unsigned int wd_timeout;
u32 max_event_log_size;
const bool shadow_reg_enable;
+ const bool pcie_l1_allowed;
};
/*
* @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off)
* @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.
*
* We enable the driver to be backward compatible wrt. hardware features.
* API differences in uCode shouldn't be handled here but through TLVs
enum iwl_led_mode led_mode;
const bool rx_with_siso_diversity;
const bool internal_wimax_coex;
+ bool high_temp;
};
/*
#endif /* CONFIG_IWLDVM */
#if IS_ENABLED(CONFIG_IWLMVM)
extern const struct iwl_cfg iwl7260_2ac_cfg;
+extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp;
extern const struct iwl_cfg iwl7260_2n_cfg;
extern const struct iwl_cfg iwl7260_n_cfg;
extern const struct iwl_cfg iwl3160_2ac_cfg;
#define IWL_DL_RX 0x01000000
#define IWL_DL_ISR 0x02000000
#define IWL_DL_HT 0x04000000
+#define IWL_DL_EXTERNAL 0x08000000
/* 0xF0000000 - 0x10000000 */
#define IWL_DL_11H 0x10000000
#define IWL_DL_STATS 0x20000000
#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a)
#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a)
+#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a)
#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a)
#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a)
#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a)
static inline bool iwl_trace_data(struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- if (ieee80211_is_data(hdr->frame_control))
- return skb->protocol != cpu_to_be16(ETH_P_PAE);
- return false;
+ if (!ieee80211_is_data(hdr->frame_control))
+ return false;
+ return !(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO);
}
static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans,
int i;
bool load_module = false;
- fw->ucode_capa.max_probe_length = 200;
+ fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
fw->ucode_capa.standard_phy_calibration_size =
IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
int ret;
drv = kzalloc(sizeof(*drv), GFP_KERNEL);
- if (!drv)
- return NULL;
+ if (!drv) {
+ ret = -ENOMEM;
+ goto err;
+ }
drv->trans = trans;
drv->dev = trans->dev;
err_free_drv:
#endif
kfree(drv);
-
+err:
return ERR_PTR(ret);
}
* @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_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
*/
enum iwl_ucode_tlv_flag {
- IWL_UCODE_TLV_FLAGS_PAN = BIT(0),
- IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
- 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_PAN = BIT(0),
+ IWL_UCODE_TLV_FLAGS_NEWSCAN = BIT(1),
+ 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_D3_6_IPV6_ADDRS = BIT(10),
+ IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11),
};
/* The default calibrate table size if not specified by firmware file */
#define IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19
#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE 253
+/* The default max probe length if not specified by the firmware file */
+#define IWL_DEFAULT_MAX_PROBE_LENGTH 200
+
/**
* enum iwl_ucode_type
*
#include "iwl-io.h"
#include "iwl-csr.h"
#include "iwl-debug.h"
+#include "iwl-fh.h"
+#include "iwl-csr.h"
#define IWL_POLL_INTERVAL 10 /* microseconds */
}
}
IWL_EXPORT_SYMBOL(iwl_clear_bits_prph);
+
+static const char *get_fh_string(int cmd)
+{
+#define IWL_CMD(x) case x: return #x
+ switch (cmd) {
+ IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
+ IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
+ IWL_CMD(FH_RSCSR_CHNL0_WPTR);
+ IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
+ IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
+ IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
+ IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
+ IWL_CMD(FH_TSSR_TX_STATUS_REG);
+ IWL_CMD(FH_TSSR_TX_ERROR_REG);
+ default:
+ return "UNKNOWN";
+ }
+#undef IWL_CMD
+}
+
+int iwl_dump_fh(struct iwl_trans *trans, char **buf)
+{
+ int i;
+ static const u32 fh_tbl[] = {
+ FH_RSCSR_CHNL0_STTS_WPTR_REG,
+ FH_RSCSR_CHNL0_RBDCB_BASE_REG,
+ FH_RSCSR_CHNL0_WPTR,
+ FH_MEM_RCSR_CHNL0_CONFIG_REG,
+ FH_MEM_RSSR_SHARED_CTRL_REG,
+ FH_MEM_RSSR_RX_STATUS_REG,
+ FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
+ FH_TSSR_TX_STATUS_REG,
+ FH_TSSR_TX_ERROR_REG
+ };
+
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ if (buf) {
+ int pos = 0;
+ size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
+
+ *buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!*buf)
+ return -ENOMEM;
+
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ "FH register values:\n");
+
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
+ pos += scnprintf(*buf + pos, bufsz - pos,
+ " %34s: 0X%08x\n",
+ get_fh_string(fh_tbl[i]),
+ iwl_read_direct32(trans, fh_tbl[i]));
+
+ return pos;
+ }
+#endif
+
+ IWL_ERR(trans, "FH register values:\n");
+ for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
+ IWL_ERR(trans, " %34s: 0X%08x\n",
+ get_fh_string(fh_tbl[i]),
+ iwl_read_direct32(trans, fh_tbl[i]));
+
+ return 0;
+}
u32 bits, u32 mask);
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
+/* Error handling */
+int iwl_dump_fh(struct iwl_trans *trans, char **buf);
+
#endif
#define LAST_2GHZ_HT_PLUS 9
#define LAST_5GHZ_HT 161
+#define DEFAULT_MAX_TX_POWER 16
/* rate data (static) */
static struct ieee80211_rate iwl_cfg80211_rates[] = {
/* Initialize regulatory-based run-time data */
- /* TODO: read the real value from the NVM */
- channel->max_power = 0;
+ /*
+ * Default value - highest tx power value. max_power
+ * is not used in mvm, and is used for backwards compatibility
+ */
+ channel->max_power = DEFAULT_MAX_TX_POWER;
is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
IWL_DEBUG_EEPROM(dev,
"Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
* 1) The driver layer (iwl-drv.c) chooses the op_mode based on the
* capabilities advertized by the fw file (in TLV format).
* 2) The driver layer starts the op_mode (ops->start)
- * 3) The op_mode registers registers mac80211
+ * 3) The op_mode registers mac80211
* 4) The op_mode is governed by mac80211
* 5) The driver layer stops the op_mode
*/
* @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 the this Rx responds to.
+ * HCMD this Rx responds to.
* This callback may sleep, it is called from a threaded IRQ handler.
* @queue_full: notifies that a HW queue is full.
* Must be atomic and called with BH disabled.
* 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 want for the response
+ * @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.
*/
*
* @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's
* ring. The transport layer doesn't map the command's buffer to DMA, but
- * rather copies it to an previously allocated DMA buffer. This flag tells
+ * rather copies it to a previously allocated DMA buffer. This flag tells
* the transport layer not to copy the command, but to map the existing
* buffer (that is passed in) instead. This saves the memcpy and allows
* commands that are bigger than the fixed buffer to be submitted.
* @handler_status: return value of the handler of the command
* (put in setup_rx_handlers) - valid for SYNC mode only
* @flags: can be CMD_*
- * @len: array of the lenths of the chunks in data
+ * @len: array of the lengths of the chunks in data
* @dataflags: IWL_HCMD_DFL_*
* @id: id of the host command
*/
* May sleep
* @dbgfs_register: add the dbgfs files under this directory. Files will be
* automatically deleted.
- * @suspend: stop the device unless WoWLAN is configured
- * @resume: resume activity of the device
* @write8: write a u8 to a register at offset ofs from the BAR
* @write32: write a u32 to a register at offset ofs from the BAR
* @read32: read a u32 register at offset ofs from the BAR
int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir);
int (*wait_tx_queue_empty)(struct iwl_trans *trans);
-#ifdef CONFIG_PM_SLEEP
- int (*suspend)(struct iwl_trans *trans);
- int (*resume)(struct iwl_trans *trans);
-#endif
+
void (*write8)(struct iwl_trans *trans, u32 ofs, u8 val);
void (*write32)(struct iwl_trans *trans, u32 ofs, u32 val);
u32 (*read32)(struct iwl_trans *trans, u32 ofs);
return trans->ops->dbgfs_register(trans, dir);
}
-#ifdef CONFIG_PM_SLEEP
-static inline int iwl_trans_suspend(struct iwl_trans *trans)
-{
- return trans->ops->suspend(trans);
-}
-
-static inline int iwl_trans_resume(struct iwl_trans *trans)
-{
- return trans->ops->resume(trans);
-}
-#endif
-
static inline void iwl_trans_write8(struct iwl_trans *trans, u32 ofs, u8 val)
{
trans->ops->write8(trans, ofs, val);
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 += scan.o time-event.o rs.o
-iwlmvm-y += power.o bt-coex.o
+iwlmvm-y += power.o power_legacy.o bt-coex.o
iwlmvm-y += led.o tt.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
{
- struct iwl_bt_coex_cmd cmd = {
- .max_kill = 5,
- .bt3_time_t7_value = 1,
- .bt3_prio_sample_time = 2,
- .bt3_timer_t2_value = 0xc,
+ 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;
- cmd.flags = iwlwifi_mod_params.bt_coex_active ?
+ /* 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;
- cmd.flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
+ bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE;
- 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);
+ 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(&cmd.decision_lut, iwl_single_shared_ant_lookup,
+ memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup,
sizeof(iwl_single_shared_ant_lookup));
else if (is_loose_coex())
- memcpy(&cmd.decision_lut, iwl_loose_lookup,
+ memcpy(&bt_cmd->decision_lut, iwl_loose_lookup,
sizeof(iwl_tight_lookup));
else
- memcpy(&cmd.decision_lut, iwl_tight_lookup,
+ memcpy(&bt_cmd->decision_lut, iwl_tight_lookup,
sizeof(iwl_tight_lookup));
- cmd.bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST);
- cmd.kill_ack_msk =
+ 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]);
- cmd.kill_cts_msk =
+ 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));
- /* 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;
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
- return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
- sizeof(cmd), &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 cmd = {};
+ 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);
return 0;
mvm->bt_kill_msk = bt_kill_msk;
- cmd.kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
- cmd.kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
- cmd.valid_bit_msk = cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS);
+
+ 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);
- return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_SYNC,
- sizeof(cmd), &cmd);
+
+ 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 cmd = {
- .valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER),
- .bt_reduced_tx_power = sta_id,
+ 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)
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)
- cmd.bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+ 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;
- /* Send ASYNC since this can be sent from an atomic context */
- return iwl_mvm_send_cmd_pdu(mvm, BT_CONFIG, CMD_ASYNC,
- sizeof(cmd), &cmd);
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
}
struct iwl_bt_iterator_data {
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);
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.
void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
- struct ieee80211_chanctx_conf *chanctx_conf;
- enum ieee80211_band band;
-
- 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();
-
- /* if we are in 2GHz we will get a notification from the fw */
- if (band == IEEE80211_BAND_2GHZ)
- return;
-
- /* else, we can remove all the constraints */
- memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
-
iwl_mvm_bt_coex_notif_handle(mvm);
}
--- /dev/null
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************/
+#ifndef __MVM_CONSTANTS_H
+#define __MVM_CONSTANTS_H
+
+#define IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
+#define IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
+#define IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC)
+#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_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
+
+#endif /* __MVM_CONSTANTS_H */
list_for_each_entry(ifa, &idev->addr_list, if_list) {
mvmvif->target_ipv6_addrs[idx] = ifa->addr;
idx++;
- if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS)
+ if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)
break;
}
read_unlock_bh(&idev->lock);
static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
- struct iwl_proto_offload_cmd cmd = {};
+ 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 (mvmvif->num_target_ipv6_addrs) {
- cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_NS);
- memcpy(cmd.ndp_mac_addr, vif->addr, ETH_ALEN);
- }
+ 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]));
- BUILD_BUG_ON(sizeof(cmd.target_ipv6_addr[i]) !=
- sizeof(mvmvif->target_ipv6_addrs[i]));
+ 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);
+ }
- for (i = 0; i < mvmvif->num_target_ipv6_addrs; i++)
- memcpy(cmd.target_ipv6_addr[i],
- &mvmvif->target_ipv6_addrs[i],
- sizeof(cmd.target_ipv6_addr[i]));
+ 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) {
- cmd.enabled |= cpu_to_le32(IWL_D3_PROTO_OFFLOAD_ARP);
- cmd.host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
- memcpy(cmd.arp_mac_addr, vif->addr, ETH_ALEN);
+ 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 (!cmd.enabled)
+ if (!enabled)
return 0;
+ common->enabled = cpu_to_le32(enabled);
+
return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
- sizeof(cmd), &cmd);
+ size, &cmd);
}
enum iwl_mvm_tcp_packet_type {
return __iwl_mvm_suspend(hw, wowlan, false);
}
-static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif)
+static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_wowlan_status *status)
{
- u32 base = mvm->error_event_table;
- struct error_table_start {
- /* cf. struct iwl_error_event_table */
- u32 valid;
- u32 error_id;
- } err_info;
+ struct sk_buff *pkt = NULL;
struct cfg80211_wowlan_wakeup wakeup = {
.pattern_idx = -1,
};
struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
- struct iwl_host_cmd cmd = {
- .id = WOWLAN_GET_STATUSES,
- .flags = CMD_SYNC | CMD_WANT_SKB,
- };
- struct iwl_wowlan_status *status;
- u32 reasons;
- int ret, len;
- struct sk_buff *pkt = NULL;
-
- iwl_trans_read_mem_bytes(mvm->trans, base,
- &err_info, sizeof(err_info));
-
- if (err_info.valid) {
- IWL_INFO(mvm, "error table is valid (%d)\n",
- err_info.valid);
- if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
- wakeup.rfkill_release = true;
- ieee80211_report_wowlan_wakeup(vif, &wakeup,
- GFP_KERNEL);
- }
- return;
- }
-
- /* only for tracing for now */
- ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
- if (ret)
- IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
-
- ret = iwl_mvm_send_cmd(mvm, &cmd);
- if (ret) {
- IWL_ERR(mvm, "failed to query status (%d)\n", ret);
- return;
- }
-
- /* RF-kill already asserted again... */
- if (!cmd.resp_pkt)
- return;
-
- len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
- if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) {
- IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- goto out;
- }
-
- status = (void *)cmd.resp_pkt->data;
-
- if (len - sizeof(struct iwl_cmd_header) !=
- sizeof(*status) +
- ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
- IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
- goto out;
- }
-
- reasons = le32_to_cpu(status->wakeup_reasons);
+ u32 reasons = le32_to_cpu(status->wakeup_reasons);
if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) {
wakeup_report = NULL;
pktsize -= hdrlen;
if (ieee80211_has_protected(hdr->frame_control)) {
+ /*
+ * This is unlocked and using gtk_i(c)vlen,
+ * but since everything is under RTNL still
+ * that's not really a problem - changing
+ * it would be difficult.
+ */
if (is_multicast_ether_addr(hdr->addr1)) {
ivlen = mvm->gtk_ivlen;
icvlen += mvm->gtk_icvlen;
report:
ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
kfree_skb(pkt);
+}
- out:
+/* releases the MVM mutex */
+static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ u32 base = mvm->error_event_table;
+ struct error_table_start {
+ /* cf. struct iwl_error_event_table */
+ u32 valid;
+ u32 error_id;
+ } err_info;
+ struct iwl_host_cmd cmd = {
+ .id = WOWLAN_GET_STATUSES,
+ .flags = CMD_SYNC | CMD_WANT_SKB,
+ };
+ struct iwl_wowlan_status *status;
+ int ret, len;
+
+ iwl_trans_read_mem_bytes(mvm->trans, base,
+ &err_info, sizeof(err_info));
+
+ if (err_info.valid) {
+ IWL_INFO(mvm, "error table is valid (%d)\n",
+ err_info.valid);
+ if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
+ struct cfg80211_wowlan_wakeup wakeup = {
+ .rfkill_release = true,
+ };
+ ieee80211_report_wowlan_wakeup(vif, &wakeup,
+ GFP_KERNEL);
+ }
+ goto out_unlock;
+ }
+
+ /* only for tracing for now */
+ ret = iwl_mvm_send_cmd_pdu(mvm, OFFLOADS_QUERY_CMD, CMD_SYNC, 0, NULL);
+ if (ret)
+ IWL_ERR(mvm, "failed to query offload statistics (%d)\n", ret);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+ if (ret) {
+ IWL_ERR(mvm, "failed to query status (%d)\n", ret);
+ goto out_unlock;
+ }
+
+ /* RF-kill already asserted again... */
+ 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)) {
+ IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+ goto out_free_resp;
+ }
+
+ status = (void *)cmd.resp_pkt->data;
+
+ if (len - sizeof(struct iwl_cmd_header) !=
+ sizeof(*status) +
+ ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) {
+ IWL_ERR(mvm, "Invalid WoWLAN status response!\n");
+ goto out_free_resp;
+ }
+
+ /* 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_free_resp(&cmd);
+ return;
+
+ out_free_resp:
iwl_free_resp(&cmd);
+ out_unlock:
+ mutex_unlock(&mvm->mutex);
}
static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
iwl_mvm_read_d3_sram(mvm);
iwl_mvm_query_wakeup_reasons(mvm, vif);
+ /* has unlocked the mutex, so skip that */
+ goto out;
out_unlock:
mutex_unlock(&mvm->mutex);
+ out:
if (!test && vif)
ieee80211_resume_disconnect(vif);
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;
}
}
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 {
return -EINVAL;
}
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;
- struct iwl_powertable_cmd cmd = {};
- char buf[256];
+ char buf[512];
int bufsz = sizeof(buf);
- int pos = 0;
+ int pos;
- 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));
- }
+ pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
}
#undef BT_MBOX_PRINT
+#define PRINT_STATS_LE32(_str, _val) \
+ pos += scnprintf(buf + pos, bufsz - pos, \
+ fmt_table, _str, \
+ le32_to_cpu(_val))
+
+static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ static const char *fmt_table = "\t%-30s %10u\n";
+ static const char *fmt_header = "%-32s\n";
+ 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;
+ struct mvm_statistics_rx_phy *ofdm;
+ struct mvm_statistics_rx_phy *cck;
+ struct mvm_statistics_rx_non_phy *general;
+ struct mvm_statistics_rx_ht_phy *ht;
+
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+
+ ofdm = &mvm->rx_stats.ofdm;
+ cck = &mvm->rx_stats.cck;
+ general = &mvm->rx_stats.general;
+ ht = &mvm->rx_stats.ofdm_ht;
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - OFDM");
+ PRINT_STATS_LE32("ina_cnt", ofdm->ina_cnt);
+ PRINT_STATS_LE32("fina_cnt", ofdm->fina_cnt);
+ PRINT_STATS_LE32("plcp_err", ofdm->plcp_err);
+ PRINT_STATS_LE32("crc32_err", ofdm->crc32_err);
+ PRINT_STATS_LE32("overrun_err", ofdm->overrun_err);
+ PRINT_STATS_LE32("early_overrun_err", ofdm->early_overrun_err);
+ PRINT_STATS_LE32("crc32_good", ofdm->crc32_good);
+ PRINT_STATS_LE32("false_alarm_cnt", ofdm->false_alarm_cnt);
+ PRINT_STATS_LE32("fina_sync_err_cnt", ofdm->fina_sync_err_cnt);
+ PRINT_STATS_LE32("sfd_timeout", ofdm->sfd_timeout);
+ PRINT_STATS_LE32("fina_timeout", ofdm->fina_timeout);
+ PRINT_STATS_LE32("unresponded_rts", ofdm->unresponded_rts);
+ PRINT_STATS_LE32("rxe_frame_lmt_overrun",
+ ofdm->rxe_frame_limit_overrun);
+ PRINT_STATS_LE32("sent_ack_cnt", ofdm->sent_ack_cnt);
+ PRINT_STATS_LE32("sent_cts_cnt", ofdm->sent_cts_cnt);
+ PRINT_STATS_LE32("sent_ba_rsp_cnt", ofdm->sent_ba_rsp_cnt);
+ PRINT_STATS_LE32("dsp_self_kill", ofdm->dsp_self_kill);
+ PRINT_STATS_LE32("mh_format_err", ofdm->mh_format_err);
+ PRINT_STATS_LE32("re_acq_main_rssi_sum", ofdm->re_acq_main_rssi_sum);
+ PRINT_STATS_LE32("reserved", ofdm->reserved);
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - CCK");
+ PRINT_STATS_LE32("ina_cnt", cck->ina_cnt);
+ PRINT_STATS_LE32("fina_cnt", cck->fina_cnt);
+ PRINT_STATS_LE32("plcp_err", cck->plcp_err);
+ PRINT_STATS_LE32("crc32_err", cck->crc32_err);
+ PRINT_STATS_LE32("overrun_err", cck->overrun_err);
+ PRINT_STATS_LE32("early_overrun_err", cck->early_overrun_err);
+ PRINT_STATS_LE32("crc32_good", cck->crc32_good);
+ PRINT_STATS_LE32("false_alarm_cnt", cck->false_alarm_cnt);
+ PRINT_STATS_LE32("fina_sync_err_cnt", cck->fina_sync_err_cnt);
+ PRINT_STATS_LE32("sfd_timeout", cck->sfd_timeout);
+ PRINT_STATS_LE32("fina_timeout", cck->fina_timeout);
+ PRINT_STATS_LE32("unresponded_rts", cck->unresponded_rts);
+ PRINT_STATS_LE32("rxe_frame_lmt_overrun",
+ cck->rxe_frame_limit_overrun);
+ PRINT_STATS_LE32("sent_ack_cnt", cck->sent_ack_cnt);
+ PRINT_STATS_LE32("sent_cts_cnt", cck->sent_cts_cnt);
+ PRINT_STATS_LE32("sent_ba_rsp_cnt", cck->sent_ba_rsp_cnt);
+ PRINT_STATS_LE32("dsp_self_kill", cck->dsp_self_kill);
+ PRINT_STATS_LE32("mh_format_err", cck->mh_format_err);
+ PRINT_STATS_LE32("re_acq_main_rssi_sum", cck->re_acq_main_rssi_sum);
+ PRINT_STATS_LE32("reserved", cck->reserved);
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - GENERAL");
+ PRINT_STATS_LE32("bogus_cts", general->bogus_cts);
+ PRINT_STATS_LE32("bogus_ack", general->bogus_ack);
+ PRINT_STATS_LE32("non_bssid_frames", general->non_bssid_frames);
+ PRINT_STATS_LE32("filtered_frames", general->filtered_frames);
+ PRINT_STATS_LE32("non_channel_beacons", general->non_channel_beacons);
+ PRINT_STATS_LE32("channel_beacons", general->channel_beacons);
+ PRINT_STATS_LE32("num_missed_bcon", general->num_missed_bcon);
+ PRINT_STATS_LE32("adc_rx_saturation_time",
+ general->adc_rx_saturation_time);
+ PRINT_STATS_LE32("ina_detection_search_time",
+ general->ina_detection_search_time);
+ PRINT_STATS_LE32("beacon_silence_rssi_a",
+ general->beacon_silence_rssi_a);
+ PRINT_STATS_LE32("beacon_silence_rssi_b",
+ general->beacon_silence_rssi_b);
+ PRINT_STATS_LE32("beacon_silence_rssi_c",
+ general->beacon_silence_rssi_c);
+ PRINT_STATS_LE32("interference_data_flag",
+ general->interference_data_flag);
+ PRINT_STATS_LE32("channel_load", general->channel_load);
+ PRINT_STATS_LE32("dsp_false_alarms", general->dsp_false_alarms);
+ PRINT_STATS_LE32("beacon_rssi_a", general->beacon_rssi_a);
+ PRINT_STATS_LE32("beacon_rssi_b", general->beacon_rssi_b);
+ PRINT_STATS_LE32("beacon_rssi_c", general->beacon_rssi_c);
+ PRINT_STATS_LE32("beacon_energy_a", general->beacon_energy_a);
+ 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("directed_data_mpdu", general->directed_data_mpdu);
+
+ pos += scnprintf(buf + pos, bufsz - pos, fmt_header,
+ "Statistics_Rx - HT");
+ PRINT_STATS_LE32("plcp_err", ht->plcp_err);
+ PRINT_STATS_LE32("overrun_err", ht->overrun_err);
+ PRINT_STATS_LE32("early_overrun_err", ht->early_overrun_err);
+ PRINT_STATS_LE32("crc32_good", ht->crc32_good);
+ PRINT_STATS_LE32("crc32_err", ht->crc32_err);
+ PRINT_STATS_LE32("mh_format_err", ht->mh_format_err);
+ PRINT_STATS_LE32("agg_crc32_good", ht->agg_crc32_good);
+ PRINT_STATS_LE32("agg_mpdu_cnt", ht->agg_mpdu_cnt);
+ PRINT_STATS_LE32("agg_cnt", ht->agg_cnt);
+ PRINT_STATS_LE32("unsupport_mcs", ht->unsupport_mcs);
+
+ mutex_unlock(&mvm->mutex);
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ kfree(buf);
+
+ return ret;
+}
+#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)
{
struct iwl_mvm *mvm = file->private_data;
- bool restart_fw = iwlwifi_mod_params.restart_fw;
int ret;
- iwlwifi_mod_params.restart_fw = true;
-
mutex_lock(&mvm->mutex);
+ /* allow one more restart that we're provoking here */
+ if (mvm->restart_fw >= 0)
+ 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);
mutex_unlock(&mvm->mutex);
- iwlwifi_mod_params.restart_fw = restart_fw;
-
return count;
}
case MVM_DEBUGFS_BF_ROAMING_STATE:
dbgfs_bf->bf_roaming_state = value;
break;
- case MVM_DEBUGFS_BF_TEMPERATURE_DELTA:
- dbgfs_bf->bf_temperature_delta = value;
+ 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;
value > IWL_BF_ROAMING_STATE_MAX)
return -EINVAL;
param = MVM_DEBUGFS_BF_ROAMING_STATE;
- } else if (!strncmp("bf_temperature_delta=", buf, 21)) {
- if (sscanf(buf+21, "%d", &value) != 1)
+ } 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;
- if (value < IWL_BF_TEMPERATURE_DELTA_MIN ||
- value > IWL_BF_TEMPERATURE_DELTA_MAX)
+ param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
+ } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
+ if (sscanf(buf+20, "%d", &value) != 1)
return -EINVAL;
- param = MVM_DEBUGFS_BF_TEMPERATURE_DELTA;
+ 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 (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) {
ret = iwl_mvm_disable_beacon_filter(mvm, vif);
} else {
- if (mvmvif->bf_enabled)
- ret = iwl_mvm_enable_beacon_filter(mvm, vif);
- else
- ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ ret = iwl_mvm_enable_beacon_filter(mvm, vif);
}
mutex_unlock(&mvm->mutex);
int pos = 0;
const size_t bufsz = sizeof(buf);
struct iwl_beacon_filter_cmd cmd = {
- .bf_energy_delta = IWL_BF_ENERGY_DELTA_DEFAULT,
- .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT,
- .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT,
- .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT,
- .bf_enable_beacon_filter = IWL_BF_ENABLE_BEACON_FILTER_DEFAULT,
- .bf_debug_flag = 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),
- .ba_enable_beacon_abort = IWL_BA_ENABLE_BEACON_ABORT_DEFAULT,
+ 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_enabled)
- cmd.bf_enable_beacon_filter = 1;
+ 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",
- cmd.bf_energy_delta);
+ le32_to_cpu(cmd.bf_energy_delta));
pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
- cmd.bf_roaming_energy_delta);
+ le32_to_cpu(cmd.bf_roaming_energy_delta));
pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
- cmd.bf_roaming_state);
- pos += scnprintf(buf+pos, bufsz-pos, "bf_temperature_delta = %d\n",
- cmd.bf_temperature_delta);
+ 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",
- cmd.bf_enable_beacon_filter);
+ le32_to_cpu(cmd.bf_enable_beacon_filter));
pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
- cmd.bf_debug_flag);
+ le32_to_cpu(cmd.bf_debug_flag));
pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
- cmd.bf_escape_timer);
+ le32_to_cpu(cmd.bf_escape_timer));
pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
- cmd.ba_escape_timer);
+ le32_to_cpu(cmd.ba_escape_timer));
pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
- cmd.ba_enable_beacon_abort);
+ le32_to_cpu(cmd.ba_enable_beacon_abort));
return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
}
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(fw_rx_stats);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart);
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram);
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(fw_rx_stats, mvm->debugfs_dir, S_IRUSR);
MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
#ifdef CONFIG_PM_SLEEP
MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR);
IWL_D3_PROTO_OFFLOAD_NS = BIT(1),
};
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS 2
+#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
/**
- * struct iwl_proto_offload_cmd - ARP/NS offload configuration
+ * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
* @enabled: enable flags
* @remote_ipv4_addr: remote address to answer to (or zero if all)
* @host_ipv4_addr: our IPv4 address to respond to queries for
* @arp_mac_addr: our MAC address for ARP responses
- * @remote_ipv6_addr: remote address to answer to (or zero if all)
- * @solicited_node_ipv6_addr: broken -- solicited node address exists
- * for each target address
- * @target_ipv6_addr: our target addresses
- * @ndp_mac_addr: neighbor soliciation response MAC address
+ * @reserved: unused
*/
-struct iwl_proto_offload_cmd {
+struct iwl_proto_offload_cmd_common {
__le32 enabled;
__be32 remote_ipv4_addr;
__be32 host_ipv4_addr;
u8 arp_mac_addr[ETH_ALEN];
- __le16 reserved1;
+ __le16 reserved;
+} __packed;
+/**
+ * struct iwl_proto_offload_cmd_v1 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ * for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd_v1 {
+ struct iwl_proto_offload_cmd_common common;
u8 remote_ipv6_addr[16];
u8 solicited_node_ipv6_addr[16];
- u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS][16];
+ u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1][16];
u8 ndp_mac_addr[ETH_ALEN];
__le16 reserved2;
} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_1 */
+/**
+ * struct iwl_proto_offload_cmd_v2 - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @remote_ipv6_addr: remote address to answer to (or zero if all)
+ * @solicited_node_ipv6_addr: broken -- solicited node address exists
+ * for each target address
+ * @target_ipv6_addr: our target addresses
+ * @ndp_mac_addr: neighbor soliciation response MAC address
+ */
+struct iwl_proto_offload_cmd_v2 {
+ struct iwl_proto_offload_cmd_common common;
+ u8 remote_ipv6_addr[16];
+ u8 solicited_node_ipv6_addr[16];
+ u8 target_ipv6_addr[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2][16];
+ u8 ndp_mac_addr[ETH_ALEN];
+ u8 numValidIPv6Addresses;
+ u8 reserved2[3];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
+
/*
* WOWLAN_PATTERNS
* '1' Driver enables PM (use rest of parameters)
* @POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM,
* '1' PM could sleep over DTIM till listen Interval.
+ * @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
+ * access categories are both delivery and trigger enabled.
+ * @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
+ * 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_POWER_SAVE_ENA_MSK = BIT(0),
POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = BIT(1),
POWER_FLAGS_SKIP_OVER_DTIM_MSK = BIT(2),
+ POWER_FLAGS_SNOOZE_ENA_MSK = BIT(5),
+ POWER_FLAGS_BT_SCO_ENA = BIT(8),
POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9),
POWER_FLAGS_LPRX_ENA_MSK = BIT(11),
};
#define IWL_POWER_VEC_SIZE 5
/**
- * struct iwl_powertable_cmd - Power Table Command
+ * struct iwl_powertable_cmd - legacy power command. Beside old API support this
+ * is used also with a new power API for device wide power settings.
* POWER_TABLE_CMD = 0x77 (command, has simple generic response)
*
* @flags: Power table command flags from POWER_FLAGS_*
__le32 lprx_rssi_threshold;
} __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
+ * @flags: Power table command flags from POWER_FLAGS_*
+ * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
+ * Minimum allowed:- 3 * DTIM. Keep alive period must be
+ * set regardless of power scheme or current power state.
+ * FW use this value also when PM is disabled.
+ * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
+ * PSM transition - legacy PM
+ * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
+ * PSM transition - legacy PM
+ * @sleep_interval: not in use
+ * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
+ * is set. For example, if it is required to skip over
+ * one DTIM, this value need to be set to 2 (DTIM periods).
+ * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to
+ * PSM transition - uAPSD
+ * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to
+ * PSM transition - uAPSD
+ * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
+ * Default: 80dbm
+ * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set
+ * @snooze_interval: Maximum time between attempts to retrieve buffered data
+ * from the AP [msec]
+ * @snooze_window: A window of time in which PBW snoozing insures that all
+ * packets received. It is also the minimum time from last
+ * received unicast RX packet, before client stops snoozing
+ * for data. [msec]
+ * @snooze_step: TBD
+ * @qndp_tid: TID client shall use for uAPSD QNDP triggers
+ * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
+ * each corresponding AC.
+ * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
+ * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct
+ * values.
+ * @heavy_tx_thld_packets: TX threshold measured in number of packets
+ * @heavy_rx_thld_packets: RX threshold measured in number of packets
+ * @heavy_tx_thld_percentage: TX threshold measured in load's percentage
+ * @heavy_rx_thld_percentage: RX threshold measured in load's percentage
+ * @limited_ps_threshold:
+*/
+struct iwl_mac_power_cmd {
+ /* CONTEXT_DESC_API_T_VER_1 */
+ __le32 id_and_color;
+
+ /* CLIENT_PM_POWER_TABLE_S_VER_1 */
+ __le16 flags;
+ __le16 keep_alive_seconds;
+ __le32 rx_data_timeout;
+ __le32 tx_data_timeout;
+ __le32 rx_data_timeout_uapsd;
+ __le32 tx_data_timeout_uapsd;
+ u8 lprx_rssi_threshold;
+ u8 skip_dtim_periods;
+ __le16 snooze_interval;
+ __le16 snooze_window;
+ u8 snooze_step;
+ u8 qndp_tid;
+ u8 uapsd_ac_flags;
+ u8 uapsd_max_sp;
+ u8 heavy_tx_thld_packets;
+ u8 heavy_rx_thld_packets;
+ u8 heavy_tx_thld_percentage;
+ u8 heavy_rx_thld_percentage;
+ u8 limited_ps_threshold;
+ u8 reserved;
+} __packed;
+
/**
* struct iwl_beacon_filter_cmd
* REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
* calculated for current beacon is less than the threshold, use
* Roaming Energy Delta Threshold, otherwise use normal Energy Delta
* Threshold. Typical energy threshold is -72dBm.
- * @bf_temperature_delta: Send Beacon to driver if delta in temperature values
- * calculated for this and the last passed beacon is greater than this
- * threshold. Zero value means that the temperature changeis ignored for
+ * @bf_temp_threshold: This threshold determines the type of temperature
+ * filtering (Slow or Fast) that is selected (Units are in Celsuis):
+ * If the current temperature is above this threshold - Fast filter
+ * will be used, If the current temperature is below this threshold -
+ * Slow filter will be used.
+ * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values
+ * calculated for this and the last passed beacon is greater than this
+ * threshold. Zero value means that the temperature change is ignored for
* beacon filtering; beacons will not be forced to be sent to driver
* regardless of whether its temerature has been changed.
+ * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values
+ * calculated for this and the last passed beacon is greater than this
+ * threshold. Zero value means that the temperature change is ignored for
+ * beacon filtering; beacons will not be forced to be sent to driver
+ * regardless of whether its temerature has been changed.
* @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled.
* @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed
* for a specific period of time. Units: Beacons.
* @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled.
*/
struct iwl_beacon_filter_cmd {
- u8 bf_energy_delta;
- u8 bf_roaming_energy_delta;
- u8 bf_roaming_state;
- u8 bf_temperature_delta;
- u8 bf_enable_beacon_filter;
- u8 bf_debug_flag;
- __le16 reserved1;
+ __le32 bf_energy_delta;
+ __le32 bf_roaming_energy_delta;
+ __le32 bf_roaming_state;
+ __le32 bf_temp_threshold;
+ __le32 bf_temp_fast_filter;
+ __le32 bf_temp_slow_filter;
+ __le32 bf_enable_beacon_filter;
+ __le32 bf_debug_flag;
__le32 bf_escape_timer;
__le32 ba_escape_timer;
- u8 ba_enable_beacon_abort;
- u8 reserved2[3];
+ __le32 ba_enable_beacon_abort;
} __packed;
/* Beacon filtering and beacon abort */
#define IWL_BF_ROAMING_STATE_MAX 255
#define IWL_BF_ROAMING_STATE_MIN 0
-#define IWL_BF_TEMPERATURE_DELTA_DEFAULT 5
-#define IWL_BF_TEMPERATURE_DELTA_MAX 255
-#define IWL_BF_TEMPERATURE_DELTA_MIN 0
+#define IWL_BF_TEMP_THRESHOLD_DEFAULT 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_MAX 255
+#define IWL_BF_TEMP_FAST_FILTER_MIN 0
+
+#define IWL_BF_TEMP_SLOW_FILTER_DEFAULT 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_ESCAPE_TIMER_MAX 1024
#define IWL_BF_ESCAPE_TIMER_MIN 0
-#define IWL_BA_ESCAPE_TIMER_DEFAULT 3
+#define IWL_BA_ESCAPE_TIMER_DEFAULT 6
+#define IWL_BA_ESCAPE_TIMER_D3 6
#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 = IWL_BF_ENERGY_DELTA_DEFAULT, \
- .bf_roaming_energy_delta = IWL_BF_ROAMING_ENERGY_DELTA_DEFAULT, \
- .bf_roaming_state = IWL_BF_ROAMING_STATE_DEFAULT, \
- .bf_temperature_delta = IWL_BF_TEMPERATURE_DELTA_DEFAULT, \
- .bf_debug_flag = IWL_BF_DEBUG_FLAG_DEFAULT, \
- .bf_escape_timer = cpu_to_le32(IWL_BF_ESCAPE_TIMER_DEFAULT), \
+#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)
#endif
*@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
*@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
*@SCAN_FLAGS_FRAGMENTED_SCAN:
+ *@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active
+ * in the past hour, even if they are marked as passive.
*/
enum iwl_scan_flags {
SCAN_FLAGS_PERIODIC_SCAN = BIT(0),
SCAN_FLAGS_DELAYED_SCAN_LOWBAND = BIT(2),
SCAN_FLAGS_DELAYED_SCAN_HIGHBAND = BIT(3),
SCAN_FLAGS_FRAGMENTED_SCAN = BIT(4),
+ SCAN_FLAGS_PASSIVE2ACTIVE = BIT(5),
};
/**
* @quiet_time: in msecs, dwell this time for active scan on quiet channels
* @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
* this number of packets were received (typically 1)
- * @passive2active: is auto switching from passive to active allowed (0 or 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
* @suspend_time: how long to pause scan when returning to service channel:
* @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
* @TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration
* @TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation
- * @TX_CMD_FLG_CTS_ONLY: send CTS only, no data after that
* @TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id
* @TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped
* @TX_CMD_FLG_EXEC_PAPD: execute PAPD
TX_CMD_FLG_RESP_TO_DRV = BIT(21),
TX_CMD_FLG_CCMP_AGG = BIT(22),
TX_CMD_FLG_TKIP_MIC_DONE = BIT(23),
- TX_CMD_FLG_CTS_ONLY = BIT(24),
TX_CMD_FLG_DUR = BIT(25),
TX_CMD_FLG_FW_DROP = BIT(26),
TX_CMD_FLG_EXEC_PAPD = BIT(27),
CALIB_RES_NOTIF_PHY_DB = 0x6b,
/* PHY_DB_CMD = 0x6c, */
- /* Power */
+ /* Power - legacy power table command */
POWER_TABLE_CMD = 0x77,
/* Thermal Throttling*/
TX_ANT_CONFIGURATION_CMD = 0x98,
BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
+ REDUCE_TX_POWER_CMD = 0x9f,
/* RF-KILL commands and notifications */
CARD_STATE_CMD = 0xa0,
MISSED_BEACONS_NOTIFICATION = 0xa2,
+ /* Power - new power table command */
+ MAC_PM_POWER_TABLE = 0xa9,
+
REPLY_RX_PHY_CMD = 0xc0,
REPLY_RX_MPDU_CMD = 0xc1,
BA_NOTIF = 0xc5,
__le32 valid;
} __packed;
+/**
+ * struct iwl_reduce_tx_power_cmd - TX power reduction command
+ * REDUCE_TX_POWER_CMD = 0x9f
+ * @flags: (reserved for future implementation)
+ * @mac_context_id: id of the mac ctx for which we are reducing TX power.
+ * @pwr_restriction: TX power restriction in dBms.
+ */
+struct iwl_reduce_tx_power_cmd {
+ u8 flags;
+ u8 mac_context_id;
+ __le16 pwr_restriction;
+} __packed; /* TX_REDUCED_POWER_API_S_VER_1 */
+
/*
* Calibration control struct.
* Sent as part of the phy configuration command.
TE_MAX
}; /* MAC_EVENT_TYPE_API_E_VER_1 */
+
+
+/* Time event - defines for command API v1 */
+
+/*
+ * @TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ * the first fragment is scheduled.
+ * @TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ * the first 2 fragments are scheduled.
+ * @TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
+ * number of fragments are valid.
+ *
+ * Other than the constant defined above, specifying a fragmentation value 'x'
+ * means that the event can be fragmented but only the first 'x' will be
+ * scheduled.
+ */
+enum {
+ TE_V1_FRAG_NONE = 0,
+ TE_V1_FRAG_SINGLE = 1,
+ TE_V1_FRAG_DUAL = 2,
+ TE_V1_FRAG_ENDLESS = 0xffffffff
+};
+
+/* If a Time Event can be fragmented, this is the max number of fragments */
+#define TE_V1_FRAG_MAX_MSK 0x0fffffff
+/* Repeat the time event endlessly (until removed) */
+#define TE_V1_REPEAT_ENDLESS 0xffffffff
+/* If a Time Event has bounded repetitions, this is the maximal value */
+#define TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff
+
/* Time Event dependencies: none, on another TE, or in a specific time */
enum {
- TE_INDEPENDENT = 0,
- TE_DEP_OTHER = 1,
- TE_DEP_TSF = 2,
- TE_EVENT_SOCIOPATHIC = 4,
+ TE_V1_INDEPENDENT = 0,
+ TE_V1_DEP_OTHER = BIT(0),
+ TE_V1_DEP_TSF = BIT(1),
+ TE_V1_EVENT_SOCIOPATHIC = BIT(2),
}; /* MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */
+
/*
+ * @TE_V1_NOTIF_NONE: no notifications
+ * @TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ *
* Supported Time event notifications configuration.
* 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
* notification for monolithic events.
- *
- * @TE_NOTIF_NONE: no notifications
- * @TE_NOTIF_HOST_EVENT_START: request/receive notification on event start
- * @TE_NOTIF_HOST_EVENT_END:request/receive notification on event end
- * @TE_NOTIF_INTERNAL_EVENT_START: internal FW use
- * @TE_NOTIF_INTERNAL_EVENT_END: internal FW use.
- * @TE_NOTIF_HOST_FRAG_START: request/receive notification on frag start
- * @TE_NOTIF_HOST_FRAG_END:request/receive notification on frag end
- * @TE_NOTIF_INTERNAL_FRAG_START: internal FW use.
- * @TE_NOTIF_INTERNAL_FRAG_END: internal FW use.
*/
enum {
- TE_NOTIF_NONE = 0,
- TE_NOTIF_HOST_EVENT_START = 0x1,
- TE_NOTIF_HOST_EVENT_END = 0x2,
- TE_NOTIF_INTERNAL_EVENT_START = 0x4,
- TE_NOTIF_INTERNAL_EVENT_END = 0x8,
- TE_NOTIF_HOST_FRAG_START = 0x10,
- TE_NOTIF_HOST_FRAG_END = 0x20,
- TE_NOTIF_INTERNAL_FRAG_START = 0x40,
- TE_NOTIF_INTERNAL_FRAG_END = 0x80
+ TE_V1_NOTIF_NONE = 0,
+ TE_V1_NOTIF_HOST_EVENT_START = BIT(0),
+ TE_V1_NOTIF_HOST_EVENT_END = BIT(1),
+ TE_V1_NOTIF_INTERNAL_EVENT_START = BIT(2),
+ TE_V1_NOTIF_INTERNAL_EVENT_END = BIT(3),
+ TE_V1_NOTIF_HOST_FRAG_START = BIT(4),
+ TE_V1_NOTIF_HOST_FRAG_END = BIT(5),
+ TE_V1_NOTIF_INTERNAL_FRAG_START = BIT(6),
+ 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 */
+
/*
- * @TE_FRAG_NONE: fragmentation of the time event is NOT allowed.
- * @TE_FRAG_SINGLE: fragmentation of the time event is allowed, but only
+ * @TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
+ * @TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only
* the first fragment is scheduled.
- * @TE_FRAG_DUAL: fragmentation of the time event is allowed, but only
+ * @TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only
* the first 2 fragments are scheduled.
- * @TE_FRAG_ENDLESS: fragmentation of the time event is allowed, and any number
- * of fragments are valid.
+ * @TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any
+ * number of fragments are valid.
*
* Other than the constant defined above, specifying a fragmentation value 'x'
* means that the event can be fragmented but only the first 'x' will be
* scheduled.
*/
enum {
- TE_FRAG_NONE = 0,
- TE_FRAG_SINGLE = 1,
- TE_FRAG_DUAL = 2,
- TE_FRAG_ENDLESS = 0xffffffff
+ TE_V2_FRAG_NONE = 0,
+ TE_V2_FRAG_SINGLE = 1,
+ TE_V2_FRAG_DUAL = 2,
+ TE_V2_FRAG_MAX = 0xfe,
+ TE_V2_FRAG_ENDLESS = 0xff
};
/* Repeat the time event endlessly (until removed) */
-#define TE_REPEAT_ENDLESS (0xffffffff)
+#define TE_V2_REPEAT_ENDLESS 0xff
/* If a Time Event has bounded repetitions, this is the maximal value */
-#define TE_REPEAT_MAX_MSK (0x0fffffff)
-/* If a Time Event can be fragmented, this is the max number of fragments */
-#define TE_FRAG_MAX_MSK (0x0fffffff)
+#define TE_V2_REPEAT_MAX 0xfe
+
+#define TE_V2_PLACEMENT_POS 12
+#define TE_V2_ABSENCE_POS 15
+
+/* Time event policy values (for time event cmd api v2)
+ * 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
+ * notification for monolithic events.
+ *
+ * @TE_V2_DEFAULT_POLICY: independent, social, present, unoticable
+ * @TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start
+ * @TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end
+ * @TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use
+ * @TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use.
+ * @TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start
+ * @TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end
+ * @TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use.
+ * @TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use.
+ * @TE_V2_DEP_OTHER: depends on another time event
+ * @TE_V2_DEP_TSF: depends on a specific time
+ * @TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC
+ * @TE_V2_ABSENCE: are we present or absent during the Time Event.
+ */
+enum {
+ TE_V2_DEFAULT_POLICY = 0x0,
+
+ /* notifications (event start/stop, fragment start/stop) */
+ TE_V2_NOTIF_HOST_EVENT_START = BIT(0),
+ TE_V2_NOTIF_HOST_EVENT_END = BIT(1),
+ TE_V2_NOTIF_INTERNAL_EVENT_START = BIT(2),
+ TE_V2_NOTIF_INTERNAL_EVENT_END = BIT(3),
+
+ TE_V2_NOTIF_HOST_FRAG_START = BIT(4),
+ TE_V2_NOTIF_HOST_FRAG_END = BIT(5),
+ TE_V2_NOTIF_INTERNAL_FRAG_START = BIT(6),
+ TE_V2_NOTIF_INTERNAL_FRAG_END = BIT(7),
+
+ TE_V2_NOTIF_MSK = 0xff,
+
+ /* placement characteristics */
+ TE_V2_DEP_OTHER = BIT(TE_V2_PLACEMENT_POS),
+ TE_V2_DEP_TSF = BIT(TE_V2_PLACEMENT_POS + 1),
+ TE_V2_EVENT_SOCIOPATHIC = BIT(TE_V2_PLACEMENT_POS + 2),
+
+ /* are we present or absent during the Time Event. */
+ TE_V2_ABSENCE = BIT(TE_V2_ABSENCE_POS),
+};
/**
- * struct iwl_time_event_cmd - configuring Time Events
+ * struct iwl_time_event_cmd_api_v2 - 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 )
* @id_and_color: ID and color of the relevant MAC
* @action: action to perform, one of FW_CTXT_ACTION_*
* @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_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
- * @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_NOTIF_* (whom to notify when)
+ * @policy: defines whether uCode shall notify the host or other uCode modules
+ * on event and/or fragment start and/or end
+ * using one of TE_INDEPENDENT, TE_DEP_OTHER, TE_DEP_TSF
+ * TE_EVENT_SOCIOPATHIC
+ * using TE_ABSENCE and using TE_NOTIF_*
*/
-struct iwl_time_event_cmd {
+struct iwl_time_event_cmd_v2 {
/* COMMON_INDEX_HDR_API_S_VER_1 */
__le32 id_and_color;
__le32 action;
__le32 id;
- /* MAC_TIME_EVENT_DATA_API_S_VER_1 */
+ /* MAC_TIME_EVENT_DATA_API_S_VER_2 */
__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 */
+ u8 repeat;
+ u8 max_frags;
+ __le16 policy;
+} __packed; /* MAC_TIME_EVENT_CMD_API_S_VER_2 */
/**
* struct iwl_time_event_resp - response structure to iwl_time_event_cmd
} __packed; /* PHY_CONTEXT_CMD_API_VER_1 */
#define IWL_RX_INFO_PHY_CNT 8
+#define IWL_RX_INFO_ENERGY_ANT_ABC_IDX 1
+#define IWL_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff
+#define IWL_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00
+#define IWL_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000
+#define IWL_RX_INFO_ENERGY_ANT_A_POS 0
+#define IWL_RX_INFO_ENERGY_ANT_B_POS 8
+#define IWL_RX_INFO_ENERGY_ANT_C_POS 16
+
#define IWL_RX_INFO_AGC_IDX 1
#define IWL_RX_INFO_RSSI_AB_IDX 2
#define IWL_OFDM_AGC_A_MSK 0x0000007f
struct mvm_statistics_general_common common;
__le32 beacon_filtered;
__le32 missed_beacons;
- __s8 beacon_filter_everage_energy;
+ __s8 beacon_filter_average_energy;
__s8 beacon_filter_reason;
__s8 beacon_filter_current_energy;
__s8 beacon_filter_reserved;
#define UCODE_VALID_OK cpu_to_le32(0x1)
-/* Default calibration values for WkP - set to INIT image w/o running */
-static const u8 wkp_calib_values_rx_iq_skew[] = { 0x00, 0x00, 0x01, 0x00 };
-static const u8 wkp_calib_values_tx_iq_skew[] = { 0x01, 0x00, 0x00, 0x00 };
-
-struct iwl_calib_default_data {
- u16 size;
- void *data;
-};
-
-#define CALIB_SIZE_N_DATA(_buf) {.size = sizeof(_buf), .data = &_buf}
-
-static const struct iwl_calib_default_data wkp_calib_default_data[12] = {
- [9] = CALIB_SIZE_N_DATA(wkp_calib_values_tx_iq_skew),
- [11] = CALIB_SIZE_N_DATA(wkp_calib_values_rx_iq_skew),
-};
-
struct iwl_mvm_alive_data {
bool valid;
u32 scd_base_addr;
sizeof(phy_cfg_cmd), &phy_cfg_cmd);
}
-static int iwl_set_default_calibrations(struct iwl_mvm *mvm)
-{
- u8 cmd_raw[16]; /* holds the variable size commands */
- struct iwl_set_calib_default_cmd *cmd =
- (struct iwl_set_calib_default_cmd *)cmd_raw;
- int ret, i;
-
- /* Setting default values for calibrations we don't run */
- for (i = 0; i < ARRAY_SIZE(wkp_calib_default_data); i++) {
- u16 cmd_len;
-
- if (wkp_calib_default_data[i].size == 0)
- continue;
-
- memset(cmd_raw, 0, sizeof(cmd_raw));
- cmd_len = wkp_calib_default_data[i].size + sizeof(cmd);
- cmd->calib_index = cpu_to_le16(i);
- cmd->length = cpu_to_le16(wkp_calib_default_data[i].size);
- if (WARN_ONCE(cmd_len > sizeof(cmd_raw),
- "Need to enlarge cmd_raw to %d\n", cmd_len))
- break;
- memcpy(cmd->data, wkp_calib_default_data[i].data,
- wkp_calib_default_data[i].size);
- ret = iwl_mvm_send_cmd_pdu(mvm, SET_CALIB_DEFAULT_CMD, 0,
- sizeof(*cmd) +
- wkp_calib_default_data[i].size,
- cmd);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
{
struct iwl_notification_wait calib_wait;
if (ret)
goto error;
- /* need to set default values */
- ret = iwl_set_default_calibrations(mvm);
- if (ret)
- goto error;
-
/*
* Send phy configurations command to init uCode
* to start the 16.0 uCode init image internal calibrations.
return 0;
/* Therefore, in recovery, we can't get here */
- WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status));
+ if (WARN_ON_ONCE(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)))
+ return -EBUSY;
mvmvif->id = find_first_bit(data.available_mac_ids,
NUM_MAC_INDEX_DRIVER);
IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TIMING_BEACON_ONLY |
- IEEE80211_HW_CONNECTION_MONITOR;
+ IEEE80211_HW_CONNECTION_MONITOR |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
+ IEEE80211_HW_SUPPORTS_STATIC_SMPS |
+ IEEE80211_HW_SUPPORTS_UAPSD;
hw->queues = IWL_MVM_FIRST_AGG_QUEUE;
hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE;
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);
mutex_lock(&mvm->mutex);
- /* Allocate resources for the MAC context, and add it the the fw */
+ /* Allocate resources for the MAC context, and add it to the fw */
ret = iwl_mvm_mac_ctxt_init(mvm, vif);
if (ret)
goto out_unlock;
goto out_release;
}
+ iwl_mvm_vif_dbgfs_register(mvm, vif);
goto out_unlock;
}
iwl_mvm_power_update_mode(mvm, vif);
/* beacon filtering */
+ ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+ if (ret)
+ goto out_remove_mac;
+
if (!mvm->bf_allowed_vif &&
- vif->type == NL80211_IFTYPE_STATION && !vif->p2p){
+ vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+ mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED){
mvm->bf_allowed_vif = mvmvif;
- vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI;
}
- ret = iwl_mvm_disable_beacon_filter(mvm, vif);
- if (ret)
- goto out_release;
-
/*
* P2P_DEVICE interface does not have a channel context assigned to it,
* so a dedicated PHY context is allocated to it and the corresponding
mvmvif->phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
if (!mvmvif->phy_ctxt) {
ret = -ENOSPC;
- goto out_remove_mac;
+ goto out_free_bf;
}
iwl_mvm_phy_ctxt_ref(mvm, mvmvif->phy_ctxt);
iwl_mvm_binding_remove_vif(mvm, vif);
out_unref_phy:
iwl_mvm_phy_ctxt_unref(mvm, mvmvif->phy_ctxt);
+ out_free_bf:
+ if (mvm->bf_allowed_vif == mvmvif) {
+ mvm->bf_allowed_vif = NULL;
+ vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI);
+ }
out_remove_mac:
mvmvif->phy_ctxt = NULL;
iwl_mvm_mac_ctxt_remove(mvm, vif);
if (mvm->bf_allowed_vif == mvmvif) {
mvm->bf_allowed_vif = NULL;
- vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
+ vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER |
+ IEEE80211_VIF_SUPPORTS_CQM_RSSI);
}
iwl_mvm_vif_dbgfs_clean(mvm, vif);
mutex_unlock(&mvm->mutex);
}
+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, CMD_SYNC,
+ sizeof(reduce_txpwr_cmd),
+ &reduce_txpwr_cmd);
+}
+
static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
{
return 0;
IWL_ERR(mvm, "failed to update quotas\n");
return;
}
- iwl_mvm_bt_coex_vif_assoc(mvm, vif);
iwl_mvm_configure_mcast_filter(mvm, vif);
} else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
/* remove AP station now that the MAC is unassoc */
if (ret)
IWL_ERR(mvm, "failed to update quotas\n");
}
- ret = iwl_mvm_power_update_mode(mvm, vif);
- if (ret)
- IWL_ERR(mvm, "failed to update power mode\n");
+
+ /* 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);
} else if (changes & BSS_CHANGED_BEACON_INFO) {
/*
* We received a beacon _after_ association so
*/
iwl_mvm_remove_time_event(mvm, mvmvif,
&mvmvif->time_event_data);
- } else if (changes & BSS_CHANGED_PS) {
+ } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) {
ret = iwl_mvm_power_update_mode(mvm, vif);
if (ret)
IWL_ERR(mvm, "failed to update power mode\n");
}
+ if (changes & BSS_CHANGED_TXPOWER) {
+ IWL_DEBUG_CALIB(mvm, "Changing TX Power to %d\n",
+ bss_conf->txpower);
+ iwl_mvm_set_tx_power(mvm, vif, bss_conf->txpower);
+ }
+
+ if (changes & BSS_CHANGED_CQM) {
+ IWL_DEBUG_MAC80211(mvm, "cqm info_changed");
+ /* 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");
+ }
}
static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
#include "iwl-trans.h"
#include "sta.h"
#include "fw-api.h"
+#include "constants.h"
#define IWL_INVALID_MAC80211_QUEUE 0xff
#define IWL_MVM_MAX_ADDRESSES 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;
+
/**
* struct iwl_mvm_mod_params - module parameters for iwlmvm
* @init_dbg: if true, then the NIC won't be stopped if the INIT fw asserted.
};
#define IWL_CONN_MAX_LISTEN_INTERVAL 70
+#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_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),
};
struct iwl_dbgfs_pm {
- u8 keep_alive_seconds;
+ u16 keep_alive_seconds;
u32 rx_data_timeout;
u32 tx_data_timeout;
bool skip_over_dtim;
bool disable_power_off;
bool lprx_ena;
u32 lprx_rssi_threshold;
+ bool snooze_ena;
int mask;
};
MVM_DEBUGFS_BF_ENERGY_DELTA = BIT(0),
MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA = BIT(1),
MVM_DEBUGFS_BF_ROAMING_STATE = BIT(2),
- MVM_DEBUGFS_BF_TEMPERATURE_DELTA = BIT(3),
- MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(4),
- MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(5),
- MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(6),
- MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(7),
- MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(8),
+ MVM_DEBUGFS_BF_TEMP_THRESHOLD = BIT(3),
+ MVM_DEBUGFS_BF_TEMP_FAST_FILTER = BIT(4),
+ MVM_DEBUGFS_BF_TEMP_SLOW_FILTER = BIT(5),
+ MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER = BIT(6),
+ MVM_DEBUGFS_BF_DEBUG_FLAG = BIT(7),
+ MVM_DEBUGFS_BF_ESCAPE_TIMER = BIT(8),
+ MVM_DEBUGFS_BA_ESCAPE_TIMER = BIT(9),
+ MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT = BIT(10),
};
struct iwl_dbgfs_bf {
- u8 bf_energy_delta;
- u8 bf_roaming_energy_delta;
- u8 bf_roaming_state;
- u8 bf_temperature_delta;
- u8 bf_enable_beacon_filter;
- u8 bf_debug_flag;
+ u32 bf_energy_delta;
+ u32 bf_roaming_energy_delta;
+ u32 bf_roaming_state;
+ u32 bf_temp_threshold;
+ u32 bf_temp_fast_filter;
+ u32 bf_temp_slow_filter;
+ u32 bf_enable_beacon_filter;
+ u32 bf_debug_flag;
u32 bf_escape_timer;
u32 ba_escape_timer;
- u8 ba_enable_beacon_abort;
+ u32 ba_enable_beacon_abort;
int mask;
};
#endif
NUM_IWL_MVM_SMPS_REQ,
};
+/**
+* struct iwl_mvm_vif_bf_data - beacon filtering related data
+* @bf_enabled: indicates if beacon filtering is enabled
+* @ba_enabled: indicated if beacon abort is enabled
+* @last_beacon_signal: last beacon rssi signal in dbm
+* @ave_beacon_signal: average beacon signal
+* @last_cqm_event: rssi of the last cqm event
+*/
+struct iwl_mvm_vif_bf_data {
+ bool bf_enabled;
+ bool ba_enabled;
+ s8 ave_beacon_signal;
+ s8 last_cqm_event;
+};
+
/**
* struct iwl_mvm_vif - data per Virtual Interface, it is a MAC context
* @id: between 0 and 3
bool uploaded;
bool ap_active;
bool monitor_active;
- /* indicate whether beacon filtering is enabled */
- bool bf_enabled;
+ struct iwl_mvm_vif_bf_data bf_data;
u32 ap_beacon_time;
#if IS_ENABLED(CONFIG_IPV6)
/* IPv6 addresses for WoWLAN */
- struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS];
+ struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
int num_target_ipv6_addrs;
#endif
#endif
struct iwl_notif_wait_data notif_wait;
+ struct mvm_statistics_rx rx_stats;
+
unsigned long transport_queue_stop;
u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
atomic_t queue_stop_count[IWL_MAX_HW_QUEUES];
*/
u8 vif_count;
+ /* -1 for always, 0 for never, >0 for that many times */
+ s8 restart_fw;
+
struct led_classdev led;
struct ieee80211_vif *p2p_device_vif;
/* Thermal Throttling and CTkill */
struct iwl_mvm_tt_mgmt thermal_throttle;
s32 temperature; /* Celsius */
+
+ const struct iwl_mvm_power_ops *pm_ops;
};
/* Extract MVM priv from op_mode and _hw */
enum ieee80211_band band);
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);
u8 first_antenna(u8 mask);
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
u8 flags, bool init);
/* power managment */
-int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
-void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_powertable_cmd *cmd);
+static inline int iwl_mvm_power_update_mode(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ return mvm->pm_ops->power_update_mode(mvm, vif);
+}
+
+static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ 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);
struct ieee80211_vif *vif);
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);
/* SMPS */
void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_CMD),
CMD(STATISTICS_NOTIFICATION),
+ CMD(REDUCE_TX_POWER_CMD),
CMD(TX_ANT_CONFIGURATION_CMD),
CMD(D3_CONFIG_CMD),
CMD(PROT_OFFLOAD_CONFIG_CMD),
CMD(MCAST_FILTER_CMD),
CMD(REPLY_BEACON_FILTERING_CMD),
CMD(REPLY_THERMAL_MNG_BACKOFF),
+ CMD(MAC_PM_POWER_TABLE),
};
#undef CMD
mvm->fw = fw;
mvm->hw = hw;
+ mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0;
+
mutex_init(&mvm->mutex);
spin_lock_init(&mvm->async_handlers_lock);
INIT_LIST_HEAD(&mvm->time_event_list);
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));
+
return op_mode;
out_unregister:
ieee80211_free_txskb(mvm->hw, skb);
}
+struct iwl_mvm_reprobe {
+ struct device *dev;
+ struct work_struct work;
+};
+
+static void iwl_mvm_reprobe_wk(struct work_struct *wk)
+{
+ struct iwl_mvm_reprobe *reprobe;
+
+ reprobe = container_of(wk, struct iwl_mvm_reprobe, work);
+ if (device_reprobe(reprobe->dev))
+ dev_err(reprobe->dev, "reprobe failed!\n");
+ kfree(reprobe);
+ module_put(THIS_MODULE);
+}
+
static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
{
iwl_abort_notification_waits(&mvm->notif_wait);
* can't recover this since we're already half suspended.
*/
if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
- IWL_ERR(mvm, "Firmware error during reconfiguration! Abort.\n");
- } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
- iwlwifi_mod_params.restart_fw) {
+ struct iwl_mvm_reprobe *reprobe;
+
+ IWL_ERR(mvm,
+ "Firmware error during reconfiguration - reprobe!\n");
+
+ /*
+ * get a module reference to avoid doing this while unloading
+ * anyway and to avoid scheduling a work with code that's
+ * being removed.
+ */
+ if (!try_module_get(THIS_MODULE)) {
+ IWL_ERR(mvm, "Module is being unloaded - abort\n");
+ return;
+ }
+
+ reprobe = kzalloc(sizeof(*reprobe), GFP_ATOMIC);
+ if (!reprobe) {
+ module_put(THIS_MODULE);
+ return;
+ }
+ reprobe->dev = mvm->trans->dev;
+ 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
break;
}
+ if (mvm->restart_fw > 0)
+ mvm->restart_fw--;
ieee80211_restart_hw(mvm->hw);
}
}
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);
iwl_mvm_nic_restart(mvm);
}
#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)
+int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
+ struct iwl_beacon_filter_cmd *cmd)
{
int ret;
if (!ret) {
IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
- cmd->ba_enable_beacon_abort);
+ le32_to_cpu(cmd->ba_enable_beacon_abort));
IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
- cmd->ba_escape_timer);
+ le32_to_cpu(cmd->ba_escape_timer));
IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
- cmd->bf_debug_flag);
+ le32_to_cpu(cmd->bf_debug_flag));
IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
- cmd->bf_enable_beacon_filter);
+ le32_to_cpu(cmd->bf_enable_beacon_filter));
IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
- cmd->bf_energy_delta);
+ le32_to_cpu(cmd->bf_energy_delta));
IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
- cmd->bf_escape_timer);
+ le32_to_cpu(cmd->bf_escape_timer));
IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
- cmd->bf_roaming_energy_delta);
+ le32_to_cpu(cmd->bf_roaming_energy_delta));
IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
- cmd->bf_roaming_state);
- IWL_DEBUG_POWER(mvm, "bf_temperature_delta is: %d\n",
- cmd->bf_temperature_delta);
+ 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;
}
-static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
- struct ieee80211_vif *vif, bool enable)
+static
+void iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_beacon_filter_cmd *cmd)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (vif->bss_conf.cqm_rssi_thold) {
+ cmd->bf_energy_delta =
+ cpu_to_le32(vif->bss_conf.cqm_rssi_hyst);
+ /* fw uses an absolute value for this */
+ cmd->bf_roaming_state =
+ cpu_to_le32(-vif->bss_conf.cqm_rssi_thold);
+ }
+ 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 = 1,
- .ba_enable_beacon_abort = enable,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
+ .ba_enable_beacon_abort = cpu_to_le32(enable),
};
- if (!mvmvif->bf_enabled)
+ 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_powertable_cmd *cmd)
+ struct iwl_mac_power_cmd *cmd)
{
IWL_DEBUG_POWER(mvm,
- "Sending power table command for power level %d, flags = 0x%X\n",
- iwlmvm_mod_params.power_scheme,
+ "Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
+ cmd->id_and_color, 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));
+ IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
+ le16_to_cpu(cmd->keep_alive_seconds));
+
+ if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {
+ IWL_DEBUG_POWER(mvm, "Disable power management\n");
+ return;
+ }
+
+ 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",
+ 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",
+ cmd->lprx_rssi_threshold);
+ if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
+ IWL_DEBUG_POWER(mvm, "uAPSD enabled\n");
+ IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
+ le32_to_cpu(cmd->rx_data_timeout_uapsd));
+ IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n",
+ le32_to_cpu(cmd->tx_data_timeout_uapsd));
+ IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid);
+ IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags);
+ IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp);
}
}
-void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
- struct iwl_powertable_cmd *cmd)
+static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct iwl_mac_power_cmd *cmd)
{
struct ieee80211_hw *hw = mvm->hw;
struct ieee80211_chanctx_conf *chanctx_conf;
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));
+ dtimper = hw->conf.ps_dtim_period ?: 1;
/*
* Regardless of power management state the driver must set
* keep alive period. FW will use it for sending keep alive NDPs
- * immediately after association.
+ * immediately after association. Check that keep alive period
+ * is at least 3 * DTIM
*/
- cmd->keep_alive_seconds = POWER_KEEP_ALIVE_PERIOD_SEC;
+ dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+ keep_alive = max_t(int, 3 * dtimper_msec,
+ MSEC_PER_SEC * POWER_KEEP_ALIVE_PERIOD_SEC);
+ 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)
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 &&
(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);
+ cmd->lprx_rssi_threshold = 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);
(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);
+ cmd->skip_dtim_periods = 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);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
} else {
- cmd->rx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
- cmd->tx_data_timeout = cpu_to_le32(10 * USEC_PER_MSEC);
+ cmd->rx_data_timeout =
+ cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+ cmd->tx_data_timeout =
+ 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;
+
+ 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;
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
- cmd->keep_alive_seconds = mvmvif->dbgfs_pm.keep_alive_seconds;
+ cmd->keep_alive_seconds =
+ cpu_to_le16(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 |=
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);
+ cmd->skip_dtim_periods = 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);
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);
+ cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
+ if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
+ if (mvmvif->dbgfs_pm.snooze_ena)
+ cmd->flags |=
+ cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+ else
+ cmd->flags &=
+ cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
+ }
#endif /* CONFIG_IWLWIFI_DEBUGFS */
}
-int iwl_mvm_power_update_mode(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
{
int ret;
bool ba_enable;
- struct iwl_powertable_cmd cmd = {};
+ 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);
- ret = iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC,
+ ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
sizeof(cmd), &cmd);
if (ret)
return ret;
return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
}
-int iwl_mvm_power_disable(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
{
- struct iwl_powertable_cmd cmd = {};
+ struct iwl_mac_power_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;
+ cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
+ mvmvif->color));
+
if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
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,
+ return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
sizeof(cmd), &cmd);
}
#ifdef CONFIG_IWLWIFI_DEBUGFS
+static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif, char *buf,
+ int bufsz)
+{
+ struct iwl_mac_power_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, "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",
+ 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);
+ }
+ }
+ return pos;
+}
+
void
iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
struct iwl_beacon_filter_cmd *cmd)
struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
- cmd->bf_energy_delta = dbgfs_bf->bf_energy_delta;
+ cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);
if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
cmd->bf_roaming_energy_delta =
- dbgfs_bf->bf_roaming_energy_delta;
+ cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);
if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
- cmd->bf_roaming_state = dbgfs_bf->bf_roaming_state;
- if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMPERATURE_DELTA)
- cmd->bf_temperature_delta = dbgfs_bf->bf_temperature_delta;
+ cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)
+ cmd->bf_temp_threshold =
+ cpu_to_le32(dbgfs_bf->bf_temp_threshold);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)
+ cmd->bf_temp_fast_filter =
+ cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);
+ if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)
+ cmd->bf_temp_slow_filter =
+ cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);
if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
- cmd->bf_debug_flag = dbgfs_bf->bf_debug_flag;
+ cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);
if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
- cmd->ba_enable_beacon_abort = dbgfs_bf->ba_enable_beacon_abort;
+ cmd->ba_enable_beacon_abort =
+ cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);
}
#endif
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 = 1,
+ .bf_enable_beacon_filter = cpu_to_le32(1),
};
int ret;
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);
if (!ret)
- mvmvif->bf_enabled = true;
+ mvmvif->bf_data.bf_enabled = true;
return ret;
}
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
- if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+ if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) ||
+ vif->type != NL80211_IFTYPE_STATION || vif->p2p)
return 0;
ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
if (!ret)
- mvmvif->bf_enabled = false;
+ mvmvif->bf_data.bf_enabled = false;
return ret;
}
+
+int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!mvmvif->bf_data.bf_enabled)
+ return 0;
+
+ return iwl_mvm_enable_beacon_filter(mvm, vif);
+}
+
+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,
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+ .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
+#endif
+};
--- /dev/null
+/******************************************************************************
+ *
+ * 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
+};
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_bindings, quota, quota_rem;
+ struct iwl_time_quota_cmd cmd = {};
+ int i, idx, ret, num_active_macs, quota, quota_rem;
struct iwl_mvm_quota_iterator_data data = {
.n_interfaces = {},
.colors = { -1, -1, -1, -1 },
.new_vif = newvif,
};
+ lockdep_assert_held(&mvm->mutex);
+
/* update all upon completion */
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
return 0;
- BUILD_BUG_ON(data.colors[MAX_BINDINGS - 1] != -1);
-
- lockdep_assert_held(&mvm->mutex);
-
- memset(&cmd, 0, sizeof(cmd));
+ /* iterator data above must match */
+ BUILD_BUG_ON(MAX_BINDINGS != 4);
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
* IWL_MVM_MAX_QUOTA fragments. Divide these fragments
* equally between all the bindings that require quota
*/
- num_active_bindings = 0;
+ num_active_macs = 0;
for (i = 0; i < MAX_BINDINGS; i++) {
cmd.quotas[i].id_and_color = cpu_to_le32(FW_CTXT_INVALID);
- if (data.n_interfaces[i] > 0)
- num_active_bindings++;
+ num_active_macs += data.n_interfaces[i];
}
quota = 0;
quota_rem = 0;
- if (num_active_bindings) {
- quota = IWL_MVM_MAX_QUOTA / num_active_bindings;
- quota_rem = IWL_MVM_MAX_QUOTA % num_active_bindings;
+ if (num_active_macs) {
+ quota = IWL_MVM_MAX_QUOTA / num_active_macs;
+ quota_rem = IWL_MVM_MAX_QUOTA % num_active_macs;
}
for (idx = 0, i = 0; i < MAX_BINDINGS; i++) {
cmd.quotas[idx].quota = cpu_to_le32(0);
cmd.quotas[idx].max_duration = cpu_to_le32(0);
} else {
- cmd.quotas[idx].quota = cpu_to_le32(quota);
+ cmd.quotas[idx].quota =
+ cpu_to_le32(quota * data.n_interfaces[i]);
cmd.quotas[idx].max_duration =
cpu_to_le32(IWL_MVM_MAX_QUOTA);
}
#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ)
static u8 rs_ht_to_legacy[] = {
- IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
- IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX,
- IWL_RATE_6M_INDEX,
- IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX,
- IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX,
- IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX,
- IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX
+ [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,
};
static const u8 ant_toggle_lookup[] = {
- /*ANT_NONE -> */ ANT_NONE,
- /*ANT_A -> */ ANT_B,
- /*ANT_B -> */ ANT_C,
- /*ANT_AB -> */ ANT_BC,
- /*ANT_C -> */ ANT_A,
- /*ANT_AC -> */ ANT_AB,
- /*ANT_BC -> */ ANT_AC,
- /*ANT_ABC -> */ ANT_ABC,
+ [ANT_NONE] = ANT_NONE,
+ [ANT_A] = ANT_B,
+ [ANT_B] = ANT_C,
+ [ANT_AB] = ANT_BC,
+ [ANT_C] = ANT_A,
+ [ANT_AC] = ANT_AB,
+ [ANT_BC] = ANT_AC,
+ [ANT_ABC] = ANT_ABC,
};
-#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \
+#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_MIMO3_##s##M_PLCP,\
- IWL_RATE_##r##M_IEEE, \
- IWL_RATE_##ip##M_INDEX, \
- IWL_RATE_##in##M_INDEX, \
IWL_RATE_##rp##M_INDEX, \
- IWL_RATE_##rn##M_INDEX, \
- IWL_RATE_##pp##M_INDEX, \
- IWL_RATE_##np##M_INDEX }
+ IWL_RATE_##rn##M_INDEX }
/*
* Parameter order:
- * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate
+ * rate, ht rate, prev rate, next rate
*
* If there isn't a valid next or previous rate then INV is used which
* maps to IWL_RATE_INVALID
*
*/
static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
- IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */
- IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */
- IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */
- IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */
- IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */
- IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */
- IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */
- IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */
- IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */
- IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */
- IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */
- IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */
- IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */
+ IWL_DECLARE_RATE_INFO(1, INV, INV, 2), /* 1mbps */
+ 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) */
};
if (rate_n_flags & RATE_MCS_HT_MSK) {
idx = rs_extract_rate(rate_n_flags);
- if (idx >= IWL_RATE_MIMO3_6M_PLCP)
- idx = idx - IWL_RATE_MIMO3_6M_PLCP;
- else if (idx >= IWL_RATE_MIMO2_6M_PLCP)
+ WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP);
+ if (idx >= IWL_RATE_MIMO2_6M_PLCP)
idx = idx - IWL_RATE_MIMO2_6M_PLCP;
idx += IWL_FIRST_OFDM_RATE;
#ifdef CONFIG_MAC80211_DEBUGFS
static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
- u32 *rate_n_flags, int index);
+ u32 *rate_n_flags);
#else
static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
- u32 *rate_n_flags, int index)
+ u32 *rate_n_flags)
{}
#endif
{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] = {
- {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] = {
- {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 */
- {0, 0, 0, 0, 277, 0, 478, 624, 737, 911, 1026, 1070, 1109}, /* AGG+SGI */
-};
-
/* mbps, mcs */
static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = {
{ "1", "BPSK DSSS"},
return (ant_type & valid_antenna) == ant_type;
}
-/*
- * removes the old data from the statistics. All data that is older than
- * TID_MAX_TIME_DIFF, will be deleted.
- */
-static void rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time)
-{
- /* The oldest age we want to keep */
- u32 oldest_time = curr_time - TID_MAX_TIME_DIFF;
-
- while (tl->queue_count &&
- (tl->time_stamp < oldest_time)) {
- tl->total -= tl->packet_count[tl->head];
- tl->packet_count[tl->head] = 0;
- tl->time_stamp += TID_QUEUE_CELL_SPACING;
- tl->queue_count--;
- tl->head++;
- if (tl->head >= TID_QUEUE_MAX_SIZE)
- tl->head = 0;
- }
-}
-
-/*
- * increment traffic load value for tid and also remove
- * any old values if passed the certain time period
- */
-static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data,
- struct ieee80211_hdr *hdr)
-{
- u32 curr_time = jiffies_to_msecs(jiffies);
- u32 time_diff;
- s32 index;
- struct iwl_traffic_load *tl = NULL;
- u8 tid;
-
- if (ieee80211_is_data_qos(hdr->frame_control)) {
- u8 *qc = ieee80211_get_qos_ctl(hdr);
- tid = qc[0] & 0xf;
- } else {
- return IWL_MAX_TID_COUNT;
- }
-
- if (unlikely(tid >= IWL_MAX_TID_COUNT))
- return IWL_MAX_TID_COUNT;
-
- tl = &lq_data->load[tid];
-
- curr_time -= curr_time % TID_ROUND_VALUE;
-
- /* Happens only for the first packet. Initialize the data */
- if (!(tl->queue_count)) {
- tl->total = 1;
- tl->time_stamp = curr_time;
- tl->queue_count = 1;
- tl->head = 0;
- tl->packet_count[0] = 1;
- return IWL_MAX_TID_COUNT;
- }
-
- time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
- index = time_diff / TID_QUEUE_CELL_SPACING;
-
- /* The history is too long: remove data that is older than */
- /* TID_MAX_TIME_DIFF */
- if (index >= TID_QUEUE_MAX_SIZE)
- rs_tl_rm_old_stats(tl, curr_time);
-
- index = (tl->head + index) % TID_QUEUE_MAX_SIZE;
- tl->packet_count[index] = tl->packet_count[index] + 1;
- tl->total = tl->total + 1;
-
- if ((index + 1) > tl->queue_count)
- tl->queue_count = index + 1;
-
- return tid;
-}
-
#ifdef CONFIG_MAC80211_DEBUGFS
/**
* Program the device to use fixed rate for frame transmit
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 */
- lq_sta->active_mimo3_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);
}
#endif
-/*
- get the traffic load value for tid
-*/
-static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid)
-{
- u32 curr_time = jiffies_to_msecs(jiffies);
- u32 time_diff;
- s32 index;
- struct iwl_traffic_load *tl = NULL;
-
- if (tid >= IWL_MAX_TID_COUNT)
- return 0;
-
- tl = &(lq_data->load[tid]);
-
- curr_time -= curr_time % TID_ROUND_VALUE;
-
- if (!(tl->queue_count))
- return 0;
-
- time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time);
- index = time_diff / TID_QUEUE_CELL_SPACING;
-
- /* The history is too long: remove data that is older than */
- /* TID_MAX_TIME_DIFF */
- if (index >= TID_QUEUE_MAX_SIZE)
- rs_tl_rm_old_stats(tl, curr_time);
-
- return tl->total;
-}
-
static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm,
struct iwl_lq_sta *lq_data, u8 tid,
struct ieee80211_sta *sta)
{
int ret = -EAGAIN;
- u32 load;
-
- load = rs_tl_get_load(lq_data, tid);
/*
* Don't create TX aggregation sessions when in high
else if (is_mimo2(tbl->lq_type))
rate_n_flags |= iwl_rates[index].plcp_mimo2;
else
- rate_n_flags |= iwl_rates[index].plcp_mimo3;
+ WARN_ON_ONCE(1);
} else {
IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type);
}
u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags);
u8 mcs;
- memset(tbl, 0, sizeof(struct iwl_scale_tbl_info));
+ memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win));
*rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags);
if (*rate_idx == IWL_RATE_INVALID) {
} else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) {
if (num_of_ant == 2)
tbl->lq_type = LQ_MIMO2;
- /* MIMO3 */
} else {
- if (num_of_ant == 3) {
- tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
- tbl->lq_type = LQ_MIMO3;
- }
+ WARN_ON_ONCE(num_of_ant == 3);
}
}
return 0;
} else {
if (is_siso(rate_type))
return lq_sta->active_siso_rate;
- else if (is_mimo2(rate_type))
+ else {
+ WARN_ON_ONCE(!is_mimo2(rate_type));
return lq_sta->active_mimo2_rate;
- else
- return lq_sta->active_mimo3_rate;
+ }
}
}
}
/* Choose among many HT tables depending on number of streams
- * (SISO/MIMO2/MIMO3), channel width (20/40), SGI, and aggregation
+ * (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;
ht_tbl_pointer = expected_tpt_siso40MHz;
else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40)
ht_tbl_pointer = expected_tpt_mimo2_20MHz;
- else if (is_mimo2(tbl->lq_type))
+ else {
+ WARN_ON_ONCE(!is_mimo2(tbl->lq_type));
ht_tbl_pointer = expected_tpt_mimo2_40MHz;
- else if (is_mimo3(tbl->lq_type) && !tbl->is_ht40)
- ht_tbl_pointer = expected_tpt_mimo3_20MHz;
- else /* if (is_mimo3(tbl->lq_type)) <-- must be true */
- ht_tbl_pointer = expected_tpt_mimo3_40MHz;
+ }
if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */
tbl->expected_tpt = ht_tbl_pointer[0];
return 0;
}
-/*
- * Set up search table for MIMO3
- */
-static int rs_switch_to_mimo3(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)) < 3)
- return -1;
-
- IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO3\n");
-
- tbl->lq_type = LQ_MIMO3;
- tbl->action = 0;
- tbl->max_search = IWL_MAX_11N_MIMO3_SEARCH;
- rate_mask = lq_sta->active_mimo3_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: MIMO3 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;
- }
- 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
*/
}
break;
- case IWL_LEGACY_SWITCH_MIMO2_AB:
- case IWL_LEGACY_SWITCH_MIMO2_AC:
- case IWL_LEGACY_SWITCH_MIMO2_BC:
+ 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;
- if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB)
- search_tbl->ant_type = ANT_AB;
- else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC)
- search_tbl->ant_type = ANT_AC;
- else
- search_tbl->ant_type = ANT_BC;
+ search_tbl->ant_type = ANT_AB;
if (!rs_is_valid_ant(valid_tx_ant,
search_tbl->ant_type))
goto out;
}
break;
-
- case IWL_LEGACY_SWITCH_MIMO3_ABC:
- IWL_DEBUG_RATE(mvm, "LQ: Legacy switch to MIMO3\n");
-
- /* Set up search table to try MIMO3 */
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
-
- search_tbl->ant_type = ANT_ABC;
-
- if (!rs_is_valid_ant(valid_tx_ant,
- search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
- search_tbl, index);
- if (!ret) {
- lq_sta->action_counter = 0;
- goto out;
- }
- break;
+ default:
+ WARN_ON_ONCE(1);
}
tbl->action++;
- if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
if (tbl->action == start_action)
out:
lq_sta->search_better_tbl = 1;
tbl->action++;
- if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+ if (tbl->action > IWL_LEGACY_SWITCH_MIMO2)
tbl->action = IWL_LEGACY_SWITCH_ANTENNA1;
if (update_search_tbl_counter)
search_tbl->action = tbl->action;
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_AB;
+ tbl->action = IWL_SISO_SWITCH_MIMO2;
break;
case IWL_BT_COEX_TRAFFIC_LOAD_HIGH:
case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS:
goto out;
}
break;
- case IWL_SISO_SWITCH_MIMO2_AB:
- case IWL_SISO_SWITCH_MIMO2_AC:
- case IWL_SISO_SWITCH_MIMO2_BC:
+ 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;
- if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB)
- search_tbl->ant_type = ANT_AB;
- else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC)
- search_tbl->ant_type = ANT_AC;
- else
- search_tbl->ant_type = ANT_BC;
+ search_tbl->ant_type = ANT_AB;
if (!rs_is_valid_ant(valid_tx_ant,
search_tbl->ant_type))
index, is_green);
update_search_tbl_counter = 1;
goto out;
- case IWL_SISO_SWITCH_MIMO3_ABC:
- IWL_DEBUG_RATE(mvm, "LQ: SISO switch to MIMO3\n");
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- search_tbl->ant_type = ANT_ABC;
-
- if (!rs_is_valid_ant(valid_tx_ant,
- search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
- search_tbl, index);
- if (!ret)
- goto out;
- break;
+ default:
+ WARN_ON_ONCE(1);
}
tbl->action++;
- if (tbl->action > IWL_LEGACY_SWITCH_MIMO3_ABC)
+ if (tbl->action > IWL_SISO_SWITCH_GI)
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
if (tbl->action == start_action)
out:
lq_sta->search_better_tbl = 1;
tbl->action++;
- if (tbl->action > IWL_SISO_SWITCH_MIMO3_ABC)
+ if (tbl->action > IWL_SISO_SWITCH_GI)
tbl->action = IWL_SISO_SWITCH_ANTENNA1;
if (update_search_tbl_counter)
search_tbl->action = tbl->action;
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_C)
+ if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
tbl->action = IWL_MIMO2_SWITCH_SISO_A;
break;
default:
break;
case IWL_MIMO2_SWITCH_SISO_A:
case IWL_MIMO2_SWITCH_SISO_B:
- case IWL_MIMO2_SWITCH_SISO_C:
IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n");
/* Set up new search table for SISO */
if (tbl->action == IWL_MIMO2_SWITCH_SISO_A)
search_tbl->ant_type = ANT_A;
- else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B)
+ else /* tbl->action == IWL_MIMO2_SWITCH_SISO_B */
search_tbl->ant_type = ANT_B;
- else
- search_tbl->ant_type = ANT_C;
if (!rs_is_valid_ant(valid_tx_ant,
search_tbl->ant_type))
index, is_green);
update_search_tbl_counter = 1;
goto out;
-
- case IWL_MIMO2_SWITCH_MIMO3_ABC:
- IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to MIMO3\n");
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- search_tbl->ant_type = ANT_ABC;
-
- if (!rs_is_valid_ant(valid_tx_ant,
- search_tbl->ant_type))
- break;
-
- ret = rs_switch_to_mimo3(mvm, lq_sta, sta,
- search_tbl, index);
- if (!ret)
- goto out;
-
- break;
+ default:
+ WARN_ON_ONCE(1);
}
tbl->action++;
- if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+ if (tbl->action > IWL_MIMO2_SWITCH_GI)
tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
if (tbl->action == start_action)
out:
lq_sta->search_better_tbl = 1;
tbl->action++;
- if (tbl->action > IWL_MIMO2_SWITCH_MIMO3_ABC)
+ if (tbl->action > IWL_MIMO2_SWITCH_GI)
tbl->action = IWL_MIMO2_SWITCH_ANTENNA1;
if (update_search_tbl_counter)
search_tbl->action = tbl->action;
return 0;
}
-/*
- * Try to switch to new modulation mode from MIMO3
- */
-static int rs_move_mimo3_to_other(struct iwl_mvm *mvm,
- struct iwl_lq_sta *lq_sta,
- struct ieee80211_sta *sta, int index)
-{
- 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);
- int ret;
- u8 update_search_tbl_counter = 0;
-
- 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_MIMO3_SWITCH_SISO_A)
- tbl->action = IWL_MIMO3_SWITCH_SISO_A;
- break;
- case IWL_BT_COEX_TRAFFIC_LOAD_LOW:
- /* avoid antenna B unless MIMO */
- if (tbl->action == IWL_MIMO3_SWITCH_SISO_B ||
- tbl->action == IWL_MIMO3_SWITCH_SISO_C)
- tbl->action = IWL_MIMO3_SWITCH_SISO_A;
- 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_MIMO3_SWITCH_ANTENNA1:
- case IWL_MIMO3_SWITCH_ANTENNA2:
- IWL_DEBUG_RATE(mvm, "LQ: MIMO3 toggle Antennas\n");
-
- if (tx_chains_num <= 3)
- break;
-
- if (window->success_ratio >= IWL_RS_GOOD_RATIO)
- break;
-
- memcpy(search_tbl, tbl, sz);
- if (rs_toggle_antenna(valid_tx_ant,
- &search_tbl->current_rate,
- search_tbl))
- goto out;
- break;
- case IWL_MIMO3_SWITCH_SISO_A:
- case IWL_MIMO3_SWITCH_SISO_B:
- case IWL_MIMO3_SWITCH_SISO_C:
- IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to SISO\n");
-
- /* Set up new search table for SISO */
- memcpy(search_tbl, tbl, sz);
-
- if (tbl->action == IWL_MIMO3_SWITCH_SISO_A)
- search_tbl->ant_type = ANT_A;
- else if (tbl->action == IWL_MIMO3_SWITCH_SISO_B)
- search_tbl->ant_type = ANT_B;
- else
- search_tbl->ant_type = ANT_C;
-
- 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;
-
- break;
-
- case IWL_MIMO3_SWITCH_MIMO2_AB:
- case IWL_MIMO3_SWITCH_MIMO2_AC:
- case IWL_MIMO3_SWITCH_MIMO2_BC:
- IWL_DEBUG_RATE(mvm, "LQ: MIMO3 switch to MIMO2\n");
-
- memcpy(search_tbl, tbl, sz);
- search_tbl->is_SGI = 0;
- if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AB)
- search_tbl->ant_type = ANT_AB;
- else if (tbl->action == IWL_MIMO3_SWITCH_MIMO2_AC)
- search_tbl->ant_type = ANT_AC;
- else
- search_tbl->ant_type = ANT_BC;
-
- 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;
-
- break;
-
- case IWL_MIMO3_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: MIMO3 toggle SGI/NGI\n");
-
- /* Set up new search table for MIMO */
- 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;
- }
- tbl->action++;
- if (tbl->action > IWL_MIMO3_SWITCH_GI)
- tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
-
- 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_MIMO3_SWITCH_GI)
- tbl->action = IWL_MIMO3_SWITCH_ANTENNA1;
- if (update_search_tbl_counter)
- search_tbl->action = tbl->action;
-
- return 0;
-}
-
/*
* Check whether we should continue using same modulation mode, or
* begin search for a new mode, based on:
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false);
}
+static u8 rs_get_tid(struct iwl_lq_sta *lq_data,
+ struct ieee80211_hdr *hdr)
+{
+ u8 tid = IWL_MAX_TID_COUNT;
+
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ u8 *qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & 0xf;
+ }
+
+ if (unlikely(tid > IWL_MAX_TID_COUNT))
+ tid = IWL_MAX_TID_COUNT;
+
+ return tid;
+}
+
/*
* Do rate scaling and search for new modulation mode.
*/
lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
- tid = rs_tl_add_packet(lq_sta, hdr);
+ tid = rs_get_tid(lq_sta, hdr);
if ((tid != IWL_MAX_TID_COUNT) &&
(lq_sta->tx_agg_tid_en & (1 << tid))) {
tid_data = &sta_priv->tid_data[tid];
scale_action = 0;
if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
- IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
- (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+ 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)) {
/*
BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD);
if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >=
- IWL_BT_COEX_TRAFFIC_LOAD_HIGH) &&
- (is_mimo2(tbl->lq_type) || is_mimo3(tbl->lq_type))) {
+ IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) {
/* search for a new modulation */
rs_stay_in_table(lq_sta, true);
goto lq_update;
else if (is_mimo2(tbl->lq_type))
rs_move_mimo2_to_other(mvm, lq_sta, sta, index);
else
- rs_move_mimo3_to_other(mvm, lq_sta, sta, index);
+ WARN_ON_ONCE(1);
/* If new "search" mode was selected, set up in uCode table */
if (lq_sta->search_better_tbl) {
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_MIMO3_6M_PLCP)
- rate_idx = rate_idx + (2 * MCS_INDEX_PER_STREAM);
- else if (rs_extract_rate(lq_sta->last_rate_n_flags) >=
- IWL_RATE_MIMO2_6M_PLCP)
+ 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)
lq_sta->flush_timer = 0;
lq_sta->supp_rates = sta->supp_rates[sband->band];
- 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]);
IWL_DEBUG_RATE(mvm,
"LQ: *** rate scale station global init for station %d ***\n",
lq_sta->active_mimo2_rate &= ~((u16)0x2);
lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE;
- lq_sta->active_mimo3_rate = ht_cap->mcs.rx_mask[2] << 1;
- lq_sta->active_mimo3_rate |= ht_cap->mcs.rx_mask[2] & 0x1;
- lq_sta->active_mimo3_rate &= ~((u16)0x2);
- lq_sta->active_mimo3_rate <<= IWL_FIRST_OFDM_RATE;
-
IWL_DEBUG_RATE(mvm,
- "SISO-RATE=%X MIMO2-RATE=%X MIMO3-RATE=%X\n",
+ "SISO-RATE=%X MIMO2-RATE=%X\n",
lq_sta->active_siso_rate,
- lq_sta->active_mimo2_rate,
- lq_sta->active_mimo3_rate);
+ lq_sta->active_mimo2_rate);
/* These values will be overridden later */
lq_sta->lq.single_stream_ant_msk =
struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
/* Override starting rate (index 0) if needed for debug purposes */
- rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+ rs_dbgfs_set_mcs(lq_sta, &new_rate);
/* Interpret new_rate (rate_n_flags) */
rs_get_tbl_info_from_mcs(new_rate, lq_sta->band,
}
/* Override next rate if needed for debug purposes */
- rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+ rs_dbgfs_set_mcs(lq_sta, &new_rate);
/* Fill next table entry */
lq_cmd->rs_table[index] =
use_ht_possible = 0;
/* Override next rate if needed for debug purposes */
- rs_dbgfs_set_mcs(lq_sta, &new_rate, index);
+ rs_dbgfs_set_mcs(lq_sta, &new_rate);
/* Fill next table entry */
lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
#ifdef CONFIG_MAC80211_DEBUGFS
static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
- u32 *rate_n_flags, int index)
+ u32 *rate_n_flags)
{
struct iwl_mvm *mvm;
u8 valid_tx_ant;
(is_legacy(tbl->lq_type)) ? "legacy" : "HT");
if (is_Ht(tbl->lq_type)) {
desc += sprintf(buff+desc, " %s",
- (is_siso(tbl->lq_type)) ? "SISO" :
- ((is_mimo2(tbl->lq_type)) ? "MIMO2" : "MIMO3"));
+ (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2");
desc += sprintf(buff+desc, " %s",
(tbl->is_ht40) ? "40MHz" : "20MHz");
desc += sprintf(buff+desc, " %s %s %s\n",
.llseek = default_llseek,
};
-static ssize_t rs_sta_dbgfs_rate_scale_data_read(struct file *file,
- char __user *user_buf, size_t count, loff_t *ppos)
-{
- struct iwl_lq_sta *lq_sta = file->private_data;
- struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl];
- char buff[120];
- int desc = 0;
-
- if (is_Ht(tbl->lq_type))
- desc += sprintf(buff+desc,
- "Bit Rate= %d Mb/s\n",
- tbl->expected_tpt[lq_sta->last_txrate_idx]);
- else
- desc += sprintf(buff+desc,
- "Bit Rate= %d Mb/s\n",
- iwl_rates[lq_sta->last_txrate_idx].ieee >> 1);
-
- return simple_read_from_buffer(user_buf, count, ppos, buff, desc);
-}
-
-static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = {
- .read = rs_sta_dbgfs_rate_scale_data_read,
- .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;
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_rate_scale_data_file =
- debugfs_create_file("rate_scale_data", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_rate_scale_data_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);
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_rate_scale_data_file);
debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
}
#endif
* station is added we ignore it.
*/
static void rs_rate_init_stub(void *mvm_r,
- struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *mvm_sta)
+ struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *mvm_sta)
{
}
static struct rate_control_ops rs_mvm_ops = {
* iwl_mvm_tx_protection - Gets LQ command, change it to enable/disable
* Tx protection, according to this rquest and previous requests,
* and send the LQ command.
- * @lq: The LQ command
* @mvmsta: The station
* @enable: Enable Tx protection?
*/
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
- struct iwl_mvm_sta *mvmsta, bool enable)
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+ bool enable)
{
+ struct iwl_lq_cmd *lq = &mvmsta->lq_sta.lq;
+
lockdep_assert_held(&mvm->mutex);
if (enable) {
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_mimo3; /* uCode API: IWL_RATE_MIMO3_6M_PLCP, etc. */
- u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */
- u8 prev_ieee; /* previous rate in IEEE speeds */
- u8 next_ieee; /* next rate in IEEE speeds */
u8 prev_rs; /* previous rate used in rs algo */
u8 next_rs; /* next rate used in rs algo */
- u8 prev_rs_tgg; /* previous rate used in TGG rs algo */
- u8 next_rs_tgg; /* next rate used in TGG rs algo */
};
#define IWL_RATE_60M_PLCP 3
IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP,
};
-/* MAC header values for bit rates */
-enum {
- IWL_RATE_6M_IEEE = 12,
- IWL_RATE_9M_IEEE = 18,
- IWL_RATE_12M_IEEE = 24,
- IWL_RATE_18M_IEEE = 36,
- IWL_RATE_24M_IEEE = 48,
- IWL_RATE_36M_IEEE = 72,
- IWL_RATE_48M_IEEE = 96,
- IWL_RATE_54M_IEEE = 108,
- IWL_RATE_60M_IEEE = 120,
- IWL_RATE_1M_IEEE = 2,
- IWL_RATE_2M_IEEE = 4,
- IWL_RATE_5M_IEEE = 11,
- IWL_RATE_11M_IEEE = 22,
-};
-
#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1)
#define IWL_INVALID_VALUE -1
#define IWL_LEGACY_SWITCH_ANTENNA1 0
#define IWL_LEGACY_SWITCH_ANTENNA2 1
#define IWL_LEGACY_SWITCH_SISO 2
-#define IWL_LEGACY_SWITCH_MIMO2_AB 3
-#define IWL_LEGACY_SWITCH_MIMO2_AC 4
-#define IWL_LEGACY_SWITCH_MIMO2_BC 5
-#define IWL_LEGACY_SWITCH_MIMO3_ABC 6
+#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_AB 2
-#define IWL_SISO_SWITCH_MIMO2_AC 3
-#define IWL_SISO_SWITCH_MIMO2_BC 4
-#define IWL_SISO_SWITCH_GI 5
-#define IWL_SISO_SWITCH_MIMO3_ABC 6
-
+#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_SISO_C 4
-#define IWL_MIMO2_SWITCH_GI 5
-#define IWL_MIMO2_SWITCH_MIMO3_ABC 6
-
+#define IWL_MIMO2_SWITCH_GI 4
-/* possible actions when in mimo3 mode */
-#define IWL_MIMO3_SWITCH_ANTENNA1 0
-#define IWL_MIMO3_SWITCH_ANTENNA2 1
-#define IWL_MIMO3_SWITCH_SISO_A 2
-#define IWL_MIMO3_SWITCH_SISO_B 3
-#define IWL_MIMO3_SWITCH_SISO_C 4
-#define IWL_MIMO3_SWITCH_MIMO2_AB 5
-#define IWL_MIMO3_SWITCH_MIMO2_AC 6
-#define IWL_MIMO3_SWITCH_MIMO2_BC 7
-#define IWL_MIMO3_SWITCH_GI 8
-
-
-#define IWL_MAX_11N_MIMO3_SEARCH IWL_MIMO3_SWITCH_GI
-#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_MIMO3_ABC
-
-/*FIXME:RS:add possible actions for MIMO3*/
+#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI
#define IWL_ACTION_LIMIT 3 /* # possible actions */
LQ_A,
LQ_SISO, /* high-throughput types */
LQ_MIMO2,
- LQ_MIMO3,
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_mimo3(tbl) ((tbl) == LQ_MIMO3)
-#define is_mimo(tbl) (is_mimo2(tbl) || is_mimo3(tbl))
+#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 iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
};
-struct iwl_traffic_load {
- unsigned long time_stamp; /* age of the oldest statistics */
- u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time
- * slice */
- u32 total; /* total num of packets during the
- * last TID_MAX_TIME_DIFF */
- u8 queue_count; /* number of queues that has
- * been used since the last cleanup */
- u8 head; /* start of the circular buffer */
-};
-
/**
* struct iwl_lq_sta -- driver's rate scaling private structure
*
u16 active_legacy_rate;
u16 active_siso_rate;
u16 active_mimo2_rate;
- u16 active_mimo3_rate;
s8 max_rate_idx; /* Max rate set by user */
u8 missed_rate_counter;
struct iwl_lq_cmd lq;
struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
- struct iwl_traffic_load load[IWL_MAX_TID_COUNT];
u8 tx_agg_tid_en;
#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_rate_scale_data_file;
struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
u32 dbg_fixed_rate;
#endif
struct iwl_mvm_sta;
-int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq,
- struct iwl_mvm_sta *mvmsta, bool enable);
+int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
+ bool enable);
#endif /* __rs__ */
ieee80211_rx_ni(mvm->hw, skb);
}
-/*
- * iwl_mvm_calc_rssi - calculate the rssi in dBm
- * @phy_info: the phy information for the coming packet
- */
-static int iwl_mvm_calc_rssi(struct iwl_mvm *mvm,
- struct iwl_rx_phy_info *phy_info)
+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;
- /* Find max rssi among 2 possible receivers.
- * These values are measured by the Digital Signal Processor (DSP).
- * They should stay fairly constant even as the signal strength varies,
- * if the radio's Automatic Gain Control (AGC) is working right.
- * AGC value (see below) will provide the "interesting" info.
- */
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;
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);
- return max_rssi_dbm;
+ 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;
+}
+
+/*
+ * iwl_mvm_get_signal_strength - use new rx PHY INFO API
+ * values are reported by the fw as positive values - need to negate
+ * to obtain their dBM. Account for missing antennas by replacing 0
+ * values by -256dBm: practically 0 power and a non-feasible 8 bit value.
+ */
+static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm,
+ struct iwl_rx_phy_info *phy_info,
+ struct ieee80211_rx_status *rx_status)
+{
+ int energy_a, energy_b, energy_c, max_energy;
+ u32 val;
+
+ val =
+ le32_to_cpu(phy_info->non_cfg_phy[IWL_RX_INFO_ENERGY_ANT_ABC_IDX]);
+ energy_a = (val & IWL_RX_INFO_ENERGY_ANT_A_MSK) >>
+ IWL_RX_INFO_ENERGY_ANT_A_POS;
+ energy_a = energy_a ? -energy_a : -256;
+ energy_b = (val & IWL_RX_INFO_ENERGY_ANT_B_MSK) >>
+ IWL_RX_INFO_ENERGY_ANT_B_POS;
+ energy_b = energy_b ? -energy_b : -256;
+ energy_c = (val & IWL_RX_INFO_ENERGY_ANT_C_MSK) >>
+ IWL_RX_INFO_ENERGY_ANT_C_POS;
+ energy_c = energy_c ? -energy_c : -256;
+ max_energy = max(energy_a, energy_b);
+ max_energy = max(max_energy, energy_c);
+
+ IWL_DEBUG_STATS(mvm, "energy In A %d B %d C %d , and max %d\n",
+ energy_a, energy_b, energy_c, max_energy);
+
+ rx_status->signal = max_energy;
+ 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] = energy_a;
+ rx_status->chain_signal[1] = energy_b;
+ rx_status->chain_signal[2] = energy_c;
}
/*
*/
/*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/
- /* Find max signal strength (dBm) among 3 antenna/receiver chains */
- rx_status.signal = iwl_mvm_calc_rssi(mvm, phy_info);
+ 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_DEBUG_STATS_LIMIT(mvm, "Rssi %d, TSF %llu\n", rx_status.signal,
(unsigned long long)rx_status.mactime);
- /*
- * "antenna number"
- *
- * It seems that the antenna field in the phy flags value
- * is actually a bit field. This is undefined by radiotap,
- * it wants an actual antenna number but I always get "7"
- * for most legacy frames I receive indicating that the
- * same frame was received on all three RX chains.
- *
- * I think this field should be removed in favor of a
- * new 802.11n radiotap field "RX chains" that is defined
- * as a bitmask.
- */
- rx_status.antenna = (le16_to_cpu(phy_info->phy_flags) &
- RX_RES_PHY_FLAGS_ANTENNA)
- >> RX_RES_PHY_FLAGS_ANTENNA_POS;
-
/* set the preamble flag if appropriate */
if (phy_info->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_SHORT_PREAMBLE))
rx_status.flag |= RX_FLAG_SHORTPRE;
return 0;
}
+static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
+ struct iwl_notif_statistics *stats)
+{
+ /*
+ * NOTE FW aggregates the statistics - BUT the statistics are cleared
+ * when the driver issues REPLY_STATISTICS_CMD 0x9c with CLEAR_STATS
+ * bit set.
+ */
+ lockdep_assert_held(&mvm->mutex);
+ memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx));
+}
+
+struct iwl_mvm_stat_data {
+ struct iwl_notif_statistics *stats;
+ struct iwl_mvm *mvm;
+};
+
+static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_stat_data *data = _data;
+ struct iwl_notif_statistics *stats = data->stats;
+ struct iwl_mvm *mvm = data->mvm;
+ int sig = -stats->general.beacon_filter_average_energy;
+ int last_event;
+ int thold = vif->bss_conf.cqm_rssi_thold;
+ int hyst = vif->bss_conf.cqm_rssi_hyst;
+ u16 id = le32_to_cpu(stats->rx.general.mac_id);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (mvmvif->id != id)
+ return;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ mvmvif->bf_data.ave_beacon_signal = sig;
+
+ if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI))
+ return;
+
+ /* CQM Notification */
+ last_event = mvmvif->bf_data.last_cqm_event;
+ if (thold && sig < thold && (last_event == 0 ||
+ sig < last_event - hyst)) {
+ mvmvif->bf_data.last_cqm_event = sig;
+ IWL_DEBUG_RX(mvm, "cqm_iterator cqm low %d\n",
+ sig);
+ ieee80211_cqm_rssi_notify(
+ vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ GFP_KERNEL);
+ } else if (sig > thold &&
+ (last_event == 0 || sig > last_event + hyst)) {
+ mvmvif->bf_data.last_cqm_event = sig;
+ IWL_DEBUG_RX(mvm, "cqm_iterator cqm high %d\n",
+ sig);
+ ieee80211_cqm_rssi_notify(
+ vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ GFP_KERNEL);
+ }
+}
+
/*
* iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler
*
* TODO: This handler is implemented partially.
- * It only gets the NIC's temperature.
*/
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_notif_statistics *stats = (void *)&pkt->data;
struct mvm_statistics_general_common *common = &stats->general.common;
+ struct iwl_mvm_stat_data data = {
+ .stats = stats,
+ .mvm = mvm,
+ };
if (mvm->temperature != le32_to_cpu(common->temperature)) {
mvm->temperature = le32_to_cpu(common->temperature);
iwl_mvm_tt_handler(mvm);
}
+ iwl_mvm_update_rx_statistics(mvm, stats);
+ ieee80211_iterate_active_interfaces(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_stat_iterator,
+ &data);
return 0;
}
*/
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;
} else {
cmd->passive2active = 0;
+ cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
}
iwl_mvm_scan_fill_ssids(cmd, req);
* method for HT traffic
* this function also sends the LQ command
*/
- return iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
- mvmsta, true);
+ 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)
#include "iwl-prph.h"
/* A TimeUnit is 1024 microsecond */
-#define TU_TO_JIFFIES(_tu) (usecs_to_jiffies((_tu) * 1024))
#define MSEC_TO_TU(_msec) (_msec*1000/1024)
/*
}
}
- if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) {
+ if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_END) {
IWL_DEBUG_TE(mvm,
"TE ended - current time %lu, estimated end %lu\n",
jiffies, te_data->end_jiffies);
iwl_mvm_te_check_disconnect(mvm, te_data->vif,
"No assocation and the time event is over already...");
iwl_mvm_te_clear_data(mvm, te_data);
- } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
+ } else if (le32_to_cpu(notif->action) & TE_V2_NOTIF_HOST_EVENT_START) {
te_data->running = true;
- te_data->end_jiffies = jiffies +
- TU_TO_JIFFIES(te_data->duration);
+ te_data->end_jiffies = TU_TO_EXP_TIME(te_data->duration);
if (te_data->vif->type == NL80211_IFTYPE_P2P_DEVICE) {
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
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 *te_cmd)
+ struct iwl_time_event_cmd_v2 *te_cmd)
{
static const u8 time_event_response[] = { TIME_EVENT_CMD };
struct iwl_notification_wait wait_time_event;
ARRAY_SIZE(time_event_response),
iwl_mvm_time_event_response, te_data);
- ret = iwl_mvm_send_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
- sizeof(*te_cmd), te_cmd);
+ ret = iwl_mvm_send_time_event_cmd(mvm, 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);
{
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 time_cmd = {};
+ struct iwl_time_event_cmd_v2 time_cmd = {};
lockdep_assert_held(&mvm->mutex);
if (te_data->running &&
- time_after(te_data->end_jiffies,
- jiffies + TU_TO_JIFFIES(min_duration))) {
+ time_after(te_data->end_jiffies, TU_TO_EXP_TIME(min_duration))) {
IWL_DEBUG_TE(mvm, "We have enough time in the current TE: %u\n",
jiffies_to_msecs(te_data->end_jiffies - jiffies));
return;
time_cmd.apply_time =
cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG));
- time_cmd.dep_policy = TE_INDEPENDENT;
- time_cmd.is_present = cpu_to_le32(1);
- time_cmd.max_frags = cpu_to_le32(TE_FRAG_NONE);
+ time_cmd.max_frags = TE_V2_FRAG_NONE;
time_cmd.max_delay = cpu_to_le32(500);
/* TODO: why do we need to interval = bi if it is not periodic? */
time_cmd.interval = cpu_to_le32(1);
- time_cmd.interval_reciprocal = cpu_to_le32(iwl_mvm_reciprocal(1));
time_cmd.duration = cpu_to_le32(duration);
- time_cmd.repeat = cpu_to_le32(1);
- time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
- TE_NOTIF_HOST_EVENT_END);
+ time_cmd.repeat = 1;
+ time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+ TE_V2_NOTIF_HOST_EVENT_END);
iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
struct iwl_mvm_vif *mvmvif,
struct iwl_mvm_time_event_data *te_data)
{
- struct iwl_time_event_cmd time_cmd = {};
+ struct iwl_time_event_cmd_v2 time_cmd = {};
u32 id, uid;
int ret;
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_cmd_pdu(mvm, TIME_EVENT_CMD, CMD_SYNC,
- sizeof(time_cmd), &time_cmd);
+ ret = iwl_mvm_send_time_event_cmd(mvm, &time_cmd);
if (WARN_ON(ret))
return;
}
{
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 time_cmd = {};
+ struct iwl_time_event_cmd_v2 time_cmd = {};
lockdep_assert_held(&mvm->mutex);
if (te_data->running) {
}
time_cmd.apply_time = cpu_to_le32(0);
- time_cmd.dep_policy = cpu_to_le32(TE_INDEPENDENT);
- time_cmd.is_present = cpu_to_le32(1);
time_cmd.interval = cpu_to_le32(1);
/*
* scheduled. To improve the chances of it being scheduled, allow them
* to be fragmented, and in addition allow them to be delayed.
*/
- time_cmd.max_frags = cpu_to_le32(MSEC_TO_TU(duration)/20);
+ time_cmd.max_frags = min(MSEC_TO_TU(duration)/50, TE_V2_FRAG_ENDLESS);
time_cmd.max_delay = cpu_to_le32(MSEC_TO_TU(duration/2));
time_cmd.duration = cpu_to_le32(MSEC_TO_TU(duration));
- time_cmd.repeat = cpu_to_le32(1);
- time_cmd.notify = cpu_to_le32(TE_NOTIF_HOST_EVENT_START |
- TE_NOTIF_HOST_EVENT_END);
+ time_cmd.repeat = 1;
+ time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+ TE_V2_NOTIF_HOST_EVENT_END);
return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
}
mvmsta = (void *)sta->drv_priv;
if (enable == mvmsta->tt_tx_protection)
continue;
- err = iwl_mvm_tx_protection(mvm, &mvmsta->lq_sta.lq,
- mvmsta, enable);
+ err = iwl_mvm_tx_protection(mvm, mvmsta, enable);
if (err) {
IWL_ERR(mvm, "Failed to %s Tx protection\n",
enable ? "enable" : "disable");
.support_tx_backoff = true,
};
+static const struct iwl_tt_params iwl7000_high_temp_tt_params = {
+ .ct_kill_entry = 118,
+ .ct_kill_exit = 96,
+ .ct_kill_duration = 5,
+ .dynamic_smps_entry = 114,
+ .dynamic_smps_exit = 110,
+ .tx_protection_entry = 114,
+ .tx_protection_exit = 108,
+ .tx_backoff = {
+ {.temperature = 112, .backoff = 300},
+ {.temperature = 113, .backoff = 800},
+ {.temperature = 114, .backoff = 1500},
+ {.temperature = 115, .backoff = 3000},
+ {.temperature = 116, .backoff = 5000},
+ {.temperature = 117, .backoff = 10000},
+ },
+ .support_ct_kill = true,
+ .support_dynamic_smps = true,
+ .support_tx_protection = true,
+ .support_tx_backoff = true,
+};
+
void iwl_mvm_tt_initialize(struct iwl_mvm *mvm)
{
struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle;
IWL_DEBUG_TEMP(mvm, "Initialize Thermal Throttling\n");
- tt->params = &iwl7000_tt_params;
+
+ if (mvm->cfg->high_temp)
+ tt->params = &iwl7000_high_temp_tt_params;
+ else
+ tt->params = &iwl7000_tt_params;
+
tt->throttle = false;
INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill);
}
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 &&
- (skb->protocol == cpu_to_be16(ETH_P_PAE) ||
- is_multicast_ether_addr(hdr->addr1) ||
- ieee80211_is_back_req(fc) ||
- ieee80211_is_mgmt(fc)))
+ 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))
* it
*/
WARN_ON_ONCE(info->flags & IEEE80211_TX_CTL_AMPDU);
+ } else if (skb->protocol == cpu_to_be16(ETH_P_PAE)) {
+ tx_cmd->pm_frame_timeout = cpu_to_le16(2);
} else {
tx_cmd->pm_frame_timeout = 0;
}
}
/*
- * for data packets, rate info comes from the table inside he fw. This
+ * for data packets, rate info comes from the table inside the fw. This
* table is controlled by LINK_QUALITY commands
*/
IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
}
+void iwl_mvm_dump_sram(struct iwl_mvm *mvm)
+{
+ const struct fw_img *img;
+ int ofs, len = 0;
+ u8 *buf;
+
+ if (!mvm->ucode_loaded)
+ return;
+
+ img = &mvm->fw->img[mvm->cur_ucode];
+ ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ iwl_trans_read_mem_bytes(mvm->trans, ofs, buf, len);
+ iwl_print_hex_error(mvm->trans, buf, len);
+
+ kfree(buf);
+}
+
/**
* iwl_mvm_send_lq_cmd() - Send link quality command
* @init: This command is sent as part of station initialization right
{IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)},
{IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)},
{IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg)},
- {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)},
+ {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)},
{IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)},
{IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)},
{IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)},
int ret;
iwl_trans = iwl_trans_pcie_alloc(pdev, ent, cfg);
- if (iwl_trans == NULL)
- return -ENOMEM;
+ if (IS_ERR(iwl_trans))
+ return PTR_ERR(iwl_trans);
pci_set_drvdata(pdev, iwl_trans);
trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);
trans_pcie->drv = iwl_drv_start(iwl_trans, cfg);
- if (IS_ERR_OR_NULL(trans_pcie->drv)) {
+ if (IS_ERR(trans_pcie->drv)) {
ret = PTR_ERR(trans_pcie->drv);
goto out_free_trans;
}
static int iwl_pci_suspend(struct device *device)
{
- struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_trans *iwl_trans = pci_get_drvdata(pdev);
-
/* Before you put code here, think about WoWLAN. You cannot check here
* whether WoWLAN is enabled or not, and your code will run even if
* WoWLAN is enabled - don't kill the NIC, someone may need it in Sx.
*/
- return iwl_trans_suspend(iwl_trans);
+ return 0;
}
static int iwl_pci_resume(struct device *device)
{
struct pci_dev *pdev = to_pci_dev(device);
- struct iwl_trans *iwl_trans = pci_get_drvdata(pdev);
+ struct iwl_trans *trans = pci_get_drvdata(pdev);
+ bool hw_rfkill;
/* Before you put code here, think about WoWLAN. You cannot check here
* whether WoWLAN is enabled or not, and your code will run even if
*/
pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00);
- return iwl_trans_resume(iwl_trans);
+ if (!trans->op_mode)
+ return 0;
+
+ iwl_enable_rfkill_int(trans);
+
+ hw_rfkill = iwl_is_rfkill_set(trans);
+ iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
+
+ return 0;
}
static SIMPLE_DEV_PM_OPS(iwl_dev_pm_ops, iwl_pci_suspend, iwl_pci_resume);
/*****************************************************
* Error handling
******************************************************/
-int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf);
void iwl_pcie_dump_csr(struct iwl_trans *trans);
/*****************************************************
*/
static int iwl_rxq_space(const struct iwl_rxq *rxq)
{
- int s = rxq->read - rxq->write;
-
- if (s <= 0)
- s += RX_QUEUE_SIZE;
- /* keep some buffer to not confuse full and empty queue */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
+ /* Make sure RX_QUEUE_SIZE is a power of 2 */
+ BUILD_BUG_ON(RX_QUEUE_SIZE & (RX_QUEUE_SIZE - 1));
+
+ /*
+ * There can be up to (RX_QUEUE_SIZE - 1) free slots, to avoid ambiguity
+ * between empty and completely full queues.
+ * The following is equivalent to modulo by RX_QUEUE_SIZE and is well
+ * defined for negative dividends.
+ */
+ return (rxq->read - rxq->write - 1) & (RX_QUEUE_SIZE - 1);
}
/*
}
iwl_pcie_dump_csr(trans);
- iwl_pcie_dump_fh(trans, NULL);
+ iwl_dump_fh(trans, NULL);
set_bit(STATUS_FW_ERROR, &trans_pcie->status);
clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
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);
/* the thread will service interrupts and re-enable them */
if (likely(inta))
return IRQ_WAKE_THREAD;
- else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
- !trans_pcie->inta)
- iwl_enable_interrupts(trans);
- return IRQ_HANDLED;
+
+ ret = IRQ_HANDLED;
none:
/* re-enable interrupts here since we don't have anything to service. */
!trans_pcie->inta)
iwl_enable_interrupts(trans);
- return IRQ_NONE;
+ return ret;
}
/* interrupt handler using ict table, with this interrupt driver will
u32 val = 0;
u32 read;
unsigned long flags;
+ irqreturn_t ret = IRQ_NONE;
if (!trans)
return IRQ_NONE;
* use legacy interrupt.
*/
if (unlikely(!trans_pcie->use_ict)) {
- irqreturn_t ret = iwl_pcie_isr(irq, data);
+ ret = iwl_pcie_isr(irq, data);
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
return ret;
}
if (likely(inta)) {
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
return IRQ_WAKE_THREAD;
- } else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
- !trans_pcie->inta) {
- /* Allow interrupt if was disabled by this handler and
- * no tasklet was schedules, We should not enable interrupt,
- * tasklet will enable it.
- */
- iwl_enable_interrupts(trans);
}
- spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
none:
/* re-enable interrupts here since we don't have anything to service.
iwl_enable_interrupts(trans);
spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
- return IRQ_NONE;
+ return ret;
}
clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
}
-#ifdef CONFIG_PM_SLEEP
-static int iwl_trans_pcie_suspend(struct iwl_trans *trans)
-{
- return 0;
-}
-
-static int iwl_trans_pcie_resume(struct iwl_trans *trans)
-{
- bool hw_rfkill;
-
- iwl_enable_rfkill_int(trans);
-
- hw_rfkill = iwl_is_rfkill_set(trans);
- iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
-
- return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
unsigned long *flags)
{
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
}
-static const char *get_fh_string(int cmd)
-{
-#define IWL_CMD(x) case x: return #x
- switch (cmd) {
- IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG);
- IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG);
- IWL_CMD(FH_RSCSR_CHNL0_WPTR);
- IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG);
- IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG);
- IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG);
- IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV);
- IWL_CMD(FH_TSSR_TX_STATUS_REG);
- IWL_CMD(FH_TSSR_TX_ERROR_REG);
- default:
- return "UNKNOWN";
- }
-#undef IWL_CMD
-}
-
-int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf)
-{
- int i;
- static const u32 fh_tbl[] = {
- FH_RSCSR_CHNL0_STTS_WPTR_REG,
- FH_RSCSR_CHNL0_RBDCB_BASE_REG,
- FH_RSCSR_CHNL0_WPTR,
- FH_MEM_RCSR_CHNL0_CONFIG_REG,
- FH_MEM_RSSR_SHARED_CTRL_REG,
- FH_MEM_RSSR_RX_STATUS_REG,
- FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV,
- FH_TSSR_TX_STATUS_REG,
- FH_TSSR_TX_ERROR_REG
- };
-
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- if (buf) {
- int pos = 0;
- size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40;
-
- *buf = kmalloc(bufsz, GFP_KERNEL);
- if (!*buf)
- return -ENOMEM;
-
- pos += scnprintf(*buf + pos, bufsz - pos,
- "FH register values:\n");
-
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
- pos += scnprintf(*buf + pos, bufsz - pos,
- " %34s: 0X%08x\n",
- get_fh_string(fh_tbl[i]),
- iwl_read_direct32(trans, fh_tbl[i]));
-
- return pos;
- }
-#endif
-
- IWL_ERR(trans, "FH register values:\n");
- for (i = 0; i < ARRAY_SIZE(fh_tbl); i++)
- IWL_ERR(trans, " %34s: 0X%08x\n",
- get_fh_string(fh_tbl[i]),
- iwl_read_direct32(trans, fh_tbl[i]));
-
- return 0;
-}
-
static const char *get_csr_string(int cmd)
{
#define IWL_CMD(x) case x: return #x
} while (0)
/* file operation */
-#define DEBUGFS_READ_FUNC(name) \
-static ssize_t iwl_dbgfs_##name##_read(struct file *file, \
- char __user *user_buf, \
- size_t count, loff_t *ppos);
-
-#define DEBUGFS_WRITE_FUNC(name) \
-static ssize_t iwl_dbgfs_##name##_write(struct file *file, \
- const char __user *user_buf, \
- size_t count, loff_t *ppos);
-
#define DEBUGFS_READ_FILE_OPS(name) \
- DEBUGFS_READ_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.read = iwl_dbgfs_##name##_read, \
.open = simple_open, \
};
#define DEBUGFS_WRITE_FILE_OPS(name) \
- DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.open = simple_open, \
};
#define DEBUGFS_READ_WRITE_FILE_OPS(name) \
- DEBUGFS_READ_FUNC(name); \
- DEBUGFS_WRITE_FUNC(name); \
static const struct file_operations iwl_dbgfs_##name##_ops = { \
.write = iwl_dbgfs_##name##_write, \
.read = iwl_dbgfs_##name##_read, \
int pos = 0;
ssize_t ret = -EFAULT;
- ret = pos = iwl_pcie_dump_fh(trans, &buf);
+ ret = pos = iwl_dump_fh(trans, &buf);
if (buf) {
ret = simple_read_from_buffer(user_buf,
count, ppos, buf, pos);
.wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty,
-#ifdef CONFIG_PM_SLEEP
- .suspend = iwl_trans_pcie_suspend,
- .resume = iwl_trans_pcie_resume,
-#endif
.write8 = iwl_trans_pcie_write8,
.write32 = iwl_trans_pcie_write32,
.read32 = iwl_trans_pcie_read32,
trans = kzalloc(sizeof(struct iwl_trans) +
sizeof(struct iwl_trans_pcie), GFP_KERNEL);
-
- if (!trans)
- return NULL;
+ if (!trans) {
+ err = -ENOMEM;
+ goto out;
+ }
trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
spin_lock_init(&trans_pcie->reg_lock);
init_waitqueue_head(&trans_pcie->ucode_write_waitq);
- if (pci_enable_device(pdev)) {
- err = -ENODEV;
- goto out_no_pci;
+ if (!cfg->base_params->pcie_l1_allowed) {
+ /*
+ * W/A - seems to solve weird behavior. We need to remove this
+ * if we don't want to stay in L1 all the time. This wastes a
+ * lot of power.
+ */
+ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S |
+ PCIE_LINK_STATE_L1 |
+ PCIE_LINK_STATE_CLKPM);
}
- /* W/A - seems to solve weird behavior. We need to remove this if we
- * don't want to stay in L1 all the time. This wastes a lot of power */
- pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
- PCIE_LINK_STATE_CLKPM);
+ err = pci_enable_device(pdev);
+ if (err)
+ goto out_no_pci;
pci_set_master(pdev);
SLAB_HWCACHE_ALIGN,
NULL);
- if (!trans->dev_cmd_pool)
+ if (!trans->dev_cmd_pool) {
+ err = -ENOMEM;
goto out_pci_disable_msi;
+ }
trans_pcie->inta_mask = CSR_INI_SET_MASK;
if (iwl_pcie_alloc_ict(trans))
goto out_free_cmd_pool;
- if (request_threaded_irq(pdev->irq, iwl_pcie_isr_ict,
- iwl_pcie_irq_handler,
- IRQF_SHARED, DRV_NAME, trans)) {
+ err = request_threaded_irq(pdev->irq, iwl_pcie_isr_ict,
+ iwl_pcie_irq_handler,
+ IRQF_SHARED, DRV_NAME, trans);
+ if (err) {
IWL_ERR(trans, "Error allocating IRQ %d\n", pdev->irq);
goto out_free_ict;
}
pci_disable_device(pdev);
out_no_pci:
kfree(trans);
- return NULL;
+out:
+ return ERR_PTR(err);
}
***************************************************/
static int iwl_queue_space(const struct iwl_queue *q)
{
- int s = q->read_ptr - q->write_ptr;
-
- if (q->read_ptr > q->write_ptr)
- s -= q->n_bd;
-
- if (s <= 0)
- s += q->n_window;
- /* keep some reserve to not confuse empty and full situations */
- s -= 2;
- if (s < 0)
- s = 0;
- return s;
+ unsigned int max;
+ unsigned int used;
+
+ /*
+ * 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.
+ */
+ if (q->n_window < q->n_bd)
+ max = q->n_window;
+ else
+ max = q->n_bd - 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.
+ */
+ used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1);
+
+ if (WARN_ON(used > max))
+ return 0;
+
+ return max - used;
}
/*
return -EINVAL;
}
- if (WARN_ON(addr & ~DMA_BIT_MASK(36)))
+ if (WARN(addr & ~IWL_TX_DMA_MASK,
+ "Unaligned address = %llx\n", (unsigned long long)addr))
return -EINVAL;
- if (unlikely(addr & ~IWL_TX_DMA_MASK))
- IWL_ERR(trans, "Unaligned address = %llx\n",
- (unsigned long long)addr);
-
iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len);
return 0;
sizeof(struct iwl_txq), GFP_KERNEL);
if (!trans_pcie->txq) {
IWL_ERR(trans, "Not enough memory for txq\n");
- ret = ENOMEM;
+ ret = -ENOMEM;
goto error;
}
/*
* iwl_pcie_enqueue_hcmd - enqueue a uCode command
* @priv: device private data point
- * @cmd: a point to the ucode command structure
+ * @cmd: a pointer to the ucode command structure
*
- * The function returns < 0 values to indicate the operation is
- * failed. On success, it turns the index (> 0) of command in the
+ * The function returns < 0 values to indicate the operation
+ * failed. On success, it returns the index (>= 0) of command in the
* command queue.
*/
static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
txq = &trans_pcie->txq[txq_id];
q = &txq->q;
- if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) {
- WARN_ON_ONCE(1);
+ if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used),
+ "TX on unused queue %d\n", txq_id))
return -EINVAL;
- }
spin_lock(&txq->lock);
* Check here that the packets are in the right place on the ring.
*/
wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
- WARN_ONCE(trans_pcie->txq[txq_id].ampdu &&
+ WARN_ONCE(txq->ampdu &&
(wifi_seq & 0xff) != q->write_ptr,
"Q: %d WiFi Seq %d tfdNum %d",
txq_id, wifi_seq, q->write_ptr);
*/
len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) +
hdr_len - IWL_HCMD_SCRATCHBUF_SIZE;
- tb1_len = (len + 3) & ~3;
+ tb1_len = ALIGN(len, 4);
/* Tell NIC about any 2-byte padding after MAC header */
if (tb1_len != len)
if (WARN_ON(skb->len < 10)) {
/* Should not happen; just a sanity check for addr1 use */
- dev_kfree_skb(skb);
+ ieee80211_free_txskb(hw, skb);
return;
}
}
if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) {
- dev_kfree_skb(skb);
+ ieee80211_free_txskb(hw, skb);
return;
}
if (data->idle && !data->tmp_chan) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
- dev_kfree_skb(skb);
+ ieee80211_free_txskb(hw, skb);
return;
}
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)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
hw->flags |= IEEE80211_HW_SUPPORTS_RC_TABLE;
hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
- WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
+ 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);
struct mwifiex_ie_types_extcap *ext_cap;
int ret_len = 0;
struct ieee80211_supported_band *sband;
+ struct ieee_types_header *hdr;
u8 radio_type;
if (!buffer || !*buffer)
}
if (bss_desc->bcn_ext_cap) {
+ hdr = (void *)bss_desc->bcn_ext_cap;
ext_cap = (struct mwifiex_ie_types_extcap *) *buffer;
memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap));
ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY);
- ext_cap->header.len = cpu_to_le16(sizeof(ext_cap->ext_cap));
+ ext_cap->header.len = cpu_to_le16(hdr->len);
- memcpy((u8 *)ext_cap + sizeof(struct mwifiex_ie_types_header),
+ memcpy((u8 *)ext_cap->ext_capab,
bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header),
le16_to_cpu(ext_cap->header.len));
- *buffer += sizeof(struct mwifiex_ie_types_extcap);
- ret_len += sizeof(struct mwifiex_ie_types_extcap);
+ if (hdr->len > 3 &&
+ ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED)
+ priv->hs2_enabled = true;
+ else
+ priv->hs2_enabled = false;
+
+ *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
+ ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len;
}
return ret_len;
memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset);
/* Copy SNAP header */
- snap.snap_type = *(u16 *) ((u8 *)skb_src->data + dt_offset);
+ snap.snap_type = le16_to_cpu(*(__le16 *) ((u8 *)skb_src->data + dt_offset));
dt_offset += sizeof(u16);
memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr));
skb_src = skb_dequeue(&pra_list->skb_head);
- pra_list->total_pkts_size -= skb_src->len;
+ pra_list->total_pkt_count--;
atomic_dec(&priv->wmm.tx_pkts_queued);
skb_queue_tail(&pra_list->skb_head, skb_aggr);
- pra_list->total_pkts_size += skb_aggr->len;
+ pra_list->total_pkt_count++;
atomic_inc(&priv->wmm.tx_pkts_queued);
static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = {
{
- .max = 2, .types = BIT(NL80211_IFTYPE_STATION),
+ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT),
},
{
.max = 1, .types = BIT(NL80211_IFTYPE_AP),
struct sk_buff *skb;
u16 pkt_len;
const struct ieee80211_mgmt *mgmt;
+ struct mwifiex_txinfo *tx_info;
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
if (!buf || !len) {
return -ENOMEM;
}
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ tx_info->bss_num = priv->bss_num;
+ tx_info->bss_type = priv->bss_type;
+
mwifiex_form_mgmt_frame(skb, buf, len);
mwifiex_queue_tx_pkt(priv, skb);
u16 frame_type, bool reg)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev);
+ u32 mask;
if (reg)
- priv->mgmt_frame_mask |= BIT(frame_type >> 4);
+ mask = priv->mgmt_frame_mask | BIT(frame_type >> 4);
else
- priv->mgmt_frame_mask &= ~BIT(frame_type >> 4);
-
- mwifiex_send_cmd_async(priv, HostCmd_CMD_MGMT_FRAME_REG,
- HostCmd_ACT_GEN_SET, 0, &priv->mgmt_frame_mask);
+ mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4);
- wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+ 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);
+ wiphy_dbg(wiphy, "info: mgmt frame registered\n");
+ }
}
/*
" reason code %d\n", priv->cfg_bssid, reason_code);
memset(priv->cfg_bssid, 0, ETH_ALEN);
+ priv->hs2_enabled = false;
return 0;
}
}
EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf);
-#ifdef CONFIG_PM
static bool
-mwifiex_is_pattern_supported(struct cfg80211_wowlan_trig_pkt_pattern *pat,
- s8 *byte_seq)
+mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
+ u8 max_byte_seq)
{
int j, k, valid_byte_cnt = 0;
bool dont_care_byte = false;
dont_care_byte = true;
}
- if (valid_byte_cnt > MAX_BYTESEQ)
+ if (valid_byte_cnt > max_byte_seq)
return false;
}
}
- byte_seq[MAX_BYTESEQ] = valid_byte_cnt;
+ byte_seq[max_byte_seq] = valid_byte_cnt;
return true;
}
+#ifdef CONFIG_PM
static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
struct cfg80211_wowlan *wowlan)
{
struct mwifiex_mef_entry *mef_entry;
int i, filt_num = 0, ret;
bool first_pat = true;
- u8 byte_seq[MAX_BYTESEQ + 1];
+ u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
const u8 ipv4_mc_mac[] = {0x33, 0x33};
const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
struct mwifiex_private *priv =
for (i = 0; i < wowlan->n_patterns; i++) {
memset(byte_seq, 0, sizeof(byte_seq));
if (!mwifiex_is_pattern_supported(&wowlan->patterns[i],
- byte_seq)) {
+ byte_seq,
+ MWIFIEX_MEF_MAX_BYTESEQ)) {
wiphy_err(wiphy, "Pattern not supported\n");
kfree(mef_entry);
return -EOPNOTSUPP;
if (!wowlan->patterns[i].pkt_offset) {
if (!(byte_seq[0] & 0x01) &&
- (byte_seq[MAX_BYTESEQ] == 1)) {
+ (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
continue;
} else if (is_broadcast_ether_addr(byte_seq)) {
mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
continue;
} else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
- (byte_seq[MAX_BYTESEQ] == 2)) ||
+ (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
(!memcmp(byte_seq, ipv6_mc_mac, 3) &&
- (byte_seq[MAX_BYTESEQ] == 3))) {
+ (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST;
continue;
}
mef_entry->filter[filt_num].repeat = 16;
memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
ETH_ALEN);
- mef_entry->filter[filt_num].byte_seq[MAX_BYTESEQ] = 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].filt_type = TYPE_EQ;
if (filt_num)
}
#endif
+static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq)
+{
+ const u8 ipv4_mc_mac[] = {0x33, 0x33};
+ const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
+ const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff};
+
+ if ((byte_seq[0] & 0x01) &&
+ (byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 1))
+ return PACKET_TYPE_UNICAST;
+ else if (!memcmp(byte_seq, bc_mac, 4))
+ return PACKET_TYPE_BROADCAST;
+ else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
+ byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) ||
+ (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
+ byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3))
+ return PACKET_TYPE_MULTICAST;
+
+ return 0;
+}
+
+static int
+mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv,
+ struct cfg80211_coalesce_rules *crule,
+ struct mwifiex_coalesce_rule *mrule)
+{
+ u8 byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ + 1];
+ struct filt_field_param *param;
+ int i;
+
+ mrule->max_coalescing_delay = crule->delay;
+
+ param = mrule->params;
+
+ for (i = 0; i < crule->n_patterns; i++) {
+ memset(byte_seq, 0, sizeof(byte_seq));
+ if (!mwifiex_is_pattern_supported(&crule->patterns[i],
+ byte_seq,
+ MWIFIEX_COALESCE_MAX_BYTESEQ)) {
+ dev_err(priv->adapter->dev, "Pattern not supported\n");
+ return -EOPNOTSUPP;
+ }
+
+ if (!crule->patterns[i].pkt_offset) {
+ u8 pkt_type;
+
+ pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq);
+ if (pkt_type && mrule->pkt_type) {
+ dev_err(priv->adapter->dev,
+ "Multiple packet types not allowed\n");
+ return -EOPNOTSUPP;
+ } else if (pkt_type) {
+ mrule->pkt_type = pkt_type;
+ continue;
+ }
+ }
+
+ if (crule->condition == NL80211_COALESCE_CONDITION_MATCH)
+ param->operation = RECV_FILTER_MATCH_TYPE_EQ;
+ else
+ param->operation = RECV_FILTER_MATCH_TYPE_NE;
+
+ param->operand_len = byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ];
+ memcpy(param->operand_byte_stream, byte_seq,
+ param->operand_len);
+ param->offset = crule->patterns[i].pkt_offset;
+ param++;
+
+ mrule->num_of_fields++;
+ }
+
+ if (!mrule->pkt_type) {
+ dev_err(priv->adapter->dev,
+ "Packet type can not be determined\n");
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy,
+ struct cfg80211_coalesce *coalesce)
+{
+ struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+ int i, ret;
+ struct mwifiex_ds_coalesce_cfg coalesce_cfg;
+ struct mwifiex_private *priv =
+ mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
+
+ memset(&coalesce_cfg, 0, sizeof(coalesce_cfg));
+ 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);
+ }
+
+ coalesce_cfg.num_of_rules = coalesce->n_rules;
+ for (i = 0; i < coalesce->n_rules; i++) {
+ ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i],
+ &coalesce_cfg.rule[i]);
+ if (ret) {
+ dev_err(priv->adapter->dev,
+ "Recheck the patterns provided for rule %d\n",
+ i + 1);
+ return ret;
+ }
+ }
+
+ return mwifiex_send_cmd_sync(priv, HostCmd_CMD_COALESCE_CFG,
+ HostCmd_ACT_GEN_SET, 0, &coalesce_cfg);
+}
+
/* station cfg80211 operations */
static struct cfg80211_ops mwifiex_cfg80211_ops = {
.add_virtual_intf = mwifiex_add_virtual_intf,
.resume = mwifiex_cfg80211_resume,
.set_wakeup = mwifiex_cfg80211_set_wakeup,
#endif
+ .set_coalesce = mwifiex_cfg80211_set_coalesce,
};
#ifdef CONFIG_PM
static const struct wiphy_wowlan_support mwifiex_wowlan_support = {
.flags = WIPHY_WOWLAN_MAGIC_PKT,
- .n_patterns = MWIFIEX_MAX_FILTERS,
+ .n_patterns = MWIFIEX_MEF_MAX_FILTERS,
.pattern_min_len = 1,
.pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
.max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
return false;
}
+static const struct wiphy_coalesce_support mwifiex_coalesce_support = {
+ .n_rules = MWIFIEX_COALESCE_MAX_RULES,
+ .max_delay = MWIFIEX_MAX_COALESCING_DELAY,
+ .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS,
+ .pattern_min_len = 1,
+ .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN,
+ .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN,
+};
+
/*
* This function registers the device with CFG802.11 subsystem.
*
wiphy->wowlan = &mwifiex_wowlan_support;
#endif
+ wiphy->coalesce = &mwifiex_coalesce_support;
+
wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
return false;
}
-/*
- * This function gets the supported data rates.
- *
- * The function works in both Ad-Hoc and infra mode by printing the
- * band and returning the data rates.
+/* This function gets the supported data rates from bitmask inside
+ * cfg80211_scan_request.
+ */
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+ u8 *rates, u8 radio_type)
+{
+ struct wiphy *wiphy = priv->adapter->wiphy;
+ struct cfg80211_scan_request *request = priv->scan_request;
+ u32 num_rates, rate_mask;
+ struct ieee80211_supported_band *sband;
+ int i;
+
+ if (radio_type) {
+ sband = wiphy->bands[IEEE80211_BAND_5GHZ];
+ if (WARN_ON_ONCE(!sband))
+ return 0;
+ rate_mask = request->rates[IEEE80211_BAND_5GHZ];
+ } else {
+ sband = wiphy->bands[IEEE80211_BAND_2GHZ];
+ if (WARN_ON_ONCE(!sband))
+ return 0;
+ rate_mask = request->rates[IEEE80211_BAND_2GHZ];
+ }
+
+ num_rates = 0;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((BIT(i) & rate_mask) == 0)
+ continue; /* skip rate */
+ rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5);
+ }
+
+ return num_rates;
+}
+
+/* This function gets the supported data rates. The function works in
+ * both Ad-Hoc and infra mode by printing the band and returning the
+ * data rates.
*/
u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates)
{
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/ieee80211.h>
+#include <uapi/linux/if_arp.h>
#include <net/mac80211.h>
#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0)
#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1)
-#define MWIFIEX_BRIDGED_PKTS_THRESHOLD 1024
+#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024
+#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128
enum mwifiex_bss_type {
MWIFIEX_BSS_TYPE_STA = 0,
u8 reserved;
struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS];
} __packed;
+
+struct mwifiex_arp_eth_header {
+ struct arphdr hdr;
+ u8 ar_sha[ETH_ALEN];
+ u8 ar_sip[4];
+ u8 ar_tha[ETH_ALEN];
+ u8 ar_tip[4];
+} __packed;
#endif /* !_MWIFIEX_DECL_H_ */
#define WAPI_KEY_LEN 50
#define MAX_POLL_TRIES 100
-
-#define MAX_MULTI_INTERFACE_POLL_TRIES 1000
-
#define MAX_FIRMWARE_POLL_TRIES 100
#define FIRMWARE_READY_SDIO 0xfedc
#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123)
#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 MWIFIEX_TX_DATA_BUF_SIZE_2K 2048
#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed
#define HostCmd_CMD_SET_BSS_MODE 0x00f7
#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa
+#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
(((event_cause) >> 24) & 0x00ff)
#define MWIFIEX_MAX_PATTERN_LEN 20
-#define MWIFIEX_MAX_OFFSET_LEN 50
+#define MWIFIEX_MAX_OFFSET_LEN 100
#define STACK_NBYTES 100
#define TYPE_DNUM 1
#define TYPE_BYTESEQ 2
struct mwifiex_ie_types_extcap {
struct mwifiex_ie_types_header header;
- u8 ext_cap;
+ u8 ext_capab[0];
} __packed;
struct host_cmd_ds_mac_reg_access {
u8 value;
} __packed;
-struct host_cmd_tlv {
- __le16 type;
- __le16 len;
-} __packed;
-
struct mwifiex_assoc_event {
u8 sta_addr[ETH_ALEN];
__le16 type;
} __packed;
struct host_cmd_tlv_akmp {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le16 key_mgmt;
__le16 key_mgmt_operation;
} __packed;
struct host_cmd_tlv_pwk_cipher {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le16 proto;
u8 cipher;
u8 reserved;
} __packed;
struct host_cmd_tlv_gwk_cipher {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 cipher;
u8 reserved;
} __packed;
struct host_cmd_tlv_passphrase {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 passphrase[0];
} __packed;
struct host_cmd_tlv_wep_key {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 key_index;
u8 is_default;
u8 key[1];
};
struct host_cmd_tlv_auth_type {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 auth_type;
} __packed;
struct host_cmd_tlv_encrypt_protocol {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le16 proto;
} __packed;
struct host_cmd_tlv_ssid {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 ssid[0];
} __packed;
struct host_cmd_tlv_rates {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 rates[0];
} __packed;
struct host_cmd_tlv_bcast_ssid {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 bcast_ctl;
} __packed;
struct host_cmd_tlv_beacon_period {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le16 period;
} __packed;
struct host_cmd_tlv_dtim_period {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 period;
} __packed;
struct host_cmd_tlv_frag_threshold {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le16 frag_thr;
} __packed;
struct host_cmd_tlv_rts_threshold {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le16 rts_thr;
} __packed;
struct host_cmd_tlv_retry_limit {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 limit;
} __packed;
struct host_cmd_tlv_mac_addr {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 mac_addr[ETH_ALEN];
} __packed;
struct host_cmd_tlv_channel_band {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
u8 band_config;
u8 channel;
} __packed;
struct host_cmd_tlv_ageout_timer {
- struct host_cmd_tlv tlv;
+ struct mwifiex_ie_types_header header;
__le32 sta_ao_timer;
} __packed;
__le16 data_len;
} __packed;
+struct coalesce_filt_field_param {
+ u8 operation;
+ u8 operand_len;
+ __le16 offset;
+ u8 operand_byte_stream[4];
+};
+
+struct coalesce_receive_filt_rule {
+ struct mwifiex_ie_types_header header;
+ u8 num_of_fields;
+ u8 pkt_type;
+ __le16 max_coalescing_delay;
+ struct coalesce_filt_field_param params[0];
+} __packed;
+
+struct host_cmd_ds_coalesce_cfg {
+ __le16 action;
+ __le16 num_of_rules;
+ struct coalesce_receive_filt_rule rule[0];
+} __packed;
+
struct host_cmd_ds_command {
__le16 command;
__le16 size;
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;
} params;
} __packed;
u8 *tmp;
input_len = le16_to_cpu(ie_list->len);
- travel_len = sizeof(struct host_cmd_tlv);
+ travel_len = sizeof(struct mwifiex_ie_types_header);
ie_list->len = 0;
priv->csa_chan = 0;
priv->csa_expire_time = 0;
+ priv->del_list_idx = 0;
+ priv->hs2_enabled = false;
return mwifiex_add_bss_prio_tbl(priv);
}
static void
mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
- int i;
-
if (!adapter) {
pr_err("%s: adapter is NULL\n", __func__);
return;
}
- for (i = 0; i < adapter->priv_num; i++) {
- if (adapter->priv[i])
- del_timer_sync(&adapter->priv[i]->scan_delay_timer);
- }
-
mwifiex_cancel_all_pending_cmd(adapter);
/* Free lock variables */
dev_dbg(adapter->dev, "info: free cmd buffer\n");
mwifiex_free_cmd_buffer(adapter);
- del_timer(&adapter->cmd_timer);
-
dev_dbg(adapter->dev, "info: free scan table\n");
- if (adapter->if_ops.cleanup_if)
- adapter->if_ops.cleanup_if(adapter);
-
if (adapter->sleep_cfm)
dev_kfree_skb_any(adapter->sleep_cfm);
}
if (!adapter->winner) {
dev_notice(adapter->dev,
"FW already running! Skip FW dnld\n");
- poll_num = MAX_MULTI_INTERFACE_POLL_TRIES;
goto poll_fw;
}
}
struct subsc_evt_cfg bcn_h_rssi_cfg;
};
-#define MAX_BYTESEQ 6 /* non-adjustable */
-#define MWIFIEX_MAX_FILTERS 10
+#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */
+#define MWIFIEX_MEF_MAX_FILTERS 10
struct mwifiex_mef_filter {
u16 repeat;
u16 offset;
- s8 byte_seq[MAX_BYTESEQ + 1];
+ s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
u8 filt_type;
u8 filt_action;
};
struct mwifiex_mef_entry {
u8 mode;
u8 action;
- struct mwifiex_mef_filter filter[MWIFIEX_MAX_FILTERS];
+ struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS];
};
struct mwifiex_ds_mef_cfg {
MWIFIEX_FUNC_SHUTDOWN,
};
+enum COALESCE_OPERATION {
+ RECV_FILTER_MATCH_TYPE_EQ = 0x80,
+ RECV_FILTER_MATCH_TYPE_NE,
+};
+
+enum COALESCE_PACKET_TYPE {
+ PACKET_TYPE_UNICAST = 1,
+ PACKET_TYPE_MULTICAST = 2,
+ PACKET_TYPE_BROADCAST = 3
+};
+
+#define MWIFIEX_COALESCE_MAX_RULES 8
+#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */
+#define MWIFIEX_COALESCE_MAX_FILTERS 4
+#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */
+
+struct filt_field_param {
+ u8 operation;
+ u8 operand_len;
+ u16 offset;
+ u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ];
+};
+
+struct mwifiex_coalesce_rule {
+ u16 max_coalescing_delay;
+ u8 num_of_fields;
+ u8 pkt_type;
+ struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS];
+};
+
+struct mwifiex_ds_coalesce_cfg {
+ u16 num_of_rules;
+ struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES];
+};
+
#endif /* !_MWIFIEX_IOCTL_H_ */
switch (priv->bss_mode) {
case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
return mwifiex_deauthenticate_infra(priv, mac);
case NL80211_IFTYPE_ADHOC:
return mwifiex_send_cmd_sync(priv,
{
s32 i;
+ if (adapter->if_ops.cleanup_if)
+ adapter->if_ops.cleanup_if(adapter);
+
del_timer(&adapter->cmd_timer);
/* Free private structures */
for (i = 0; i < adapter->priv_num; i++) {
if (adapter->priv[i]) {
mwifiex_free_curr_bcn(adapter->priv[i]);
+ del_timer_sync(&adapter->priv[i]->scan_delay_timer);
kfree(adapter->priv[i]);
}
}
pr_debug("info: %s: free adapter\n", __func__);
}
+/*
+ * This function cancels all works in the queue and destroys
+ * the main workqueue.
+ */
+static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
+{
+ flush_workqueue(adapter->workqueue);
+ destroy_workqueue(adapter->workqueue);
+ adapter->workqueue = NULL;
+}
+
/*
* This function gets firmware and initializes it.
*
*/
static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
{
- int ret;
+ int ret, i;
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;
if (!firmware) {
dev_err(adapter->dev,
"Failed to get firmware %s\n", adapter->fw_name);
- goto done;
+ goto err_dnld_fw;
}
memset(&fw, 0, sizeof(struct mwifiex_fw_image));
else
ret = mwifiex_dnld_fw(adapter, &fw);
if (ret == -1)
- goto done;
+ goto err_dnld_fw;
dev_notice(adapter->dev, "WLAN FW is active\n");
}
/* enable host interrupt after fw dnld is successful */
- if (adapter->if_ops.enable_int)
- adapter->if_ops.enable_int(adapter);
+ if (adapter->if_ops.enable_int) {
+ if (adapter->if_ops.enable_int(adapter))
+ goto err_dnld_fw;
+ }
adapter->init_wait_q_woken = false;
ret = mwifiex_init_fw(adapter);
if (ret == -1) {
- goto done;
+ goto err_init_fw;
} else if (!ret) {
adapter->hw_status = MWIFIEX_HW_STATUS_READY;
goto done;
wait_event_interruptible(adapter->init_wait_q,
adapter->init_wait_q_woken);
if (adapter->hw_status != MWIFIEX_HW_STATUS_READY)
- goto done;
+ goto err_init_fw;
priv = adapter->priv[MWIFIEX_BSS_ROLE_STA];
if (mwifiex_register_cfg80211(adapter)) {
dev_err(adapter->dev, "cannot register with cfg80211\n");
- goto err_init_fw;
+ goto err_register_cfg80211;
}
rtnl_lock();
dev_err(adapter->dev, "cannot create default STA interface\n");
goto err_add_intf;
}
-
- /* Create AP interface by default */
- if (!mwifiex_add_virtual_intf(adapter->wiphy, "uap%d",
- NL80211_IFTYPE_AP, NULL, NULL)) {
- dev_err(adapter->dev, "cannot create default AP interface\n");
- goto err_add_intf;
- }
-
- /* Create P2P interface by default */
- if (!mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d",
- NL80211_IFTYPE_P2P_CLIENT, NULL, NULL)) {
- dev_err(adapter->dev, "cannot create default P2P interface\n");
- goto err_add_intf;
- }
rtnl_unlock();
mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1);
goto done;
err_add_intf:
- mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev);
+ 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:
if (adapter->if_ops.disable_int)
adapter->if_ops.disable_int(adapter);
+err_dnld_fw:
pr_debug("info: %s: unregister device\n", __func__);
- adapter->if_ops.unregister_dev(adapter);
+ if (adapter->if_ops.unregister_dev)
+ adapter->if_ops.unregister_dev(adapter);
+
+ if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
+ (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
+ pr_debug("info: %s: shutdown mwifiex\n", __func__);
+ adapter->init_wait_q_woken = false;
+
+ if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS)
+ wait_event_interruptible(adapter->init_wait_q,
+ adapter->init_wait_q_woken);
+ }
+ adapter->surprise_removed = true;
+ mwifiex_terminate_workqueue(adapter);
+ init_failed = true;
done:
if (adapter->cal_data) {
release_firmware(adapter->cal_data);
adapter->cal_data = NULL;
}
- release_firmware(adapter->firmware);
+ if (adapter->firmware) {
+ release_firmware(adapter->firmware);
+ adapter->firmware = NULL;
+ }
complete(&adapter->fw_load);
+ if (init_failed)
+ mwifiex_free_adapter(adapter);
+ up(sem);
return;
}
mwifiex_main_process(adapter);
}
-/*
- * This function cancels all works in the queue and destroys
- * the main workqueue.
- */
-static void
-mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
-{
- flush_workqueue(adapter->workqueue);
- destroy_workqueue(adapter->workqueue);
- adapter->workqueue = NULL;
-}
-
/*
* This function adds the card.
*
}
adapter->iface_type = iface_type;
+ adapter->card_sem = sem;
adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING;
adapter->surprise_removed = false;
goto err_init_fw;
}
- up(sem);
return 0;
err_init_fw:
pr_debug("info: %s: unregister device\n", __func__);
if (adapter->if_ops.unregister_dev)
adapter->if_ops.unregister_dev(adapter);
-err_registerdev:
- adapter->surprise_removed = true;
- mwifiex_terminate_workqueue(adapter);
-err_kmalloc:
if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) ||
(adapter->hw_status == MWIFIEX_HW_STATUS_READY)) {
pr_debug("info: %s: shutdown mwifiex\n", __func__);
wait_event_interruptible(adapter->init_wait_q,
adapter->init_wait_q_woken);
}
-
+err_registerdev:
+ adapter->surprise_removed = true;
+ mwifiex_terminate_workqueue(adapter);
+err_kmalloc:
mwifiex_free_adapter(adapter);
err_init_sw:
struct list_head list;
struct sk_buff_head skb_head;
u8 ra[ETH_ALEN];
- u32 total_pkts_size;
u32 is_11n_enabled;
u16 max_amsdu;
- u16 pkt_count;
+ u16 ba_pkt_count;
u8 ba_packet_thr;
+ u16 total_pkt_count;
};
struct mwifiex_tid_tbl {
bool scan_aborting;
u8 csa_chan;
unsigned long csa_expire_time;
+ u8 del_list_idx;
+ bool hs2_enabled;
};
enum mwifiex_ba_status {
atomic_t is_tx_received;
atomic_t pending_bridged_pkts;
+ struct semaphore *card_sem;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv,
u8 *rates);
u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates);
+u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv,
+ u8 *rates, u8 radio_type);
u8 mwifiex_is_rate_auto(struct mwifiex_private *priv);
extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE];
void mwifiex_save_curr_bcn(struct mwifiex_private *priv);
*/
static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb)
{
- return (*(u32 *)skb->data == PKT_TYPE_MGMT);
+ return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT);
}
/* This function retrieves channel closed for operation by Channel
return false;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
/*
* Kernel needs to suspend all functions separately. Therefore all
* registered functions must have drivers with suspend and resume
* If already not suspended, this function allocates and sends a host
* sleep activate request to the firmware and turns off the traffic.
*/
-static int mwifiex_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
+static int mwifiex_pcie_suspend(struct device *dev)
{
struct mwifiex_adapter *adapter;
struct pcie_service_card *card;
int hs_actived;
+ struct pci_dev *pdev = to_pci_dev(dev);
if (pdev) {
card = (struct pcie_service_card *) pci_get_drvdata(pdev);
* If already not resumed, this function turns on the traffic and
* sends a host sleep cancel request to the firmware.
*/
-static int mwifiex_pcie_resume(struct pci_dev *pdev)
+static int mwifiex_pcie_resume(struct device *dev)
{
struct mwifiex_adapter *adapter;
struct pcie_service_card *card;
+ struct pci_dev *pdev = to_pci_dev(dev);
if (pdev) {
card = (struct pcie_service_card *) pci_get_drvdata(pdev);
wait_for_completion(&adapter->fw_load);
if (user_rmmod) {
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
if (adapter->is_suspended)
- mwifiex_pcie_resume(pdev);
+ mwifiex_pcie_resume(&pdev->dev);
#endif
for (i = 0; i < adapter->priv_num; i++)
kfree(card);
}
+static void mwifiex_pcie_shutdown(struct pci_dev *pdev)
+{
+ user_rmmod = 1;
+ mwifiex_pcie_remove(pdev);
+
+ return;
+}
+
static DEFINE_PCI_DEVICE_TABLE(mwifiex_ids) = {
{
PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P,
MODULE_DEVICE_TABLE(pci, mwifiex_ids);
+#ifdef CONFIG_PM_SLEEP
+/* Power Management Hooks */
+static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend,
+ mwifiex_pcie_resume);
+#endif
+
/* PCI Device Driver */
static struct pci_driver __refdata mwifiex_pcie = {
.name = "mwifiex_pcie",
.id_table = mwifiex_ids,
.probe = mwifiex_pcie_probe,
.remove = mwifiex_pcie_remove,
-#ifdef CONFIG_PM
- /* Power Management Hooks */
- .suspend = mwifiex_pcie_suspend,
- .resume = mwifiex_pcie_resume,
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &mwifiex_pcie_pm_ops,
+ },
#endif
+ .shutdown = mwifiex_pcie_shutdown,
};
/*
ret = 0;
break;
} else {
- mdelay(100);
+ msleep(100);
ret = -1;
}
}
else if (!winner_status) {
dev_err(adapter->dev, "PCI-E is the winner\n");
adapter->winner = 1;
- ret = -1;
} else {
dev_err(adapter->dev,
"PCI-E is not the winner <%#x,%d>, exit dnld\n",
ret, adapter->winner);
- ret = 0;
}
}
return chan_idx;
}
+/* This function appends rate TLV to scan config command. */
+static int
+mwifiex_append_rate_tlv(struct mwifiex_private *priv,
+ struct mwifiex_scan_cmd_config *scan_cfg_out,
+ u8 radio)
+{
+ struct mwifiex_ie_types_rates_param_set *rates_tlv;
+ u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos;
+ u32 rates_size;
+
+ memset(rates, 0, sizeof(rates));
+
+ tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len;
+
+ if (priv->scan_request)
+ rates_size = mwifiex_get_rates_from_cfg80211(priv, rates,
+ radio);
+ else
+ rates_size = mwifiex_get_supported_rates(priv, rates);
+
+ dev_dbg(priv->adapter->dev, "info: SCAN_CMD: Rates size = %d\n",
+ rates_size);
+ rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos;
+ rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
+ rates_tlv->header.len = cpu_to_le16((u16) rates_size);
+ memcpy(rates_tlv->rates, rates, rates_size);
+ scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size;
+
+ return rates_size;
+}
+
/*
* This function constructs and sends multiple scan config commands to
* the firmware.
struct mwifiex_chan_scan_param_set *tmp_chan_list;
struct mwifiex_chan_scan_param_set *start_chan;
- u32 tlv_idx;
+ u32 tlv_idx, rates_size;
u32 total_scan_time;
u32 done_early;
+ u8 radio_type;
if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) {
dev_dbg(priv->adapter->dev,
tlv_idx = 0;
total_scan_time = 0;
+ radio_type = 0;
chan_tlv_out->header.len = 0;
start_chan = tmp_chan_list;
done_early = false;
continue;
}
+ radio_type = tmp_chan_list->radio_type;
dev_dbg(priv->adapter->dev,
"info: Scan: Chan(%3d), Radio(%d),"
" Mode(%d, %d), Dur(%d)\n",
break;
}
+ rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out,
+ radio_type);
+
priv->adapter->scan_channels = start_chan;
/* Send the scan command to the firmware with the specified
ret = mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11_SCAN,
HostCmd_ACT_GEN_SET, 0,
scan_cfg_out);
+
+ /* rate IE is updated per scan command but same starting
+ * pointer is used each time so that rate IE from earlier
+ * scan_cfg_out->buf is overwritten with new one.
+ */
+ scan_cfg_out->tlv_buf_len -=
+ sizeof(struct mwifiex_ie_types_header) + rates_size;
+
if (ret)
break;
}
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_rates_param_set *rates_tlv;
u8 *tlv_pos;
u32 num_probes;
u32 ssid_len;
u8 radio_type;
int i;
u8 ssid_filter;
- u8 rates[MWIFIEX_SUPPORTED_RATES];
- u32 rates_size;
struct mwifiex_ie_types_htcap *ht_cap;
/* The tlv_buf_len is calculated for each scan command. The TLVs added
}
- /* Append rates tlv */
- memset(rates, 0, sizeof(rates));
-
- rates_size = mwifiex_get_supported_rates(priv, rates);
-
- rates_tlv = (struct mwifiex_ie_types_rates_param_set *) tlv_pos;
- rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES);
- rates_tlv->header.len = cpu_to_le16((u16) rates_size);
- memcpy(rates_tlv->rates, rates, rates_size);
- tlv_pos += sizeof(rates_tlv->header) + rates_size;
-
- dev_dbg(adapter->dev, "info: SCAN_CMD: Rates size = %d\n", rates_size);
-
if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) &&
(priv->adapter->config_bands & BAND_GN ||
priv->adapter->config_bands & BAND_AN)) {
static struct semaphore add_remove_card_sem;
-static int mwifiex_sdio_resume(struct device *dev);
-static void mwifiex_sdio_interrupt(struct sdio_func *func);
-
/*
* SDIO probe.
*
return ret;
}
+/*
+ * SDIO resume.
+ *
+ * Kernel needs to suspend all functions separately. Therefore all
+ * registered functions must have drivers with suspend and resume
+ * methods. Failing that the kernel simply removes the whole card.
+ *
+ * If already not resumed, this function turns on the traffic and
+ * sends a host sleep cancel request to the firmware.
+ */
+static int mwifiex_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ struct sdio_mmc_card *card;
+ struct mwifiex_adapter *adapter;
+ mmc_pm_flag_t pm_flag = 0;
+
+ if (func) {
+ pm_flag = sdio_get_host_pm_caps(func);
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ pr_err("resume: invalid card or adapter\n");
+ return 0;
+ }
+ } else {
+ pr_err("resume: sdio_func is not specified\n");
+ return 0;
+ }
+
+ adapter = card->adapter;
+
+ if (!adapter->is_suspended) {
+ dev_warn(adapter->dev, "device already resumed\n");
+ return 0;
+ }
+
+ adapter->is_suspended = false;
+
+ /* Disable Host Sleep */
+ mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
+ MWIFIEX_ASYNC_CMD);
+
+ return 0;
+}
+
/*
* SDIO remove.
*
return ret;
}
-/*
- * SDIO resume.
- *
- * Kernel needs to suspend all functions separately. Therefore all
- * registered functions must have drivers with suspend and resume
- * methods. Failing that the kernel simply removes the whole card.
- *
- * If already not resumed, this function turns on the traffic and
- * sends a host sleep cancel request to the firmware.
- */
-static int mwifiex_sdio_resume(struct device *dev)
-{
- struct sdio_func *func = dev_to_sdio_func(dev);
- struct sdio_mmc_card *card;
- struct mwifiex_adapter *adapter;
- mmc_pm_flag_t pm_flag = 0;
-
- if (func) {
- pm_flag = sdio_get_host_pm_caps(func);
- card = sdio_get_drvdata(func);
- if (!card || !card->adapter) {
- pr_err("resume: invalid card or adapter\n");
- return 0;
- }
- } else {
- pr_err("resume: sdio_func is not specified\n");
- return 0;
- }
-
- adapter = card->adapter;
-
- if (!adapter->is_suspended) {
- dev_warn(adapter->dev, "device already resumed\n");
- return 0;
- }
-
- adapter->is_suspended = false;
-
- /* Disable Host Sleep */
- mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA),
- MWIFIEX_ASYNC_CMD);
-
- return 0;
-}
-
/* Device ID for SD8786 */
#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116)
/* Device ID for SD8787 */
sdio_release_host(func);
}
+/*
+ * This function reads the interrupt status from card.
+ */
+static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ u8 sdio_ireg;
+ unsigned long flags;
+
+ if (mwifiex_read_data_sync(adapter, card->mp_regs,
+ card->reg->max_mp_regs,
+ REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
+ dev_err(adapter->dev, "read mp_regs failed\n");
+ return;
+ }
+
+ sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
+ if (sdio_ireg) {
+ /*
+ * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+ * For SDIO new mode CMD port interrupts
+ * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+ * UP_LD_CMD_PORT_HOST_INT_STATUS
+ * Clear the interrupt status register
+ */
+ dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
+ spin_lock_irqsave(&adapter->int_lock, flags);
+ adapter->int_status |= sdio_ireg;
+ spin_unlock_irqrestore(&adapter->int_lock, flags);
+ }
+}
+
+/*
+ * SDIO interrupt handler.
+ *
+ * This function reads the interrupt status from firmware and handles
+ * the interrupt in current thread (ksdioirqd) right away.
+ */
+static void
+mwifiex_sdio_interrupt(struct sdio_func *func)
+{
+ struct mwifiex_adapter *adapter;
+ struct sdio_mmc_card *card;
+
+ card = sdio_get_drvdata(func);
+ if (!card || !card->adapter) {
+ pr_debug("int: func=%p card=%p adapter=%p\n",
+ func, card, card ? card->adapter : NULL);
+ return;
+ }
+ adapter = card->adapter;
+
+ if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
+ adapter->ps_state = PS_STATE_AWAKE;
+
+ mwifiex_interrupt_status(adapter);
+ mwifiex_main_process(adapter);
+}
+
/*
* This function enables the host interrupt.
*
ret = 0;
break;
} else {
- mdelay(100);
+ msleep(100);
ret = -1;
}
}
return ret;
}
-/*
- * This function reads the interrupt status from card.
- */
-static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter)
-{
- struct sdio_mmc_card *card = adapter->card;
- u8 sdio_ireg;
- unsigned long flags;
-
- if (mwifiex_read_data_sync(adapter, card->mp_regs,
- card->reg->max_mp_regs,
- REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) {
- dev_err(adapter->dev, "read mp_regs failed\n");
- return;
- }
-
- sdio_ireg = card->mp_regs[HOST_INTSTATUS_REG];
- if (sdio_ireg) {
- /*
- * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
- * For SDIO new mode CMD port interrupts
- * DN_LD_CMD_PORT_HOST_INT_STATUS and/or
- * UP_LD_CMD_PORT_HOST_INT_STATUS
- * Clear the interrupt status register
- */
- dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
- spin_lock_irqsave(&adapter->int_lock, flags);
- adapter->int_status |= sdio_ireg;
- spin_unlock_irqrestore(&adapter->int_lock, flags);
- }
-}
-
-/*
- * SDIO interrupt handler.
- *
- * This function reads the interrupt status from firmware and handles
- * the interrupt in current thread (ksdioirqd) right away.
- */
-static void
-mwifiex_sdio_interrupt(struct sdio_func *func)
-{
- struct mwifiex_adapter *adapter;
- struct sdio_mmc_card *card;
-
- card = sdio_get_drvdata(func);
- if (!card || !card->adapter) {
- pr_debug("int: func=%p card=%p adapter=%p\n",
- func, card, card ? card->adapter : NULL);
- return;
- }
- adapter = card->adapter;
-
- if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP)
- adapter->ps_state = PS_STATE_AWAKE;
-
- mwifiex_interrupt_status(adapter);
- mwifiex_main_process(adapter);
-}
-
/*
* This function decodes a received packet.
*
case MWIFIEX_TYPE_EVENT:
dev_dbg(adapter->dev, "info: --- Rx: Event ---\n");
- adapter->event_cause = *(u32 *) skb->data;
+ adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data);
if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE))
memcpy(adapter->event_body,
for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) {
/* get curr PKT len & type */
- pkt_len = *(u16 *) &curr_ptr[0];
- pkt_type = *(u16 *) &curr_ptr[2];
+ pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]);
+ pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]);
/* copy pkt to deaggr buf */
skb_deaggr = card->mpa_rx.skb_arr[pind];
if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) {
tlv_mac = (void *)((u8 *)&key_material->key_param_set +
key_param_len);
- tlv_mac->tlv.type = cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
- tlv_mac->tlv.len = cpu_to_le16(ETH_ALEN);
+ tlv_mac->header.type =
+ cpu_to_le16(TLV_TYPE_STA_MAC_ADDR);
+ tlv_mac->header.len = cpu_to_le16(ETH_ALEN);
memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN);
cmd_size = key_param_len + S_DS_GEN +
sizeof(key_material->action) +
int i, byte_len;
u8 *stack_ptr = *buffer;
- for (i = 0; i < MWIFIEX_MAX_FILTERS; i++) {
+ for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) {
filter = &mef_entry->filter[i];
if (!filter->filt_type)
break;
*stack_ptr = TYPE_DNUM;
stack_ptr += 1;
- byte_len = filter->byte_seq[MAX_BYTESEQ];
+ byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ];
memcpy(stack_ptr, filter->byte_seq, byte_len);
stack_ptr += byte_len;
*stack_ptr = byte_len;
return 0;
}
+static int
+mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv,
+ struct host_cmd_ds_command *cmd,
+ u16 cmd_action, void *data_buf)
+{
+ struct host_cmd_ds_coalesce_cfg *coalesce_cfg =
+ &cmd->params.coalesce_cfg;
+ struct mwifiex_ds_coalesce_cfg *cfg = data_buf;
+ struct coalesce_filt_field_param *param;
+ u16 cnt, idx, length;
+ struct coalesce_receive_filt_rule *rule;
+
+ cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG);
+ cmd->size = cpu_to_le16(S_DS_GEN);
+
+ coalesce_cfg->action = cpu_to_le16(cmd_action);
+ coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules);
+ rule = coalesce_cfg->rule;
+
+ for (cnt = 0; cnt < cfg->num_of_rules; cnt++) {
+ rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE);
+ rule->max_coalescing_delay =
+ cpu_to_le16(cfg->rule[cnt].max_coalescing_delay);
+ rule->pkt_type = cfg->rule[cnt].pkt_type;
+ rule->num_of_fields = cfg->rule[cnt].num_of_fields;
+
+ length = 0;
+
+ param = rule->params;
+ for (idx = 0; idx < cfg->rule[cnt].num_of_fields; idx++) {
+ param->operation = cfg->rule[cnt].params[idx].operation;
+ param->operand_len =
+ cfg->rule[cnt].params[idx].operand_len;
+ param->offset =
+ cpu_to_le16(cfg->rule[cnt].params[idx].offset);
+ memcpy(param->operand_byte_stream,
+ cfg->rule[cnt].params[idx].operand_byte_stream,
+ param->operand_len);
+
+ length += sizeof(struct coalesce_filt_field_param);
+
+ param++;
+ }
+
+ /* Total rule length is sizeof max_coalescing_delay(u16),
+ * num_of_fields(u8), pkt_type(u8) and total length of the all
+ * params
+ */
+ rule->header.len = cpu_to_le16(length + sizeof(u16) +
+ sizeof(u8) + sizeof(u8));
+
+ /* Add the rule length to the command size*/
+ le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) +
+ sizeof(struct mwifiex_ie_types_header));
+
+ rule = (void *)((u8 *)rule->params + length);
+ }
+
+ /* Add sizeof action, num_of_rules to total command length */
+ le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16));
+
+ return 0;
+}
+
/*
* This function prepares the commands before sending them to the firmware.
*
case HostCmd_CMD_MEF_CFG:
ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf);
break;
+ case HostCmd_CMD_COALESCE_CFG:
+ ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action,
+ data_buf);
+ break;
default:
dev_err(priv->adapter->dev,
"PREP_CMD: unknown cmd- %#x\n", cmd_no);
tlv_buf = ((u8 *)rate_cfg) +
sizeof(struct host_cmd_ds_tx_rate_cfg);
- tlv_buf_len = *(u16 *) (tlv_buf + sizeof(u16));
+ tlv_buf_len = le16_to_cpu(*(__le16 *) (tlv_buf + sizeof(u16)));
while (tlv_buf && tlv_buf_len > 0) {
tlv = (*tlv_buf);
break;
case HostCmd_CMD_MEF_CFG:
break;
+ case HostCmd_CMD_COALESCE_CFG:
+ break;
default:
dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n",
resp->command);
case EVENT_DEAUTHENTICATED:
dev_dbg(adapter->dev, "event: Deauthenticated\n");
+ if (priv->wps.session_enable) {
+ dev_dbg(adapter->dev,
+ "info: receive deauth event in wps session\n");
+ break;
+ }
adapter->dbg.num_event_deauth++;
if (priv->media_connected) {
reason_code =
case EVENT_DISASSOCIATED:
dev_dbg(adapter->dev, "event: Disassociated\n");
+ if (priv->wps.session_enable) {
+ dev_dbg(adapter->dev,
+ "info: receive disassoc event in wps session\n");
+ break;
+ }
adapter->dbg.num_event_disassoc++;
if (priv->media_connected) {
reason_code =
u8 *ie_data_ptr, u16 ie_len)
{
if (ie_len) {
- priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
- if (!priv->wps_ie)
- return -ENOMEM;
- if (ie_len > sizeof(priv->wps_ie)) {
+ if (ie_len > MWIFIEX_MAX_VSIE_LEN) {
dev_dbg(priv->adapter->dev,
"info: failed to copy WPS IE, too big\n");
- kfree(priv->wps_ie);
return -1;
}
+
+ priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL);
+ if (!priv->wps_ie)
+ return -ENOMEM;
+
memcpy(priv->wps_ie, ie_data_ptr, ie_len);
priv->wps_ie_len = ie_len;
dev_dbg(priv->adapter->dev, "cmd: Set wps_ie_len=%d IE=%#x\n",
* this warranty disclaimer.
*/
+#include <uapi/linux/ipv6.h>
+#include <net/ndisc.h>
#include "decl.h"
#include "ioctl.h"
#include "util.h"
#include "11n_aggr.h"
#include "11n_rxreorder.h"
+/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement
+ * frame. If frame has both source and destination mac address as same, this
+ * function drops such gratuitous frames.
+ */
+static bool
+mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv,
+ struct sk_buff *skb)
+{
+ const struct mwifiex_arp_eth_header *arp;
+ struct ethhdr *eth_hdr;
+ struct ipv6hdr *ipv6;
+ struct icmp6hdr *icmpv6;
+
+ eth_hdr = (struct ethhdr *)skb->data;
+ switch (ntohs(eth_hdr->h_proto)) {
+ case ETH_P_ARP:
+ arp = (void *)(skb->data + sizeof(struct ethhdr));
+ if (arp->hdr.ar_op == htons(ARPOP_REPLY) ||
+ arp->hdr.ar_op == htons(ARPOP_REQUEST)) {
+ if (!memcmp(arp->ar_sip, arp->ar_tip, 4))
+ return true;
+ }
+ break;
+ case ETH_P_IPV6:
+ ipv6 = (void *)(skb->data + sizeof(struct ethhdr));
+ icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) +
+ sizeof(struct ipv6hdr));
+ if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) {
+ if (!memcmp(&ipv6->saddr, &ipv6->daddr,
+ sizeof(struct in6_addr)))
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
/*
* This function processes the received packet and forwards it
* to kernel/upper layer.
either the reconstructed EthII frame or the 802.2/llc/snap frame */
skb_pull(skb, hdr_chop);
+ if (priv->hs2_enabled &&
+ mwifiex_discard_gratuitous_arp(priv, skb)) {
+ dev_dbg(priv->adapter->dev, "Bypassed Gratuitous ARP\n");
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
priv->rxpd_rate = local_rx_pd->rx_rate;
priv->rxpd_htinfo = local_rx_pd->ht_info;
u8 *tlv = *tlv_buf;
tlv_akmp = (struct host_cmd_tlv_akmp *)tlv;
- tlv_akmp->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
- tlv_akmp->tlv.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
- sizeof(struct host_cmd_tlv));
+ tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP);
+ tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) -
+ sizeof(struct mwifiex_ie_types_header));
tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation);
tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt);
cmd_size += sizeof(struct host_cmd_tlv_akmp);
if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) {
pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
- pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
- pwk_cipher->tlv.len =
+ pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+ pwk_cipher->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA);
pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa;
cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) {
pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv;
- pwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
- pwk_cipher->tlv.len =
+ pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER);
+ pwk_cipher->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2);
pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2;
cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher);
if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) {
gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv;
- gwk_cipher->tlv.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
- gwk_cipher->tlv.len =
+ gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER);
+ gwk_cipher->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher;
cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher);
tlv += sizeof(struct host_cmd_tlv_gwk_cipher);
if (bss_cfg->wpa_cfg.length) {
passphrase = (struct host_cmd_tlv_passphrase *)tlv;
- passphrase->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
- passphrase->tlv.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
+ passphrase->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE);
+ passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length);
memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase,
bss_cfg->wpa_cfg.length);
- cmd_size += sizeof(struct host_cmd_tlv) +
+ cmd_size += sizeof(struct mwifiex_ie_types_header) +
bss_cfg->wpa_cfg.length;
- tlv += sizeof(struct host_cmd_tlv) + bss_cfg->wpa_cfg.length;
+ tlv += sizeof(struct mwifiex_ie_types_header) +
+ bss_cfg->wpa_cfg.length;
}
*param_size = cmd_size;
(bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 ||
bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) {
wep_key = (struct host_cmd_tlv_wep_key *)tlv;
- wep_key->tlv.type = cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
- wep_key->tlv.len =
+ wep_key->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_WEP_KEY);
+ wep_key->header.len =
cpu_to_le16(bss_cfg->wep_cfg[i].length + 2);
wep_key->key_index = bss_cfg->wep_cfg[i].key_index;
wep_key->is_default = bss_cfg->wep_cfg[i].is_default;
memcpy(wep_key->key, bss_cfg->wep_cfg[i].key,
bss_cfg->wep_cfg[i].length);
- cmd_size += sizeof(struct host_cmd_tlv) + 2 +
+ cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 +
bss_cfg->wep_cfg[i].length;
- tlv += sizeof(struct host_cmd_tlv) + 2 +
+ tlv += sizeof(struct mwifiex_ie_types_header) + 2 +
bss_cfg->wep_cfg[i].length;
}
}
if (bss_cfg->ssid.ssid_len) {
ssid = (struct host_cmd_tlv_ssid *)tlv;
- ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
- ssid->tlv.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
+ ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID);
+ ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len);
memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len);
- cmd_size += sizeof(struct host_cmd_tlv) +
+ cmd_size += sizeof(struct mwifiex_ie_types_header) +
bss_cfg->ssid.ssid_len;
- tlv += sizeof(struct host_cmd_tlv) + bss_cfg->ssid.ssid_len;
+ tlv += sizeof(struct mwifiex_ie_types_header) +
+ bss_cfg->ssid.ssid_len;
bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv;
- bcast_ssid->tlv.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
- bcast_ssid->tlv.len =
+ bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID);
+ bcast_ssid->header.len =
cpu_to_le16(sizeof(bcast_ssid->bcast_ctl));
bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl;
cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid);
}
if (bss_cfg->rates[0]) {
tlv_rates = (struct host_cmd_tlv_rates *)tlv;
- tlv_rates->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
+ tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES);
for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i];
i++)
tlv_rates->rates[i] = bss_cfg->rates[i];
- tlv_rates->tlv.len = cpu_to_le16(i);
+ tlv_rates->header.len = cpu_to_le16(i);
cmd_size += sizeof(struct host_cmd_tlv_rates) + i;
tlv += sizeof(struct host_cmd_tlv_rates) + i;
}
(bss_cfg->band_cfg == BAND_CONFIG_A &&
bss_cfg->channel <= MAX_CHANNEL_BAND_A))) {
chan_band = (struct host_cmd_tlv_channel_band *)tlv;
- chan_band->tlv.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
- chan_band->tlv.len =
+ chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST);
+ chan_band->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
chan_band->band_config = bss_cfg->band_cfg;
chan_band->channel = bss_cfg->channel;
cmd_size += sizeof(struct host_cmd_tlv_channel_band);
if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD &&
bss_cfg->beacon_period <= MAX_BEACON_PERIOD) {
beacon_period = (struct host_cmd_tlv_beacon_period *)tlv;
- beacon_period->tlv.type =
+ beacon_period->header.type =
cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD);
- beacon_period->tlv.len =
+ beacon_period->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
beacon_period->period = cpu_to_le16(bss_cfg->beacon_period);
cmd_size += sizeof(struct host_cmd_tlv_beacon_period);
tlv += sizeof(struct host_cmd_tlv_beacon_period);
if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD &&
bss_cfg->dtim_period <= MAX_DTIM_PERIOD) {
dtim_period = (struct host_cmd_tlv_dtim_period *)tlv;
- dtim_period->tlv.type = cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
- dtim_period->tlv.len =
+ dtim_period->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD);
+ dtim_period->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
dtim_period->period = bss_cfg->dtim_period;
cmd_size += sizeof(struct host_cmd_tlv_dtim_period);
tlv += sizeof(struct host_cmd_tlv_dtim_period);
}
if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) {
rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv;
- rts_threshold->tlv.type =
+ rts_threshold->header.type =
cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD);
- rts_threshold->tlv.len =
+ rts_threshold->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold);
cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
tlv += sizeof(struct host_cmd_tlv_frag_threshold);
if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) &&
(bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) {
frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv;
- frag_threshold->tlv.type =
+ frag_threshold->header.type =
cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD);
- frag_threshold->tlv.len =
+ frag_threshold->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold);
cmd_size += sizeof(struct host_cmd_tlv_frag_threshold);
tlv += sizeof(struct host_cmd_tlv_frag_threshold);
}
if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) {
retry_limit = (struct host_cmd_tlv_retry_limit *)tlv;
- retry_limit->tlv.type = cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
- retry_limit->tlv.len =
+ retry_limit->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT);
+ retry_limit->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
retry_limit->limit = (u8)bss_cfg->retry_limit;
cmd_size += sizeof(struct host_cmd_tlv_retry_limit);
tlv += sizeof(struct host_cmd_tlv_retry_limit);
if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) ||
(bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) {
auth_type = (struct host_cmd_tlv_auth_type *)tlv;
- auth_type->tlv.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
- auth_type->tlv.len =
+ auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth_type->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) -
- sizeof(struct host_cmd_tlv));
+ sizeof(struct mwifiex_ie_types_header));
auth_type->auth_type = (u8)bss_cfg->auth_mode;
cmd_size += sizeof(struct host_cmd_tlv_auth_type);
tlv += sizeof(struct host_cmd_tlv_auth_type);
}
if (bss_cfg->protocol) {
encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv;
- encrypt_protocol->tlv.type =
+ encrypt_protocol->header.type =
cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL);
- encrypt_protocol->tlv.len =
+ encrypt_protocol->header.len =
cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol)
- - sizeof(struct host_cmd_tlv));
+ - sizeof(struct mwifiex_ie_types_header));
encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol);
cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol);
tlv += sizeof(struct host_cmd_tlv_encrypt_protocol);
if (bss_cfg->sta_ao_timer) {
ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
- ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
- ao_timer->tlv.len = cpu_to_le16(sizeof(*ao_timer) -
- sizeof(struct host_cmd_tlv));
+ ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER);
+ ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) -
+ sizeof(struct mwifiex_ie_types_header));
ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer);
cmd_size += sizeof(*ao_timer);
tlv += sizeof(*ao_timer);
if (bss_cfg->ps_sta_ao_timer) {
ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv;
- ps_ao_timer->tlv.type = cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
- ps_ao_timer->tlv.len = cpu_to_le16(sizeof(*ps_ao_timer) -
- sizeof(struct host_cmd_tlv));
+ ps_ao_timer->header.type =
+ cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER);
+ ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) -
+ sizeof(struct mwifiex_ie_types_header));
ps_ao_timer->sta_ao_timer =
cpu_to_le32(bss_cfg->ps_sta_ao_timer);
cmd_size += sizeof(*ps_ao_timer);
static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size)
{
struct mwifiex_ie_list *ap_ie = cmd_buf;
- struct host_cmd_tlv *tlv_ie = (struct host_cmd_tlv *)tlv;
+ struct mwifiex_ie_types_header *tlv_ie = (void *)tlv;
if (!ap_ie || !ap_ie->len || !ap_ie->ie_list)
return -1;
- *ie_size += le16_to_cpu(ap_ie->len) + sizeof(struct host_cmd_tlv);
+ *ie_size += le16_to_cpu(ap_ie->len) +
+ sizeof(struct mwifiex_ie_types_header);
tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE);
tlv_ie->len = ap_ie->len;
- tlv += sizeof(struct host_cmd_tlv);
+ tlv += sizeof(struct mwifiex_ie_types_header);
memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len));
#include "11n_aggr.h"
#include "11n_rxreorder.h"
+/* This function checks if particular RA list has packets more than low bridge
+ * packet threshold and then deletes packet from this RA list.
+ * Function deletes packets from such RA list and returns true. If no such list
+ * is found, false is returned.
+ */
+static bool
+mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv,
+ struct list_head *ra_list_head)
+{
+ struct mwifiex_ra_list_tbl *ra_list;
+ struct sk_buff *skb, *tmp;
+ bool pkt_deleted = false;
+ struct mwifiex_txinfo *tx_info;
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ list_for_each_entry(ra_list, ra_list_head, list) {
+ if (skb_queue_empty(&ra_list->skb_head))
+ continue;
+
+ skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) {
+ tx_info = MWIFIEX_SKB_TXCB(skb);
+ if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) {
+ __skb_unlink(skb, &ra_list->skb_head);
+ mwifiex_write_data_complete(adapter, skb, 0,
+ -1);
+ atomic_dec(&priv->wmm.tx_pkts_queued);
+ pkt_deleted = true;
+ }
+ if ((atomic_read(&adapter->pending_bridged_pkts) <=
+ MWIFIEX_BRIDGED_PKTS_THR_LOW))
+ break;
+ }
+ }
+
+ return pkt_deleted;
+}
+
+/* This function deletes packets from particular RA List. RA list index
+ * from which packets are deleted is preserved so that packets from next RA
+ * list are deleted upon subsequent call thus maintaining fairness.
+ */
+static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv)
+{
+ unsigned long flags;
+ struct list_head *ra_list;
+ int i;
+
+ spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags);
+
+ for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) {
+ if (priv->del_list_idx == MAX_NUM_TID)
+ priv->del_list_idx = 0;
+ ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list;
+ if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list)) {
+ priv->del_list_idx++;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags);
+}
+
+
static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
struct sk_buff *skb)
{
rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset);
if ((atomic_read(&adapter->pending_bridged_pkts) >=
- MWIFIEX_BRIDGED_PKTS_THRESHOLD)) {
+ MWIFIEX_BRIDGED_PKTS_THR_HIGH)) {
dev_err(priv->adapter->dev,
"Tx: Bridge packet limit reached. Drop packet!\n");
kfree_skb(skb);
+ mwifiex_uap_cleanup_tx_queues(priv);
return;
}
atomic_inc(&adapter->tx_pending);
atomic_inc(&adapter->pending_bridged_pkts);
- if ((atomic_read(&adapter->tx_pending) >= MAX_TX_PENDING)) {
- mwifiex_set_trans_start(priv->netdev);
- mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter);
- }
return;
}
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)},
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,
{
struct usb_card_rec *card = usb_get_intfdata(intf);
struct mwifiex_adapter *adapter;
- int i;
if (!card || !card->adapter) {
pr_err("%s: card or card->adapter is NULL\n", __func__);
if (!adapter->priv_num)
return;
- /* In case driver is removed when asynchronous FW downloading is
- * in progress
- */
- wait_for_completion(&adapter->fw_load);
-
- if (user_rmmod) {
-#ifdef CONFIG_PM
- if (adapter->is_suspended)
- mwifiex_usb_resume(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);
- }
-
mwifiex_usb_free(card);
dev_dbg(adapter->dev, "%s: removing card\n", __func__);
return 0;
}
+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);
+}
+
static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter,
struct mwifiex_fw_image *fw)
{
static struct mwifiex_if_ops usb_ops = {
.register_dev = mwifiex_register_dev,
+ .unregister_dev = mwifiex_unregister_dev,
.wakeup = mwifiex_pm_wakeup_card,
.wakeup_complete = mwifiex_pm_wakeup_card_complete,
if (!down_interruptible(&add_remove_card_sem))
up(&add_remove_card_sem);
- /* set the flag as user is removing this module */
- user_rmmod = 1;
+ 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);
+ }
usb_deregister(&mwifiex_usb_driver);
}
memcpy(ra_list->ra, ra, ETH_ALEN);
- ra_list->total_pkts_size = 0;
+ ra_list->total_pkt_count = 0;
dev_dbg(adapter->dev, "info: allocated ra_list %p\n", ra_list);
ra_list, ra_list->is_11n_enabled);
if (ra_list->is_11n_enabled) {
- ra_list->pkt_count = 0;
+ ra_list->ba_pkt_count = 0;
ra_list->ba_packet_thr =
mwifiex_get_random_ba_threshold();
}
skb_queue_tail(&ra_list->skb_head, skb);
- ra_list->total_pkts_size += skb->len;
- ra_list->pkt_count++;
+ ra_list->ba_pkt_count++;
+ ra_list->total_pkt_count++;
if (atomic_read(&priv->wmm.highest_queued_prio) <
tos_to_tid_inv[tid_down])
tx_info = MWIFIEX_SKB_TXCB(skb);
dev_dbg(adapter->dev, "data: dequeuing the packet %p %p\n", ptr, skb);
- ptr->total_pkts_size -= skb->len;
+ ptr->total_pkt_count--;
if (!skb_queue_empty(&ptr->skb_head))
skb_next = skb_peek(&ptr->skb_head);
skb_queue_tail(&ptr->skb_head, skb);
- ptr->total_pkts_size += skb->len;
- ptr->pkt_count++;
+ ptr->total_pkt_count++;
+ ptr->ba_pkt_count++;
tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT;
spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock,
ra_list_flags);
mwifiex_send_single_packet() */
} else {
if (mwifiex_is_ampdu_allowed(priv, tid) &&
- ptr->pkt_count > ptr->ba_packet_thr) {
+ 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,
BA_SETUP_INPROGRESS);
rt2800usb driver.
Supported chips: RT3572
+config RT2800USB_RT3573
+ bool "rt2800usb - Include support for rt3573 devices (EXPERIMENTAL)"
+ ---help---
+ This enables support for RT3573 chipset based wireless USB devices
+ in the rt2800usb driver.
+
config RT2800USB_RT53XX
bool "rt2800usb - Include support for rt53xx devices (EXPERIMENTAL)"
---help---
#define REV_RT3071E 0x0211
#define REV_RT3090E 0x0211
#define REV_RT3390E 0x0211
+#define REV_RT3593E 0x0211
#define REV_RT5390F 0x0502
#define REV_RT5390R 0x1502
#define REV_RT5592C 0x0221
#define TX_PWR_CFG_0_9MBS FIELD32(0x00f00000)
#define TX_PWR_CFG_0_12MBS FIELD32(0x0f000000)
#define TX_PWR_CFG_0_18MBS FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_0_CCK1_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_CCK1_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_0_CCK5_CH0 FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_CCK5_CH1 FIELD32(0x0000f000)
+#define TX_PWR_CFG_0_OFDM6_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_OFDM6_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_0_OFDM12_CH0 FIELD32(0x0f000000)
+#define TX_PWR_CFG_0_OFDM12_CH1 FIELD32(0xf0000000)
/*
* TX_PWR_CFG_1:
#define TX_PWR_CFG_1_MCS1 FIELD32(0x00f00000)
#define TX_PWR_CFG_1_MCS2 FIELD32(0x0f000000)
#define TX_PWR_CFG_1_MCS3 FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_1_OFDM24_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_OFDM24_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_1_OFDM48_CH0 FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_OFDM48_CH1 FIELD32(0x0000f000)
+#define TX_PWR_CFG_1_MCS0_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_MCS0_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_1_MCS2_CH0 FIELD32(0x0f000000)
+#define TX_PWR_CFG_1_MCS2_CH1 FIELD32(0xf0000000)
/*
* TX_PWR_CFG_2:
#define TX_PWR_CFG_2_MCS9 FIELD32(0x00f00000)
#define TX_PWR_CFG_2_MCS10 FIELD32(0x0f000000)
#define TX_PWR_CFG_2_MCS11 FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_2_MCS4_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_MCS4_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_2_MCS6_CH0 FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_MCS6_CH1 FIELD32(0x0000f000)
+#define TX_PWR_CFG_2_MCS8_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_MCS8_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_2_MCS10_CH0 FIELD32(0x0f000000)
+#define TX_PWR_CFG_2_MCS10_CH1 FIELD32(0xf0000000)
/*
* TX_PWR_CFG_3:
#define TX_PWR_CFG_3_UKNOWN2 FIELD32(0x00f00000)
#define TX_PWR_CFG_3_UKNOWN3 FIELD32(0x0f000000)
#define TX_PWR_CFG_3_UKNOWN4 FIELD32(0xf0000000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_3_MCS12_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_MCS12_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_MCS14_CH0 FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_MCS14_CH1 FIELD32(0x0000f000)
+#define TX_PWR_CFG_3_STBC0_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_STBC0_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_3_STBC2_CH0 FIELD32(0x0f000000)
+#define TX_PWR_CFG_3_STBC2_CH1 FIELD32(0xf0000000)
/*
* TX_PWR_CFG_4:
#define TX_PWR_CFG_4_UKNOWN6 FIELD32(0x000000f0)
#define TX_PWR_CFG_4_UKNOWN7 FIELD32(0x00000f00)
#define TX_PWR_CFG_4_UKNOWN8 FIELD32(0x0000f000)
+/* bits for 3T devices */
+#define TX_PWR_CFG_3_STBC4_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_STBC4_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_3_STBC6_CH0 FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_STBC6_CH1 FIELD32(0x0000f000)
/*
* TX_PIN_CFG:
*/
#define EXP_ACK_TIME 0x1380
+/* TX_PWR_CFG_5 */
+#define TX_PWR_CFG_5 0x1384
+#define TX_PWR_CFG_5_MCS16_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_5_MCS16_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_5_MCS16_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_5_MCS18_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_5_MCS18_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_5_MCS18_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_6 */
+#define TX_PWR_CFG_6 0x1388
+#define TX_PWR_CFG_6_MCS20_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_6_MCS20_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_6_MCS20_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_6_MCS22_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_6_MCS22_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_6_MCS22_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_0_EXT */
+#define TX_PWR_CFG_0_EXT 0x1390
+#define TX_PWR_CFG_0_EXT_CCK1_CH2 FIELD32(0x0000000f)
+#define TX_PWR_CFG_0_EXT_CCK5_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_0_EXT_OFDM6_CH2 FIELD32(0x000f0000)
+#define TX_PWR_CFG_0_EXT_OFDM12_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_1_EXT */
+#define TX_PWR_CFG_1_EXT 0x1394
+#define TX_PWR_CFG_1_EXT_OFDM24_CH2 FIELD32(0x0000000f)
+#define TX_PWR_CFG_1_EXT_OFDM48_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_1_EXT_MCS0_CH2 FIELD32(0x000f0000)
+#define TX_PWR_CFG_1_EXT_MCS2_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_2_EXT */
+#define TX_PWR_CFG_2_EXT 0x1398
+#define TX_PWR_CFG_2_EXT_MCS4_CH2 FIELD32(0x0000000f)
+#define TX_PWR_CFG_2_EXT_MCS6_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_2_EXT_MCS8_CH2 FIELD32(0x000f0000)
+#define TX_PWR_CFG_2_EXT_MCS10_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_3_EXT */
+#define TX_PWR_CFG_3_EXT 0x139c
+#define TX_PWR_CFG_3_EXT_MCS12_CH2 FIELD32(0x0000000f)
+#define TX_PWR_CFG_3_EXT_MCS14_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_3_EXT_STBC0_CH2 FIELD32(0x000f0000)
+#define TX_PWR_CFG_3_EXT_STBC2_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_4_EXT */
+#define TX_PWR_CFG_4_EXT 0x13a0
+#define TX_PWR_CFG_4_EXT_STBC4_CH2 FIELD32(0x0000000f)
+#define TX_PWR_CFG_4_EXT_STBC6_CH2 FIELD32(0x00000f00)
+
+/* TX_PWR_CFG_7 */
+#define TX_PWR_CFG_7 0x13d4
+#define TX_PWR_CFG_7_OFDM54_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_7_OFDM54_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_7_OFDM54_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_7_MCS7_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_7_MCS7_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_7_MCS7_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_8 */
+#define TX_PWR_CFG_8 0x13d8
+#define TX_PWR_CFG_8_MCS15_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_8_MCS15_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_8_MCS15_CH2 FIELD32(0x00000f00)
+#define TX_PWR_CFG_8_MCS23_CH0 FIELD32(0x000f0000)
+#define TX_PWR_CFG_8_MCS23_CH1 FIELD32(0x00f00000)
+#define TX_PWR_CFG_8_MCS23_CH2 FIELD32(0x0f000000)
+
+/* TX_PWR_CFG_9 */
+#define TX_PWR_CFG_9 0x13dc
+#define TX_PWR_CFG_9_STBC7_CH0 FIELD32(0x0000000f)
+#define TX_PWR_CFG_9_STBC7_CH1 FIELD32(0x000000f0)
+#define TX_PWR_CFG_9_STBC7_CH2 FIELD32(0x00000f00)
+
/*
* RX_FILTER_CFG: RX configuration register.
*/
#define HW_BEACON_BASE6 0x5dc0
#define HW_BEACON_BASE7 0x5bc0
-#define HW_BEACON_OFFSET(__index) \
+#define HW_BEACON_BASE(__index) \
(((__index) < 4) ? (HW_BEACON_BASE0 + (__index * 0x0200)) : \
(((__index) < 6) ? (HW_BEACON_BASE4 + ((__index - 4) * 0x0200)) : \
(HW_BEACON_BASE6 - ((__index - 6) * 0x0200))))
+#define BEACON_BASE_TO_OFFSET(_base) (((_base) - 0x4000) / 64)
+
/*
* BBP registers.
* The wordsize of the BBP is 8 bits.
#define BBP109_TX0_POWER FIELD8(0x0f)
#define BBP109_TX1_POWER FIELD8(0xf0)
+/* BBP 110 */
+#define BBP110_TX2_POWER FIELD8(0x0f)
+
+
/*
* BBP 138: Unknown
*/
#define RFCSR3_PA2_CASCODE_BIAS_CCKK FIELD8(0x80)
/* Bits for RF3290/RF5360/RF5370/RF5372/RF5390/RF5392 */
#define RFCSR3_VCOCAL_EN FIELD8(0x80)
+/* Bits for RF3050 */
+#define RFCSR3_BIT1 FIELD8(0x02)
+#define RFCSR3_BIT2 FIELD8(0x04)
+#define RFCSR3_BIT3 FIELD8(0x08)
+#define RFCSR3_BIT4 FIELD8(0x10)
+#define RFCSR3_BIT5 FIELD8(0x20)
/*
* FRCSR 5:
#define RFCSR6_R1 FIELD8(0x03)
#define RFCSR6_R2 FIELD8(0x40)
#define RFCSR6_TXDIV FIELD8(0x0c)
+/* bits for RF3053 */
+#define RFCSR6_VCO_IC FIELD8(0xc0)
/*
* RFCSR 7:
* RFCSR 11:
*/
#define RFCSR11_R FIELD8(0x03)
+#define RFCSR11_PLL_MOD FIELD8(0x0c)
#define RFCSR11_MOD FIELD8(0xc0)
+/* bits for RF3053 */
+/* TODO: verify RFCSR11_MOD usage on other chips */
+#define RFCSR11_PLL_IDOH FIELD8(0x40)
+
/*
* RFCSR 12:
#define RFCSR17_R FIELD8(0x20)
#define RFCSR17_CODE FIELD8(0x7f)
+/* RFCSR 18 */
+#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40)
+
+
/*
* RFCSR 20:
*/
#define RFCSR31_RX_H20M FIELD8(0x20)
#define RFCSR31_RX_CALIB FIELD8(0x7f)
+/* RFCSR 32 bits for RF3053 */
+#define RFCSR32_TX_AGC_FC FIELD8(0xf8)
+
+/* RFCSR 36 bits for RF3053 */
+#define RFCSR36_RF_BS FIELD8(0x80)
+
/*
* RFCSR 38:
*/
/*
* RFCSR 39:
*/
+#define RFCSR39_RX_DIV FIELD8(0x40)
#define RFCSR39_RX_LO2_EN FIELD8(0x80)
/*
*/
#define RFCSR49_TX FIELD8(0x3f)
#define RFCSR49_EP FIELD8(0xc0)
+/* bits for RT3593 */
+#define RFCSR49_TX_LO1_IC FIELD8(0x1c)
+#define RFCSR49_TX_DIV FIELD8(0x20)
/*
* RFCSR 50:
*/
#define RFCSR50_TX FIELD8(0x3f)
#define RFCSR50_EP FIELD8(0xc0)
+/* bits for RT3593 */
+#define RFCSR50_TX_LO1_EN FIELD8(0x20)
+#define RFCSR50_TX_LO2_EN FIELD8(0x10)
+
+/* RFCSR 51 */
+/* bits for RT3593 */
+#define RFCSR51_BITS01 FIELD8(0x03)
+#define RFCSR51_BITS24 FIELD8(0x1c)
+#define RFCSR51_BITS57 FIELD8(0xe0)
+
+#define RFCSR53_TX_POWER FIELD8(0x3f)
+#define RFCSR53_UNKNOWN FIELD8(0xc0)
+
+#define RFCSR54_TX_POWER FIELD8(0x3f)
+#define RFCSR54_UNKNOWN FIELD8(0xc0)
+
+#define RFCSR55_TX_POWER FIELD8(0x3f)
+#define RFCSR55_UNKNOWN FIELD8(0xc0)
+
+#define RFCSR57_DRV_CC FIELD8(0xfc)
+
/*
* RF registers
* The wordsize of the EEPROM is 16 bits.
*/
-/*
- * Chip ID
- */
-#define EEPROM_CHIP_ID 0x0000
+enum rt2800_eeprom_word {
+ EEPROM_CHIP_ID = 0,
+ EEPROM_VERSION,
+ EEPROM_MAC_ADDR_0,
+ EEPROM_MAC_ADDR_1,
+ EEPROM_MAC_ADDR_2,
+ EEPROM_NIC_CONF0,
+ EEPROM_NIC_CONF1,
+ EEPROM_FREQ,
+ EEPROM_LED_AG_CONF,
+ EEPROM_LED_ACT_CONF,
+ EEPROM_LED_POLARITY,
+ EEPROM_NIC_CONF2,
+ EEPROM_LNA,
+ EEPROM_RSSI_BG,
+ EEPROM_RSSI_BG2,
+ EEPROM_TXMIXER_GAIN_BG,
+ EEPROM_RSSI_A,
+ EEPROM_RSSI_A2,
+ EEPROM_TXMIXER_GAIN_A,
+ EEPROM_EIRP_MAX_TX_POWER,
+ EEPROM_TXPOWER_DELTA,
+ EEPROM_TXPOWER_BG1,
+ EEPROM_TXPOWER_BG2,
+ EEPROM_TSSI_BOUND_BG1,
+ EEPROM_TSSI_BOUND_BG2,
+ EEPROM_TSSI_BOUND_BG3,
+ EEPROM_TSSI_BOUND_BG4,
+ EEPROM_TSSI_BOUND_BG5,
+ EEPROM_TXPOWER_A1,
+ EEPROM_TXPOWER_A2,
+ EEPROM_TSSI_BOUND_A1,
+ EEPROM_TSSI_BOUND_A2,
+ EEPROM_TSSI_BOUND_A3,
+ EEPROM_TSSI_BOUND_A4,
+ EEPROM_TSSI_BOUND_A5,
+ EEPROM_TXPOWER_BYRATE,
+ EEPROM_BBP_START,
+
+ /* IDs for extended EEPROM format used by three-chain devices */
+ EEPROM_EXT_LNA2,
+ EEPROM_EXT_TXPOWER_BG3,
+ EEPROM_EXT_TXPOWER_A3,
+
+ /* New values must be added before this */
+ EEPROM_WORD_COUNT
+};
/*
* EEPROM Version
*/
-#define EEPROM_VERSION 0x0001
#define EEPROM_VERSION_FAE FIELD16(0x00ff)
#define EEPROM_VERSION_VERSION FIELD16(0xff00)
/*
* HW MAC address.
*/
-#define EEPROM_MAC_ADDR_0 0x0002
#define EEPROM_MAC_ADDR_BYTE0 FIELD16(0x00ff)
#define EEPROM_MAC_ADDR_BYTE1 FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_1 0x0003
#define EEPROM_MAC_ADDR_BYTE2 FIELD16(0x00ff)
#define EEPROM_MAC_ADDR_BYTE3 FIELD16(0xff00)
-#define EEPROM_MAC_ADDR_2 0x0004
#define EEPROM_MAC_ADDR_BYTE4 FIELD16(0x00ff)
#define EEPROM_MAC_ADDR_BYTE5 FIELD16(0xff00)
* TXPATH: 1: 1T, 2: 2T, 3: 3T
* RF_TYPE: RFIC type
*/
-#define EEPROM_NIC_CONF0 0x001a
#define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f)
#define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0)
#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00)
* BT_COEXIST: 0: disable, 1: enable
* DAC_TEST: 0: disable, 1: enable
*/
-#define EEPROM_NIC_CONF1 0x001b
#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)
/*
* EEPROM frequency
*/
-#define EEPROM_FREQ 0x001d
#define EEPROM_FREQ_OFFSET FIELD16(0x00ff)
#define EEPROM_FREQ_LED_MODE FIELD16(0x7f00)
#define EEPROM_FREQ_LED_POLARITY FIELD16(0x1000)
* POLARITY_GPIO_4: Polarity GPIO4 setting.
* LED_MODE: Led mode.
*/
-#define EEPROM_LED_AG_CONF 0x001e
-#define EEPROM_LED_ACT_CONF 0x001f
-#define EEPROM_LED_POLARITY 0x0020
#define EEPROM_LED_POLARITY_RDY_BG FIELD16(0x0001)
#define EEPROM_LED_POLARITY_RDY_A FIELD16(0x0002)
#define EEPROM_LED_POLARITY_ACT FIELD16(0x0004)
* 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 0x0021
#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
*/
-#define EEPROM_LNA 0x0022
#define EEPROM_LNA_BG FIELD16(0x00ff)
#define EEPROM_LNA_A0 FIELD16(0xff00)
/*
* EEPROM RSSI BG offset
*/
-#define EEPROM_RSSI_BG 0x0023
#define EEPROM_RSSI_BG_OFFSET0 FIELD16(0x00ff)
#define EEPROM_RSSI_BG_OFFSET1 FIELD16(0xff00)
/*
* EEPROM RSSI BG2 offset
*/
-#define EEPROM_RSSI_BG2 0x0024
#define EEPROM_RSSI_BG2_OFFSET2 FIELD16(0x00ff)
#define EEPROM_RSSI_BG2_LNA_A1 FIELD16(0xff00)
/*
* EEPROM TXMIXER GAIN BG offset (note overlaps with EEPROM RSSI BG2).
*/
-#define EEPROM_TXMIXER_GAIN_BG 0x0024
#define EEPROM_TXMIXER_GAIN_BG_VAL FIELD16(0x0007)
/*
* EEPROM RSSI A offset
*/
-#define EEPROM_RSSI_A 0x0025
#define EEPROM_RSSI_A_OFFSET0 FIELD16(0x00ff)
#define EEPROM_RSSI_A_OFFSET1 FIELD16(0xff00)
/*
* EEPROM RSSI A2 offset
*/
-#define EEPROM_RSSI_A2 0x0026
#define EEPROM_RSSI_A2_OFFSET2 FIELD16(0x00ff)
#define EEPROM_RSSI_A2_LNA_A2 FIELD16(0xff00)
/*
* EEPROM TXMIXER GAIN A offset (note overlaps with EEPROM RSSI A2).
*/
-#define EEPROM_TXMIXER_GAIN_A 0x0026
#define EEPROM_TXMIXER_GAIN_A_VAL FIELD16(0x0007)
/*
* EEPROM EIRP Maximum TX power values(unit: dbm)
*/
-#define EEPROM_EIRP_MAX_TX_POWER 0x0027
#define EEPROM_EIRP_MAX_TX_POWER_2GHZ FIELD16(0x00ff)
#define EEPROM_EIRP_MAX_TX_POWER_5GHZ FIELD16(0xff00)
* TYPE: 1: Plus the delta value, 0: minus the delta value
* ENABLE: enable tx power compensation for 40BW
*/
-#define EEPROM_TXPOWER_DELTA 0x0028
#define EEPROM_TXPOWER_DELTA_VALUE_2G FIELD16(0x003f)
#define EEPROM_TXPOWER_DELTA_TYPE_2G FIELD16(0x0040)
#define EEPROM_TXPOWER_DELTA_ENABLE_2G FIELD16(0x0080)
/*
* EEPROM TXPOWER 802.11BG
*/
-#define EEPROM_TXPOWER_BG1 0x0029
-#define EEPROM_TXPOWER_BG2 0x0030
#define EEPROM_TXPOWER_BG_SIZE 7
#define EEPROM_TXPOWER_BG_1 FIELD16(0x00ff)
#define EEPROM_TXPOWER_BG_2 FIELD16(0xff00)
* MINUS3: If the actual TSSI is below this boundary, tx power needs to be
* reduced by (agc_step * -3)
*/
-#define EEPROM_TSSI_BOUND_BG1 0x0037
#define EEPROM_TSSI_BOUND_BG1_MINUS4 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_BG1_MINUS3 FIELD16(0xff00)
* MINUS1: If the actual TSSI is below this boundary, tx power needs to be
* reduced by (agc_step * -1)
*/
-#define EEPROM_TSSI_BOUND_BG2 0x0038
#define EEPROM_TSSI_BOUND_BG2_MINUS2 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_BG2_MINUS1 FIELD16(0xff00)
* PLUS1: If the actual TSSI is above this boundary, tx power needs to be
* increased by (agc_step * 1)
*/
-#define EEPROM_TSSI_BOUND_BG3 0x0039
#define EEPROM_TSSI_BOUND_BG3_REF FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_BG3_PLUS1 FIELD16(0xff00)
* PLUS3: If the actual TSSI is above this boundary, tx power needs to be
* increased by (agc_step * 3)
*/
-#define EEPROM_TSSI_BOUND_BG4 0x003a
#define EEPROM_TSSI_BOUND_BG4_PLUS2 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_BG4_PLUS3 FIELD16(0xff00)
* increased by (agc_step * 4)
* AGC_STEP: Temperature compensation step.
*/
-#define EEPROM_TSSI_BOUND_BG5 0x003b
#define EEPROM_TSSI_BOUND_BG5_PLUS4 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_BG5_AGC_STEP FIELD16(0xff00)
/*
* EEPROM TXPOWER 802.11A
*/
-#define EEPROM_TXPOWER_A1 0x003c
-#define EEPROM_TXPOWER_A2 0x0053
#define EEPROM_TXPOWER_A_SIZE 6
#define EEPROM_TXPOWER_A_1 FIELD16(0x00ff)
#define EEPROM_TXPOWER_A_2 FIELD16(0xff00)
+/* EEPROM_TXPOWER_{A,G} fields for RT3593 */
+#define EEPROM_TXPOWER_ALC FIELD8(0x1f)
+#define EEPROM_TXPOWER_FINE_CTRL FIELD8(0xe0)
+
/*
* EEPROM temperature compensation boundaries 802.11A
* MINUS4: If the actual TSSI is below this boundary, tx power needs to be
* MINUS3: If the actual TSSI is below this boundary, tx power needs to be
* reduced by (agc_step * -3)
*/
-#define EEPROM_TSSI_BOUND_A1 0x006a
#define EEPROM_TSSI_BOUND_A1_MINUS4 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_A1_MINUS3 FIELD16(0xff00)
* MINUS1: If the actual TSSI is below this boundary, tx power needs to be
* reduced by (agc_step * -1)
*/
-#define EEPROM_TSSI_BOUND_A2 0x006b
#define EEPROM_TSSI_BOUND_A2_MINUS2 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_A2_MINUS1 FIELD16(0xff00)
* PLUS1: If the actual TSSI is above this boundary, tx power needs to be
* increased by (agc_step * 1)
*/
-#define EEPROM_TSSI_BOUND_A3 0x006c
#define EEPROM_TSSI_BOUND_A3_REF FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_A3_PLUS1 FIELD16(0xff00)
* PLUS3: If the actual TSSI is above this boundary, tx power needs to be
* increased by (agc_step * 3)
*/
-#define EEPROM_TSSI_BOUND_A4 0x006d
#define EEPROM_TSSI_BOUND_A4_PLUS2 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_A4_PLUS3 FIELD16(0xff00)
* increased by (agc_step * 4)
* AGC_STEP: Temperature compensation step.
*/
-#define EEPROM_TSSI_BOUND_A5 0x006e
#define EEPROM_TSSI_BOUND_A5_PLUS4 FIELD16(0x00ff)
#define EEPROM_TSSI_BOUND_A5_AGC_STEP FIELD16(0xff00)
/*
* EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode
*/
-#define EEPROM_TXPOWER_BYRATE 0x006f
#define EEPROM_TXPOWER_BYRATE_SIZE 9
#define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f)
/*
* EEPROM BBP.
*/
-#define EEPROM_BBP_START 0x0078
#define EEPROM_BBP_SIZE 16
#define EEPROM_BBP_VALUE FIELD16(0x00ff)
#define EEPROM_BBP_REG_ID FIELD16(0xff00)
+/* EEPROM_EXT_LNA2 */
+#define EEPROM_EXT_LNA2_A1 FIELD16(0x00ff)
+#define EEPROM_EXT_LNA2_A2 FIELD16(0xff00)
+
/*
* EEPROM IQ Calibration, unlike other entries those are byte addresses.
*/
#define MCU_RADAR 0x60
#define MCU_BOOT_SIGNAL 0x72
#define MCU_ANT_SELECT 0X73
+#define MCU_FREQ_OFFSET 0x74
#define MCU_BBP_SIGNAL 0x80
#define MCU_POWER_SAVE 0x83
#define MCU_BAND_SELECT 0x91
#define TXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32))
#define RXWI_DESC_SIZE_4WORDS (4 * sizeof(__le32))
+#define RXWI_DESC_SIZE_5WORDS (5 * sizeof(__le32))
#define RXWI_DESC_SIZE_6WORDS (6 * sizeof(__le32))
/*
#define MAX_A_TXPOWER 15
#define DEFAULT_TXPOWER 5
+#define MIN_A_TXPOWER_3593 0
+#define MAX_A_TXPOWER_3593 31
+
#define TXPOWER_G_FROM_DEV(__txpower) \
((__txpower) > MAX_G_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
-#define TXPOWER_G_TO_DEV(__txpower) \
- clamp_t(char, __txpower, MIN_G_TXPOWER, MAX_G_TXPOWER)
-
#define TXPOWER_A_FROM_DEV(__txpower) \
((__txpower) > MAX_A_TXPOWER) ? DEFAULT_TXPOWER : (__txpower)
-#define TXPOWER_A_TO_DEV(__txpower) \
- clamp_t(char, __txpower, MIN_A_TXPOWER, MAX_A_TXPOWER)
-
/*
* Board's maximun TX power limitation
*/
mutex_unlock(&rt2x00dev->csr_mutex);
}
+static const unsigned int rt2800_eeprom_map[EEPROM_WORD_COUNT] = {
+ [EEPROM_CHIP_ID] = 0x0000,
+ [EEPROM_VERSION] = 0x0001,
+ [EEPROM_MAC_ADDR_0] = 0x0002,
+ [EEPROM_MAC_ADDR_1] = 0x0003,
+ [EEPROM_MAC_ADDR_2] = 0x0004,
+ [EEPROM_NIC_CONF0] = 0x001a,
+ [EEPROM_NIC_CONF1] = 0x001b,
+ [EEPROM_FREQ] = 0x001d,
+ [EEPROM_LED_AG_CONF] = 0x001e,
+ [EEPROM_LED_ACT_CONF] = 0x001f,
+ [EEPROM_LED_POLARITY] = 0x0020,
+ [EEPROM_NIC_CONF2] = 0x0021,
+ [EEPROM_LNA] = 0x0022,
+ [EEPROM_RSSI_BG] = 0x0023,
+ [EEPROM_RSSI_BG2] = 0x0024,
+ [EEPROM_TXMIXER_GAIN_BG] = 0x0024, /* overlaps with RSSI_BG2 */
+ [EEPROM_RSSI_A] = 0x0025,
+ [EEPROM_RSSI_A2] = 0x0026,
+ [EEPROM_TXMIXER_GAIN_A] = 0x0026, /* overlaps with RSSI_A2 */
+ [EEPROM_EIRP_MAX_TX_POWER] = 0x0027,
+ [EEPROM_TXPOWER_DELTA] = 0x0028,
+ [EEPROM_TXPOWER_BG1] = 0x0029,
+ [EEPROM_TXPOWER_BG2] = 0x0030,
+ [EEPROM_TSSI_BOUND_BG1] = 0x0037,
+ [EEPROM_TSSI_BOUND_BG2] = 0x0038,
+ [EEPROM_TSSI_BOUND_BG3] = 0x0039,
+ [EEPROM_TSSI_BOUND_BG4] = 0x003a,
+ [EEPROM_TSSI_BOUND_BG5] = 0x003b,
+ [EEPROM_TXPOWER_A1] = 0x003c,
+ [EEPROM_TXPOWER_A2] = 0x0053,
+ [EEPROM_TSSI_BOUND_A1] = 0x006a,
+ [EEPROM_TSSI_BOUND_A2] = 0x006b,
+ [EEPROM_TSSI_BOUND_A3] = 0x006c,
+ [EEPROM_TSSI_BOUND_A4] = 0x006d,
+ [EEPROM_TSSI_BOUND_A5] = 0x006e,
+ [EEPROM_TXPOWER_BYRATE] = 0x006f,
+ [EEPROM_BBP_START] = 0x0078,
+};
+
+static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
+ [EEPROM_CHIP_ID] = 0x0000,
+ [EEPROM_VERSION] = 0x0001,
+ [EEPROM_MAC_ADDR_0] = 0x0002,
+ [EEPROM_MAC_ADDR_1] = 0x0003,
+ [EEPROM_MAC_ADDR_2] = 0x0004,
+ [EEPROM_NIC_CONF0] = 0x001a,
+ [EEPROM_NIC_CONF1] = 0x001b,
+ [EEPROM_NIC_CONF2] = 0x001c,
+ [EEPROM_EIRP_MAX_TX_POWER] = 0x0020,
+ [EEPROM_FREQ] = 0x0022,
+ [EEPROM_LED_AG_CONF] = 0x0023,
+ [EEPROM_LED_ACT_CONF] = 0x0024,
+ [EEPROM_LED_POLARITY] = 0x0025,
+ [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,
+ [EEPROM_TSSI_BOUND_BG1] = 0x0045,
+ [EEPROM_TSSI_BOUND_BG2] = 0x0046,
+ [EEPROM_TSSI_BOUND_BG3] = 0x0047,
+ [EEPROM_TSSI_BOUND_BG4] = 0x0048,
+ [EEPROM_TSSI_BOUND_BG5] = 0x0049,
+ [EEPROM_TXPOWER_A1] = 0x004b,
+ [EEPROM_TXPOWER_A2] = 0x0065,
+ [EEPROM_EXT_TXPOWER_A3] = 0x007f,
+ [EEPROM_TSSI_BOUND_A1] = 0x009a,
+ [EEPROM_TSSI_BOUND_A2] = 0x009b,
+ [EEPROM_TSSI_BOUND_A3] = 0x009c,
+ [EEPROM_TSSI_BOUND_A4] = 0x009d,
+ [EEPROM_TSSI_BOUND_A5] = 0x009e,
+ [EEPROM_TXPOWER_BYRATE] = 0x00a0,
+};
+
+static unsigned int rt2800_eeprom_word_index(struct rt2x00_dev *rt2x00dev,
+ const enum rt2800_eeprom_word word)
+{
+ const unsigned int *map;
+ unsigned int index;
+
+ if (WARN_ONCE(word >= EEPROM_WORD_COUNT,
+ "%s: invalid EEPROM word %d\n",
+ wiphy_name(rt2x00dev->hw->wiphy), word))
+ return 0;
+
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ map = rt2800_eeprom_map_ext;
+ else
+ map = rt2800_eeprom_map;
+
+ index = map[word];
+
+ /* Index 0 is valid only for EEPROM_CHIP_ID.
+ * Otherwise it means that the offset of the
+ * given word is not initialized in the map,
+ * or that the field is not usable on the
+ * actual chipset.
+ */
+ WARN_ONCE(word != EEPROM_CHIP_ID && index == 0,
+ "%s: invalid access of EEPROM word %d\n",
+ wiphy_name(rt2x00dev->hw->wiphy), word);
+
+ return index;
+}
+
+static void *rt2800_eeprom_addr(struct rt2x00_dev *rt2x00dev,
+ const enum rt2800_eeprom_word word)
+{
+ unsigned int index;
+
+ index = rt2800_eeprom_word_index(rt2x00dev, word);
+ return rt2x00_eeprom_addr(rt2x00dev, index);
+}
+
+static void rt2800_eeprom_read(struct rt2x00_dev *rt2x00dev,
+ const enum rt2800_eeprom_word word, u16 *data)
+{
+ unsigned int index;
+
+ index = rt2800_eeprom_word_index(rt2x00dev, word);
+ rt2x00_eeprom_read(rt2x00dev, index, data);
+}
+
+static void rt2800_eeprom_write(struct rt2x00_dev *rt2x00dev,
+ const enum rt2800_eeprom_word word, u16 data)
+{
+ unsigned int index;
+
+ index = rt2800_eeprom_word_index(rt2x00dev, word);
+ rt2x00_eeprom_write(rt2x00dev, index, data);
+}
+
+static void rt2800_eeprom_read_from_array(struct rt2x00_dev *rt2x00dev,
+ const enum rt2800_eeprom_word array,
+ unsigned int offset,
+ u16 *data)
+{
+ unsigned int index;
+
+ index = rt2800_eeprom_word_index(rt2x00dev, array);
+ rt2x00_eeprom_read(rt2x00dev, index + offset, data);
+}
+
static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
{
u32 reg;
}
EXPORT_SYMBOL_GPL(rt2800_disable_wpdma);
+void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev,
+ unsigned short *txwi_size,
+ unsigned short *rxwi_size)
+{
+ switch (rt2x00dev->chip.rt) {
+ case RT3593:
+ *txwi_size = TXWI_DESC_SIZE_4WORDS;
+ *rxwi_size = RXWI_DESC_SIZE_5WORDS;
+ break;
+
+ case RT5592:
+ *txwi_size = TXWI_DESC_SIZE_5WORDS;
+ *rxwi_size = RXWI_DESC_SIZE_6WORDS;
+ break;
+
+ default:
+ *txwi_size = TXWI_DESC_SIZE_4WORDS;
+ *rxwi_size = RXWI_DESC_SIZE_4WORDS;
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(rt2800_get_txwi_rxwi_size);
+
static bool rt2800_check_firmware_crc(const u8 *data, const size_t len)
{
u16 fw_crc;
u8 offset2;
if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2);
} else {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0);
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2);
}
}
EXPORT_SYMBOL_GPL(rt2800_txdone_entry);
+static unsigned int rt2800_hw_beacon_base(struct rt2x00_dev *rt2x00dev,
+ unsigned int index)
+{
+ return HW_BEACON_BASE(index);
+}
+
+static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
+ unsigned int index)
+{
+ return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
+}
+
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
return;
}
- beacon_base = HW_BEACON_OFFSET(entry->entry_idx);
+ beacon_base = rt2800_hw_beacon_base(rt2x00dev, entry->entry_idx);
+
rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
entry->skb->len + padding_len);
EXPORT_SYMBOL_GPL(rt2800_write_beacon);
static inline void rt2800_clear_beacon_register(struct rt2x00_dev *rt2x00dev,
- unsigned int beacon_base)
+ unsigned int index)
{
int i;
const int txwi_desc_size = rt2x00dev->bcn->winfo_size;
+ unsigned int beacon_base;
+
+ beacon_base = rt2800_hw_beacon_base(rt2x00dev, index);
/*
* For the Beacon base registers we only need to clear
/*
* Clear beacon.
*/
- rt2800_clear_beacon_register(rt2x00dev,
- HW_BEACON_OFFSET(entry->entry_idx));
+ rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
/*
* Enabled beaconing again.
.word_count = CSR_REG_SIZE / sizeof(u32),
},
.eeprom = {
+ /* NOTE: The local EEPROM access functions can't
+ * be used here, use the generic versions instead.
+ */
.read = rt2x00_eeprom_read,
.write = rt2x00_eeprom_write,
.word_base = EEPROM_BASE,
led_r_mode = rt2x00_get_field32(reg, LED_CFG_LED_POLAR) ? 0 : 3;
if (led_g_mode != rt2x00_get_field32(reg, LED_CFG_G_LED_MODE) ||
led_r_mode != rt2x00_get_field32(reg, LED_CFG_R_LED_MODE)) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
led_ctrl = rt2x00_get_field16(eeprom, EEPROM_FREQ_LED_MODE);
if (led_ctrl == 0 || led_ctrl > 0x40) {
rt2x00_set_field32(®, LED_CFG_G_LED_MODE, led_g_mode);
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
break;
case 3:
- rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 0);
+ rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
break;
}
rt2x00_rt(rt2x00dev, RT3090) ||
rt2x00_rt(rt2x00dev, RT3352) ||
rt2x00_rt(rt2x00dev, RT3390)) {
- rt2x00_eeprom_read(rt2x00dev,
+ rt2800_eeprom_read(rt2x00dev,
EEPROM_NIC_CONF1, &eeprom);
if (rt2x00_get_field16(eeprom,
EEPROM_NIC_CONF1_ANT_DIVERSITY))
rt2800_bbp_write(rt2x00dev, 3, r3);
rt2800_bbp_write(rt2x00dev, 1, r1);
+
+ if (rt2x00_rt(rt2x00dev, RT3593)) {
+ if (ant->rx_chain_num == 1)
+ rt2800_bbp_write(rt2x00dev, 86, 0x00);
+ else
+ rt2800_bbp_write(rt2x00dev, 86, 0x46);
+ }
}
EXPORT_SYMBOL_GPL(rt2800_config_ant);
short lna_gain;
if (libconf->rf.channel <= 14) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_BG);
} else if (libconf->rf.channel <= 64) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &eeprom);
lna_gain = rt2x00_get_field16(eeprom, EEPROM_LNA_A0);
} else if (libconf->rf.channel <= 128) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
- lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_LNA_A1);
+ if (rt2x00_rt(rt2x00dev, RT3593)) {
+ rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom,
+ EEPROM_EXT_LNA2_A1);
+ } else {
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom,
+ EEPROM_RSSI_BG2_LNA_A1);
+ }
} else {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
- lna_gain = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_LNA_A2);
+ if (rt2x00_rt(rt2x00dev, RT3593)) {
+ rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom,
+ EEPROM_EXT_LNA2_A2);
+ } else {
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
+ lna_gain = rt2x00_get_field16(eeprom,
+ EEPROM_RSSI_A2_LNA_A2);
+ }
}
rt2x00dev->lna_gain = lna_gain;
}
+#define FREQ_OFFSET_BOUND 0x5f
+
+static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev)
+{
+ u8 freq_offset, prev_freq_offset;
+ u8 rfcsr, prev_rfcsr;
+
+ freq_offset = rt2x00_get_field8(rt2x00dev->freq_offset, RFCSR17_CODE);
+ freq_offset = min_t(u8, freq_offset, FREQ_OFFSET_BOUND);
+
+ rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
+ prev_rfcsr = rfcsr;
+
+ rt2x00_set_field8(&rfcsr, RFCSR17_CODE, freq_offset);
+ if (rfcsr == prev_rfcsr)
+ return;
+
+ if (rt2x00_is_usb(rt2x00dev)) {
+ rt2800_mcu_request(rt2x00dev, MCU_FREQ_OFFSET, 0xff,
+ freq_offset, prev_rfcsr);
+ return;
+ }
+
+ prev_freq_offset = rt2x00_get_field8(prev_rfcsr, RFCSR17_CODE);
+ while (prev_freq_offset != freq_offset) {
+ if (prev_freq_offset < freq_offset)
+ prev_freq_offset++;
+ else
+ prev_freq_offset--;
+
+ rt2x00_set_field8(&rfcsr, RFCSR17_CODE, prev_freq_offset);
+ rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+
+ usleep_range(1000, 1500);
+ }
+}
+
static void rt2800_config_channel_rf2xxx(struct rt2x00_dev *rt2x00dev,
struct ieee80211_conf *conf,
struct rf_channel *rf,
rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
}
-#define POWER_BOUND 0x27
-#define POWER_BOUND_5G 0x2b
-#define FREQ_OFFSET_BOUND 0x5f
-
-static void rt2800_adjust_freq_offset(struct rt2x00_dev *rt2x00dev)
+static void rt2800_config_channel_rf3053(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_conf *conf,
+ struct rf_channel *rf,
+ struct channel_info *info)
{
+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+ u8 txrx_agc_fc;
+ u8 txrx_h20m;
u8 rfcsr;
+ u8 bbp;
+ const bool txbf_enabled = false; /* TODO */
- rt2800_rfcsr_read(rt2x00dev, 17, &rfcsr);
- if (rt2x00dev->freq_offset > FREQ_OFFSET_BOUND)
- rt2x00_set_field8(&rfcsr, RFCSR17_CODE, FREQ_OFFSET_BOUND);
+ /* TODO: use TX{0,1,2}FinePowerControl values from EEPROM */
+ rt2800_bbp_read(rt2x00dev, 109, &bbp);
+ rt2x00_set_field8(&bbp, BBP109_TX0_POWER, 0);
+ rt2x00_set_field8(&bbp, BBP109_TX1_POWER, 0);
+ rt2800_bbp_write(rt2x00dev, 109, bbp);
+
+ rt2800_bbp_read(rt2x00dev, 110, &bbp);
+ rt2x00_set_field8(&bbp, BBP110_TX2_POWER, 0);
+ rt2800_bbp_write(rt2x00dev, 110, bbp);
+
+ if (rf->channel <= 14) {
+ /* Restore BBP 25 & 26 for 2.4 GHz */
+ rt2800_bbp_write(rt2x00dev, 25, drv_data->bbp25);
+ rt2800_bbp_write(rt2x00dev, 26, drv_data->bbp26);
+ } else {
+ /* Hard code BBP 25 & 26 for 5GHz */
+
+ /* Enable IQ Phase correction */
+ rt2800_bbp_write(rt2x00dev, 25, 0x09);
+ /* Setup IQ Phase correction value */
+ rt2800_bbp_write(rt2x00dev, 26, 0xff);
+ }
+
+ rt2800_rfcsr_write(rt2x00dev, 8, rf->rf1);
+ rt2800_rfcsr_write(rt2x00dev, 9, rf->rf3 & 0xf);
+
+ rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR11_R, (rf->rf2 & 0x3));
+ rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 11, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR11_PLL_IDOH, 1);
+ if (rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 1);
else
- rt2x00_set_field8(&rfcsr, RFCSR17_CODE, rt2x00dev->freq_offset);
- rt2800_rfcsr_write(rt2x00dev, 17, rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR11_PLL_MOD, 2);
+ rt2800_rfcsr_write(rt2x00dev, 11, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 53, &rfcsr);
+ if (rf->channel <= 14) {
+ rfcsr = 0;
+ rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER,
+ info->default_power1 & 0x1f);
+ } else {
+ if (rt2x00_is_usb(rt2x00dev))
+ rfcsr = 0x40;
+
+ rt2x00_set_field8(&rfcsr, RFCSR53_TX_POWER,
+ ((info->default_power1 & 0x18) << 1) |
+ (info->default_power1 & 7));
+ }
+ rt2800_rfcsr_write(rt2x00dev, 53, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 55, &rfcsr);
+ if (rf->channel <= 14) {
+ rfcsr = 0;
+ rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER,
+ info->default_power2 & 0x1f);
+ } else {
+ if (rt2x00_is_usb(rt2x00dev))
+ rfcsr = 0x40;
+
+ rt2x00_set_field8(&rfcsr, RFCSR55_TX_POWER,
+ ((info->default_power2 & 0x18) << 1) |
+ (info->default_power2 & 7));
+ }
+ rt2800_rfcsr_write(rt2x00dev, 55, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 54, &rfcsr);
+ if (rf->channel <= 14) {
+ rfcsr = 0;
+ rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER,
+ info->default_power3 & 0x1f);
+ } else {
+ if (rt2x00_is_usb(rt2x00dev))
+ rfcsr = 0x40;
+
+ rt2x00_set_field8(&rfcsr, RFCSR54_TX_POWER,
+ ((info->default_power3 & 0x18) << 1) |
+ (info->default_power3 & 7));
+ }
+ rt2800_rfcsr_write(rt2x00dev, 54, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+
+ switch (rt2x00dev->default_ant.tx_chain_num) {
+ case 3:
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 1);
+ /* fallthrough */
+ case 2:
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 1);
+ /* fallthrough */
+ case 1:
+ rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
+ break;
+ }
+
+ switch (rt2x00dev->default_ant.rx_chain_num) {
+ case 3:
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 1);
+ /* fallthrough */
+ case 2:
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX1_PD, 1);
+ /* fallthrough */
+ case 1:
+ rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
+ break;
+ }
+ rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+ rt2800_adjust_freq_offset(rt2x00dev);
+
+ if (conf_is_ht40(conf)) {
+ txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw40,
+ RFCSR24_TX_AGC_FC);
+ txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw40,
+ RFCSR24_TX_H20M);
+ } else {
+ txrx_agc_fc = rt2x00_get_field8(drv_data->calibration_bw20,
+ RFCSR24_TX_AGC_FC);
+ txrx_h20m = rt2x00_get_field8(drv_data->calibration_bw20,
+ RFCSR24_TX_H20M);
+ }
+
+ /* NOTE: the reference driver does not writes the new value
+ * back to RFCSR 32
+ */
+ rt2800_rfcsr_read(rt2x00dev, 32, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR32_TX_AGC_FC, txrx_agc_fc);
+
+ if (rf->channel <= 14)
+ rfcsr = 0xa0;
+ else
+ rfcsr = 0x80;
+ rt2800_rfcsr_write(rt2x00dev, 31, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR30_TX_H20M, txrx_h20m);
+ rt2x00_set_field8(&rfcsr, RFCSR30_RX_H20M, txrx_h20m);
+ rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+ /* Band selection */
+ rt2800_rfcsr_read(rt2x00dev, 36, &rfcsr);
+ if (rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 1);
+ else
+ rt2x00_set_field8(&rfcsr, RFCSR36_RF_BS, 0);
+ rt2800_rfcsr_write(rt2x00dev, 36, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 34, &rfcsr);
+ if (rf->channel <= 14)
+ rfcsr = 0x3c;
+ else
+ rfcsr = 0x20;
+ rt2800_rfcsr_write(rt2x00dev, 34, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 12, &rfcsr);
+ if (rf->channel <= 14)
+ rfcsr = 0x1a;
+ else
+ rfcsr = 0x12;
+ rt2800_rfcsr_write(rt2x00dev, 12, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 6, &rfcsr);
+ if (rf->channel >= 1 && rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1);
+ else if (rf->channel >= 36 && rf->channel <= 64)
+ rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2);
+ else if (rf->channel >= 100 && rf->channel <= 128)
+ rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 2);
+ else
+ rt2x00_set_field8(&rfcsr, RFCSR6_VCO_IC, 1);
+ rt2800_rfcsr_write(rt2x00dev, 6, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2);
+ rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+ rt2800_rfcsr_write(rt2x00dev, 46, 0x60);
+
+ if (rf->channel <= 14) {
+ rt2800_rfcsr_write(rt2x00dev, 10, 0xd3);
+ rt2800_rfcsr_write(rt2x00dev, 13, 0x12);
+ } else {
+ rt2800_rfcsr_write(rt2x00dev, 10, 0xd8);
+ rt2800_rfcsr_write(rt2x00dev, 13, 0x23);
+ }
+
+ rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR51_BITS01, 1);
+ rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+ if (rf->channel <= 14) {
+ rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 5);
+ rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 3);
+ } else {
+ rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, 4);
+ rt2x00_set_field8(&rfcsr, RFCSR51_BITS57, 2);
+ }
+ rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 49, &rfcsr);
+ if (rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 3);
+ else
+ rt2x00_set_field8(&rfcsr, RFCSR49_TX_LO1_IC, 2);
+
+ if (txbf_enabled)
+ rt2x00_set_field8(&rfcsr, RFCSR49_TX_DIV, 1);
+
+ rt2800_rfcsr_write(rt2x00dev, 49, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO1_EN, 0);
+ rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 57, &rfcsr);
+ if (rf->channel <= 14)
+ rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x1b);
+ else
+ rt2x00_set_field8(&rfcsr, RFCSR57_DRV_CC, 0x0f);
+ rt2800_rfcsr_write(rt2x00dev, 57, rfcsr);
+
+ if (rf->channel <= 14) {
+ rt2800_rfcsr_write(rt2x00dev, 44, 0x93);
+ rt2800_rfcsr_write(rt2x00dev, 52, 0x45);
+ } else {
+ rt2800_rfcsr_write(rt2x00dev, 44, 0x9b);
+ rt2800_rfcsr_write(rt2x00dev, 52, 0x05);
+ }
+
+ /* Initiate VCO calibration */
+ rt2800_rfcsr_read(rt2x00dev, 3, &rfcsr);
+ if (rf->channel <= 14) {
+ rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+ } else {
+ rt2x00_set_field8(&rfcsr, RFCSR3_BIT1, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR3_BIT2, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR3_BIT3, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR3_BIT4, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR3_BIT5, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR3_VCOCAL_EN, 1);
+ }
+ rt2800_rfcsr_write(rt2x00dev, 3, rfcsr);
+
+ if (rf->channel >= 1 && rf->channel <= 14) {
+ rfcsr = 0x23;
+ if (txbf_enabled)
+ rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+ rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+ rt2800_rfcsr_write(rt2x00dev, 45, 0xbb);
+ } else if (rf->channel >= 36 && rf->channel <= 64) {
+ rfcsr = 0x36;
+ if (txbf_enabled)
+ rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+ rt2800_rfcsr_write(rt2x00dev, 39, 0x36);
+
+ rt2800_rfcsr_write(rt2x00dev, 45, 0xeb);
+ } else if (rf->channel >= 100 && rf->channel <= 128) {
+ rfcsr = 0x32;
+ if (txbf_enabled)
+ rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+ rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+ rt2800_rfcsr_write(rt2x00dev, 45, 0xb3);
+ } else {
+ rfcsr = 0x30;
+ if (txbf_enabled)
+ rt2x00_set_field8(&rfcsr, RFCSR39_RX_DIV, 1);
+ rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+ rt2800_rfcsr_write(rt2x00dev, 45, 0x9b);
+ }
}
+#define POWER_BOUND 0x27
+#define POWER_BOUND_5G 0x2b
+
static void rt2800_config_channel_rf3290(struct rt2x00_dev *rt2x00dev,
struct ieee80211_conf *conf,
struct rf_channel *rf,
rt2800_bbp_write(rt2x00dev, 159, cal != 0xff ? cal : 0);
}
+static char rt2800_txpower_to_dev(struct rt2x00_dev *rt2x00dev,
+ unsigned int channel,
+ char txpower)
+{
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ txpower = rt2x00_get_field8(txpower, EEPROM_TXPOWER_ALC);
+
+ if (channel <= 14)
+ return clamp_t(char, txpower, MIN_G_TXPOWER, MAX_G_TXPOWER);
+
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ return clamp_t(char, txpower, MIN_A_TXPOWER_3593,
+ MAX_A_TXPOWER_3593);
+ else
+ return clamp_t(char, txpower, MIN_A_TXPOWER, MAX_A_TXPOWER);
+}
+
static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
struct ieee80211_conf *conf,
struct rf_channel *rf,
unsigned int tx_pin;
u8 bbp, rfcsr;
- if (rf->channel <= 14) {
- info->default_power1 = TXPOWER_G_TO_DEV(info->default_power1);
- info->default_power2 = TXPOWER_G_TO_DEV(info->default_power2);
- } else {
- info->default_power1 = TXPOWER_A_TO_DEV(info->default_power1);
- info->default_power2 = TXPOWER_A_TO_DEV(info->default_power2);
- }
+ info->default_power1 = rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+ info->default_power1);
+ info->default_power2 = rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+ info->default_power2);
+ if (rt2x00dev->default_ant.tx_chain_num > 2)
+ info->default_power3 =
+ rt2800_txpower_to_dev(rt2x00dev, rf->channel,
+ info->default_power3);
switch (rt2x00dev->chip.rf) {
case RF2020:
case RF3052:
rt2800_config_channel_rf3052(rt2x00dev, conf, rf, info);
break;
+ case RF3053:
+ rt2800_config_channel_rf3053(rt2x00dev, conf, rf, info);
+ break;
case RF3290:
rt2800_config_channel_rf3290(rt2x00dev, conf, rf, info);
break;
rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain);
rt2800_bbp_write(rt2x00dev, 27, 0x20);
rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain);
+ } else if (rt2x00_rt(rt2x00dev, RT3593)) {
+ if (rf->channel > 14) {
+ /* Disable CCK Packet detection on 5GHz */
+ rt2800_bbp_write(rt2x00dev, 70, 0x00);
+ } else {
+ rt2800_bbp_write(rt2x00dev, 70, 0x0a);
+ }
+
+ if (conf_is_ht40(conf))
+ rt2800_bbp_write(rt2x00dev, 105, 0x04);
+ else
+ rt2800_bbp_write(rt2x00dev, 105, 0x34);
+
+ rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
+ rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
+ rt2800_bbp_write(rt2x00dev, 64, 0x37 - rt2x00dev->lna_gain);
+ rt2800_bbp_write(rt2x00dev, 77, 0x98);
} else {
rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain);
rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain);
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 75, 0x46);
} else {
- rt2800_bbp_write(rt2x00dev, 82, 0x84);
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ rt2800_bbp_write(rt2x00dev, 82, 0x62);
+ else
+ rt2800_bbp_write(rt2x00dev, 82, 0x84);
rt2800_bbp_write(rt2x00dev, 75, 0x50);
}
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ rt2800_bbp_write(rt2x00dev, 83, 0x8a);
}
+
} else {
if (rt2x00_rt(rt2x00dev, RT3572))
rt2800_bbp_write(rt2x00dev, 82, 0x94);
+ else if (rt2x00_rt(rt2x00dev, RT3593))
+ rt2800_bbp_write(rt2x00dev, 82, 0x82);
else
rt2800_bbp_write(rt2x00dev, 82, 0xf2);
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ rt2800_bbp_write(rt2x00dev, 83, 0x9a);
+
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
rt2800_bbp_write(rt2x00dev, 75, 0x46);
else
if (rt2x00_rt(rt2x00dev, RT3572))
rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
+ if (rt2x00_rt(rt2x00dev, RT3593)) {
+ if (rt2x00_is_usb(rt2x00dev)) {
+ rt2800_register_read(rt2x00dev, GPIO_CTRL, ®);
+
+ /* Band selection. 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);
+
+ 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);
+ }
+
+ /* 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);
+
+ usleep_range(1000, 1500);
+ }
+
if (rt2x00_rt(rt2x00dev, RT5592)) {
rt2800_bbp_write(rt2x00dev, 195, 141);
rt2800_bbp_write(rt2x00dev, 196, conf_is_ht40(conf) ? 0x10 : 0x1a);
u8 step;
int i;
+ /*
+ * First check if temperature compensation is supported.
+ */
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ if (!rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC))
+ return 0;
+
/*
* Read TSSI boundaries for temperature compensation from
* the EEPROM.
* Example TSSI bounds 0xF0 0xD0 0xB5 0xA0 0x88 0x45 0x25 0x15 0x00
*/
if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG1, &eeprom);
tssi_bounds[0] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG1_MINUS4);
tssi_bounds[1] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG1_MINUS3);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG2, &eeprom);
tssi_bounds[2] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG2_MINUS2);
tssi_bounds[3] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG2_MINUS1);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG3, &eeprom);
tssi_bounds[4] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG3_REF);
tssi_bounds[5] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG3_PLUS1);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG4, &eeprom);
tssi_bounds[6] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG4_PLUS2);
tssi_bounds[7] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG4_PLUS3);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_BG5, &eeprom);
tssi_bounds[8] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG5_PLUS4);
step = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_BG5_AGC_STEP);
} else {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A1, &eeprom);
tssi_bounds[0] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A1_MINUS4);
tssi_bounds[1] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A1_MINUS3);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A2, &eeprom);
tssi_bounds[2] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A2_MINUS2);
tssi_bounds[3] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A2_MINUS1);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A3, &eeprom);
tssi_bounds[4] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A3_REF);
tssi_bounds[5] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A3_PLUS1);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A4, &eeprom);
tssi_bounds[6] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A4_PLUS2);
tssi_bounds[7] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A4_PLUS3);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TSSI_BOUND_A5, &eeprom);
tssi_bounds[8] = rt2x00_get_field16(eeprom,
EEPROM_TSSI_BOUND_A5_PLUS4);
u8 comp_type;
int comp_value = 0;
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TXPOWER_DELTA, &eeprom);
/*
* HT40 compensation not required.
u8 eirp_txpower_criterion;
u8 reg_limit;
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ return min_t(u8, txpower, 0xc);
+
if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
/*
* Check if eirp txpower exceed txpower_limit.
* .11b data rate need add additional 4dbm
* when calculating eirp txpower.
*/
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + 1,
- &eeprom);
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ 1, &eeprom);
criterion = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE0);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER,
+ rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER,
&eeprom);
if (band == IEEE80211_BAND_2GHZ)
return min_t(u8, txpower, 0xc);
}
+
+enum {
+ TX_PWR_CFG_0_IDX,
+ TX_PWR_CFG_1_IDX,
+ TX_PWR_CFG_2_IDX,
+ TX_PWR_CFG_3_IDX,
+ TX_PWR_CFG_4_IDX,
+ TX_PWR_CFG_5_IDX,
+ TX_PWR_CFG_6_IDX,
+ TX_PWR_CFG_7_IDX,
+ TX_PWR_CFG_8_IDX,
+ TX_PWR_CFG_9_IDX,
+ TX_PWR_CFG_0_EXT_IDX,
+ TX_PWR_CFG_1_EXT_IDX,
+ TX_PWR_CFG_2_EXT_IDX,
+ TX_PWR_CFG_3_EXT_IDX,
+ TX_PWR_CFG_4_EXT_IDX,
+ TX_PWR_CFG_IDX_COUNT,
+};
+
+static void rt2800_config_txpower_rt3593(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_channel *chan,
+ int power_level)
+{
+ u8 txpower;
+ u16 eeprom;
+ u32 regs[TX_PWR_CFG_IDX_COUNT];
+ unsigned int offset;
+ enum ieee80211_band band = chan->band;
+ int delta;
+ int i;
+
+ memset(regs, '\0', sizeof(regs));
+
+ /* TODO: adapt TX power reduction from the rt28xx code */
+
+ /* calculate temperature compensation delta */
+ delta = rt2800_get_gain_calibration_delta(rt2x00dev);
+
+ if (band == IEEE80211_BAND_5GHZ)
+ offset = 16;
+ else
+ offset = 0;
+
+ if (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags))
+ offset += 8;
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset, &eeprom);
+
+ /* CCK 1MBS,2MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_CCK1_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_CCK1_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX],
+ TX_PWR_CFG_0_EXT_CCK1_CH2, txpower);
+
+ /* CCK 5.5MBS,11MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 1, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_CCK5_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_CCK5_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX],
+ TX_PWR_CFG_0_EXT_CCK5_CH2, txpower);
+
+ /* OFDM 6MBS,9MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_OFDM6_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_OFDM6_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX],
+ TX_PWR_CFG_0_EXT_OFDM6_CH2, txpower);
+
+ /* OFDM 12MBS,18MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_OFDM12_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_IDX],
+ TX_PWR_CFG_0_OFDM12_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_0_EXT_IDX],
+ TX_PWR_CFG_0_EXT_OFDM12_CH2, txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 1, &eeprom);
+
+ /* OFDM 24MBS,36MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_OFDM24_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_OFDM24_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX],
+ TX_PWR_CFG_1_EXT_OFDM24_CH2, txpower);
+
+ /* OFDM 48MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_OFDM48_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_OFDM48_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX],
+ TX_PWR_CFG_1_EXT_OFDM48_CH2, txpower);
+
+ /* OFDM 54MBS */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX],
+ TX_PWR_CFG_7_OFDM54_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX],
+ TX_PWR_CFG_7_OFDM54_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX],
+ TX_PWR_CFG_7_OFDM54_CH2, txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 2, &eeprom);
+
+ /* MCS 0,1 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_MCS0_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_MCS0_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX],
+ TX_PWR_CFG_1_EXT_MCS0_CH2, txpower);
+
+ /* MCS 2,3 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_MCS2_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_IDX],
+ TX_PWR_CFG_1_MCS2_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_1_EXT_IDX],
+ TX_PWR_CFG_1_EXT_MCS2_CH2, txpower);
+
+ /* MCS 4,5 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS4_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS4_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX],
+ TX_PWR_CFG_2_EXT_MCS4_CH2, txpower);
+
+ /* MCS 6 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS6_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS6_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX],
+ TX_PWR_CFG_2_EXT_MCS6_CH2, txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 3, &eeprom);
+
+ /* MCS 7 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX],
+ TX_PWR_CFG_7_MCS7_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX],
+ TX_PWR_CFG_7_MCS7_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_7_IDX],
+ TX_PWR_CFG_7_MCS7_CH2, txpower);
+
+ /* MCS 8,9 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS8_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS8_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX],
+ TX_PWR_CFG_2_EXT_MCS8_CH2, txpower);
+
+ /* MCS 10,11 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS10_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_IDX],
+ TX_PWR_CFG_2_MCS10_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_2_EXT_IDX],
+ TX_PWR_CFG_2_EXT_MCS10_CH2, txpower);
+
+ /* MCS 12,13 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_MCS12_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_MCS12_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX],
+ TX_PWR_CFG_3_EXT_MCS12_CH2, txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 4, &eeprom);
+
+ /* MCS 14 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_MCS14_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_MCS14_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX],
+ TX_PWR_CFG_3_EXT_MCS14_CH2, txpower);
+
+ /* MCS 15 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX],
+ TX_PWR_CFG_8_MCS15_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX],
+ TX_PWR_CFG_8_MCS15_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX],
+ TX_PWR_CFG_8_MCS15_CH2, txpower);
+
+ /* MCS 16,17 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX],
+ TX_PWR_CFG_5_MCS16_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX],
+ TX_PWR_CFG_5_MCS16_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX],
+ TX_PWR_CFG_5_MCS16_CH2, txpower);
+
+ /* MCS 18,19 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX],
+ TX_PWR_CFG_5_MCS18_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX],
+ TX_PWR_CFG_5_MCS18_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_5_IDX],
+ TX_PWR_CFG_5_MCS18_CH2, txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 5, &eeprom);
+
+ /* MCS 20,21 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX],
+ TX_PWR_CFG_6_MCS20_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX],
+ TX_PWR_CFG_6_MCS20_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX],
+ TX_PWR_CFG_6_MCS20_CH2, txpower);
+
+ /* MCS 22 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX],
+ TX_PWR_CFG_6_MCS22_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX],
+ TX_PWR_CFG_6_MCS22_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_6_IDX],
+ TX_PWR_CFG_6_MCS22_CH2, txpower);
+
+ /* MCS 23 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX],
+ TX_PWR_CFG_8_MCS23_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX],
+ TX_PWR_CFG_8_MCS23_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_8_IDX],
+ TX_PWR_CFG_8_MCS23_CH2, txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 6, &eeprom);
+
+ /* STBC, MCS 0,1 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_STBC0_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_STBC0_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX],
+ TX_PWR_CFG_3_EXT_STBC0_CH2, txpower);
+
+ /* STBC, MCS 2,3 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE1);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_STBC2_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_IDX],
+ TX_PWR_CFG_3_STBC2_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_3_EXT_IDX],
+ TX_PWR_CFG_3_EXT_STBC2_CH2, txpower);
+
+ /* STBC, MCS 4,5 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE2);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE0,
+ txpower);
+
+ /* STBC, MCS 6 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE3);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE2, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_4_IDX], TX_PWR_CFG_RATE3, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_4_EXT_IDX], TX_PWR_CFG_RATE2,
+ txpower);
+
+ /* read the next four txpower values */
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ offset + 7, &eeprom);
+
+ /* STBC, MCS 7 */
+ txpower = rt2x00_get_field16(eeprom, EEPROM_TXPOWER_BYRATE_RATE0);
+ txpower = rt2800_compensate_txpower(rt2x00dev, 0, band, power_level,
+ txpower, delta);
+ rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX],
+ TX_PWR_CFG_9_STBC7_CH0, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX],
+ TX_PWR_CFG_9_STBC7_CH1, txpower);
+ rt2x00_set_field32(®s[TX_PWR_CFG_9_IDX],
+ TX_PWR_CFG_9_STBC7_CH2, txpower);
+
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, regs[TX_PWR_CFG_0_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, regs[TX_PWR_CFG_1_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, regs[TX_PWR_CFG_2_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, regs[TX_PWR_CFG_3_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, regs[TX_PWR_CFG_4_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_5, regs[TX_PWR_CFG_5_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_6, regs[TX_PWR_CFG_6_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_7, regs[TX_PWR_CFG_7_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_8, regs[TX_PWR_CFG_8_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_9, regs[TX_PWR_CFG_9_IDX]);
+
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_0_EXT,
+ regs[TX_PWR_CFG_0_EXT_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_1_EXT,
+ regs[TX_PWR_CFG_1_EXT_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_2_EXT,
+ regs[TX_PWR_CFG_2_EXT_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_3_EXT,
+ regs[TX_PWR_CFG_3_EXT_IDX]);
+ rt2800_register_write(rt2x00dev, TX_PWR_CFG_4_EXT,
+ regs[TX_PWR_CFG_4_EXT_IDX]);
+
+ for (i = 0; i < TX_PWR_CFG_IDX_COUNT; i++)
+ rt2x00_dbg(rt2x00dev,
+ "band:%cGHz, BW:%c0MHz, TX_PWR_CFG_%d%s = %08lx\n",
+ (band == IEEE80211_BAND_5GHZ) ? '5' : '2',
+ (test_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags)) ?
+ '4' : '2',
+ (i > TX_PWR_CFG_9_IDX) ?
+ (i - TX_PWR_CFG_9_IDX - 1) : i,
+ (i > TX_PWR_CFG_9_IDX) ? "_EXT" : "",
+ (unsigned long) regs[i]);
+}
+
/*
* We configure transmit power using MAC TX_PWR_CFG_{0,...,N} registers and
* BBP R1 register. TX_PWR_CFG_X allow to configure per rate TX power values,
* EEPROM_TXPOWER_BYRATE offset. We adjust them and BBP R1 settings according to
* current conditions (i.e. band, bandwidth, temperature, user settings).
*/
-static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_channel *chan,
- int power_level)
+static void rt2800_config_txpower_rt28xx(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_channel *chan,
+ int power_level)
{
u8 txpower, r1;
u16 eeprom;
rt2800_register_read(rt2x00dev, offset, ®);
/* read the next four txpower values */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i,
- &eeprom);
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ i, &eeprom);
is_rate_b = i ? 0 : 1;
/*
rt2x00_set_field32(®, TX_PWR_CFG_RATE3, txpower);
/* read the next four txpower values */
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i + 1,
- &eeprom);
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_TXPOWER_BYRATE,
+ i + 1, &eeprom);
is_rate_b = 0;
/*
}
}
+static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_channel *chan,
+ int power_level)
+{
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ rt2800_config_txpower_rt3593(rt2x00dev, chan, power_level);
+ else
+ rt2800_config_txpower_rt28xx(rt2x00dev, chan, power_level);
+}
+
void rt2800_gain_calibration(struct rt2x00_dev *rt2x00dev)
{
rt2800_config_txpower(rt2x00dev, rt2x00dev->hw->conf.chandef.chan,
rt2x00_set_field8(&rfcsr, RFCSR7_RF_TUNING, 1);
rt2800_rfcsr_write(rt2x00dev, 7, rfcsr);
break;
+ case RF3053:
case RF3290:
case RF5360:
case RF5370:
return ret;
rt2800_register_read(rt2x00dev, BCN_OFFSET0, ®);
- rt2x00_set_field32(®, BCN_OFFSET0_BCN0, 0xe0); /* 0x3800 */
- rt2x00_set_field32(®, BCN_OFFSET0_BCN1, 0xe8); /* 0x3a00 */
- rt2x00_set_field32(®, BCN_OFFSET0_BCN2, 0xf0); /* 0x3c00 */
- rt2x00_set_field32(®, BCN_OFFSET0_BCN3, 0xf8); /* 0x3e00 */
+ rt2x00_set_field32(®, BCN_OFFSET0_BCN0,
+ rt2800_get_beacon_offset(rt2x00dev, 0));
+ rt2x00_set_field32(®, BCN_OFFSET0_BCN1,
+ rt2800_get_beacon_offset(rt2x00dev, 1));
+ rt2x00_set_field32(®, BCN_OFFSET0_BCN2,
+ rt2800_get_beacon_offset(rt2x00dev, 2));
+ rt2x00_set_field32(®, BCN_OFFSET0_BCN3,
+ rt2800_get_beacon_offset(rt2x00dev, 3));
rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
rt2800_register_read(rt2x00dev, BCN_OFFSET1, ®);
- rt2x00_set_field32(®, BCN_OFFSET1_BCN4, 0xc8); /* 0x3200 */
- rt2x00_set_field32(®, BCN_OFFSET1_BCN5, 0xd0); /* 0x3400 */
- rt2x00_set_field32(®, BCN_OFFSET1_BCN6, 0x77); /* 0x1dc0 */
- rt2x00_set_field32(®, BCN_OFFSET1_BCN7, 0x6f); /* 0x1bc0 */
+ rt2x00_set_field32(®, BCN_OFFSET1_BCN4,
+ rt2800_get_beacon_offset(rt2x00dev, 4));
+ rt2x00_set_field32(®, BCN_OFFSET1_BCN5,
+ rt2800_get_beacon_offset(rt2x00dev, 5));
+ rt2x00_set_field32(®, BCN_OFFSET1_BCN6,
+ rt2800_get_beacon_offset(rt2x00dev, 6));
+ rt2x00_set_field32(®, BCN_OFFSET1_BCN7,
+ rt2800_get_beacon_offset(rt2x00dev, 7));
rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+ &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST))
rt2800_register_write(rt2x00dev, TX_SW_CFG2,
0x0000002c);
} else if (rt2x00_rt(rt2x00dev, RT3572)) {
rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000400);
rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00080606);
+ } else if (rt2x00_rt(rt2x00dev, RT3593)) {
+ rt2800_register_write(rt2x00dev, TX_SW_CFG0, 0x00000402);
+ rt2800_register_write(rt2x00dev, TX_SW_CFG1, 0x00000000);
+ if (rt2x00_rt_rev_lt(rt2x00dev, RT3593, REV_RT3593E)) {
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+ &eeprom);
+ if (rt2x00_get_field16(eeprom,
+ EEPROM_NIC_CONF1_DAC_TEST))
+ rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+ 0x0000001f);
+ else
+ rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+ 0x0000000f);
+ } else {
+ rt2800_register_write(rt2x00dev, TX_SW_CFG2,
+ 0x00000000);
+ }
} else if (rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392) ||
rt2x00_rt(rt2x00dev, RT5592)) {
/*
* Clear all beacons
*/
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE0);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE1);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE2);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE3);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE4);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE5);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE6);
- rt2800_clear_beacon_register(rt2x00dev, HW_BEACON_BASE7);
+ for (i = 0; i < 8; i++)
+ rt2800_clear_beacon_register(rt2x00dev, i);
if (rt2x00_is_usb(rt2x00dev)) {
rt2800_register_read(rt2x00dev, US_CYC_CNT, ®);
u8 value;
rt2800_bbp_read(rt2x00dev, 138, &value);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
value |= 0x20;
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
rt2800_disable_unused_dac_adc(rt2x00dev);
}
+static void rt2800_init_bbp_3593(struct rt2x00_dev *rt2x00dev)
+{
+ rt2800_init_bbp_early(rt2x00dev);
+
+ rt2800_bbp_write(rt2x00dev, 79, 0x13);
+ rt2800_bbp_write(rt2x00dev, 80, 0x05);
+ rt2800_bbp_write(rt2x00dev, 81, 0x33);
+ rt2800_bbp_write(rt2x00dev, 137, 0x0f);
+
+ rt2800_bbp_write(rt2x00dev, 84, 0x19);
+
+ /* Enable DC filter */
+ if (rt2x00_rt_rev_gte(rt2x00dev, RT3593, REV_RT3593E))
+ rt2800_bbp_write(rt2x00dev, 103, 0xc0);
+}
+
static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
{
int ant, div_mode;
rt2800_disable_unused_dac_adc(rt2x00dev);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
div_mode = rt2x00_get_field16(eeprom,
EEPROM_NIC_CONF1_ANT_DIVERSITY);
ant = (div_mode == 3) ? 1 : 0;
rt2800_bbp4_mac_if_ctrl(rt2x00dev);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
div_mode = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_ANT_DIVERSITY);
ant = (div_mode == 3) ? 1 : 0;
rt2800_bbp_read(rt2x00dev, 152, &value);
case RT3572:
rt2800_init_bbp_3572(rt2x00dev);
break;
+ case RT3593:
+ rt2800_init_bbp_3593(rt2x00dev);
+ return;
case RT5390:
case RT5392:
rt2800_init_bbp_53xx(rt2x00dev);
}
for (i = 0; i < EEPROM_BBP_SIZE; i++) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_BBP_START + i, &eeprom);
+ rt2800_eeprom_read_from_array(rt2x00dev, EEPROM_BBP_START, i,
+ &eeprom);
if (eeprom != 0xffff && eeprom != 0x0000) {
reg_id = rt2x00_get_field16(eeprom, EEPROM_BBP_REG_ID);
if (rt2x00_rt(rt2x00dev, RT3090)) {
/* Turn off unused DAC1 and ADC1 to reduce power consumption */
rt2800_bbp_read(rt2x00dev, 138, &bbp);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
rt2x00_set_field8(&bbp, BBP138_RX_ADC1, 0);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
}
}
+static void rt2800_normal_mode_setup_3593(struct rt2x00_dev *rt2x00dev)
+{
+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+ u8 rfcsr;
+ u8 tx_gain;
+
+ rt2800_rfcsr_read(rt2x00dev, 50, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR50_TX_LO2_EN, 0);
+ rt2800_rfcsr_write(rt2x00dev, 50, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 51, &rfcsr);
+ tx_gain = rt2x00_get_field8(drv_data->txmixer_gain_24g,
+ RFCSR17_TXMIXER_GAIN);
+ rt2x00_set_field8(&rfcsr, RFCSR51_BITS24, tx_gain);
+ rt2800_rfcsr_write(rt2x00dev, 51, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 38, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR38_RX_LO1_EN, 0);
+ rt2800_rfcsr_write(rt2x00dev, 38, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 39, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR39_RX_LO2_EN, 0);
+ rt2800_rfcsr_write(rt2x00dev, 39, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 1, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR1_RF_BLOCK_EN, 1);
+ rt2x00_set_field8(&rfcsr, RFCSR1_PLL_PD, 1);
+ rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
+
+ rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR30_RX_VCM, 2);
+ rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
+
+ /* TODO: enable stream mode */
+}
+
static void rt2800_normal_mode_setup_5xxx(struct rt2x00_dev *rt2x00dev)
{
u8 reg;
/* Turn off unused DAC1 and ADC1 to reduce power consumption */
rt2800_bbp_read(rt2x00dev, 138, ®);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) == 1)
rt2x00_set_field8(®, BBP138_RX_ADC1, 0);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) == 1)
rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1);
if (rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E)) {
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1,
+ &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF1_DAC_TEST))
rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3);
else
rt2800_normal_mode_setup_3xxx(rt2x00dev);
}
+static void rt3593_post_bbp_init(struct rt2x00_dev *rt2x00dev)
+{
+ u8 bbp;
+ bool txbf_enabled = false; /* FIXME */
+
+ rt2800_bbp_read(rt2x00dev, 105, &bbp);
+ if (rt2x00dev->default_ant.rx_chain_num == 1)
+ rt2x00_set_field8(&bbp, BBP105_MLD, 0);
+ else
+ rt2x00_set_field8(&bbp, BBP105_MLD, 1);
+ rt2800_bbp_write(rt2x00dev, 105, bbp);
+
+ rt2800_bbp4_mac_if_ctrl(rt2x00dev);
+
+ rt2800_bbp_write(rt2x00dev, 92, 0x02);
+ rt2800_bbp_write(rt2x00dev, 82, 0x82);
+ rt2800_bbp_write(rt2x00dev, 106, 0x05);
+ rt2800_bbp_write(rt2x00dev, 104, 0x92);
+ rt2800_bbp_write(rt2x00dev, 88, 0x90);
+ rt2800_bbp_write(rt2x00dev, 148, 0xc8);
+ rt2800_bbp_write(rt2x00dev, 47, 0x48);
+ rt2800_bbp_write(rt2x00dev, 120, 0x50);
+
+ if (txbf_enabled)
+ rt2800_bbp_write(rt2x00dev, 163, 0xbd);
+ else
+ rt2800_bbp_write(rt2x00dev, 163, 0x9d);
+
+ /* SNR mapping */
+ rt2800_bbp_write(rt2x00dev, 142, 6);
+ rt2800_bbp_write(rt2x00dev, 143, 160);
+ rt2800_bbp_write(rt2x00dev, 142, 7);
+ rt2800_bbp_write(rt2x00dev, 143, 161);
+ rt2800_bbp_write(rt2x00dev, 142, 8);
+ rt2800_bbp_write(rt2x00dev, 143, 162);
+
+ /* ADC/DAC control */
+ rt2800_bbp_write(rt2x00dev, 31, 0x08);
+
+ /* RX AGC energy lower bound in log2 */
+ rt2800_bbp_write(rt2x00dev, 68, 0x0b);
+
+ /* FIXME: BBP 105 owerwrite? */
+ rt2800_bbp_write(rt2x00dev, 105, 0x04);
+
+}
+
+static void rt2800_init_rfcsr_3593(struct rt2x00_dev *rt2x00dev)
+{
+ struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
+ u32 reg;
+ u8 rfcsr;
+
+ /* Disable GPIO #4 and #7 function for LAN PE control */
+ rt2800_register_read(rt2x00dev, GPIO_SWITCH, ®);
+ rt2x00_set_field32(®, GPIO_SWITCH_4, 0);
+ rt2x00_set_field32(®, GPIO_SWITCH_7, 0);
+ rt2800_register_write(rt2x00dev, GPIO_SWITCH, reg);
+
+ /* Initialize default register values */
+ rt2800_rfcsr_write(rt2x00dev, 1, 0x03);
+ rt2800_rfcsr_write(rt2x00dev, 3, 0x80);
+ rt2800_rfcsr_write(rt2x00dev, 5, 0x00);
+ rt2800_rfcsr_write(rt2x00dev, 6, 0x40);
+ rt2800_rfcsr_write(rt2x00dev, 8, 0xf1);
+ rt2800_rfcsr_write(rt2x00dev, 9, 0x02);
+ rt2800_rfcsr_write(rt2x00dev, 10, 0xd3);
+ rt2800_rfcsr_write(rt2x00dev, 11, 0x40);
+ rt2800_rfcsr_write(rt2x00dev, 12, 0x4e);
+ rt2800_rfcsr_write(rt2x00dev, 13, 0x12);
+ rt2800_rfcsr_write(rt2x00dev, 18, 0x40);
+ rt2800_rfcsr_write(rt2x00dev, 22, 0x20);
+ rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
+ rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
+ rt2800_rfcsr_write(rt2x00dev, 32, 0x78);
+ rt2800_rfcsr_write(rt2x00dev, 33, 0x3b);
+ rt2800_rfcsr_write(rt2x00dev, 34, 0x3c);
+ rt2800_rfcsr_write(rt2x00dev, 35, 0xe0);
+ rt2800_rfcsr_write(rt2x00dev, 38, 0x86);
+ rt2800_rfcsr_write(rt2x00dev, 39, 0x23);
+ rt2800_rfcsr_write(rt2x00dev, 44, 0xd3);
+ rt2800_rfcsr_write(rt2x00dev, 45, 0xbb);
+ rt2800_rfcsr_write(rt2x00dev, 46, 0x60);
+ rt2800_rfcsr_write(rt2x00dev, 49, 0x8e);
+ rt2800_rfcsr_write(rt2x00dev, 50, 0x86);
+ rt2800_rfcsr_write(rt2x00dev, 51, 0x75);
+ rt2800_rfcsr_write(rt2x00dev, 52, 0x45);
+ rt2800_rfcsr_write(rt2x00dev, 53, 0x18);
+ rt2800_rfcsr_write(rt2x00dev, 54, 0x18);
+ rt2800_rfcsr_write(rt2x00dev, 55, 0x18);
+ rt2800_rfcsr_write(rt2x00dev, 56, 0xdb);
+ rt2800_rfcsr_write(rt2x00dev, 57, 0x6e);
+
+ /* Initiate calibration */
+ /* TODO: use rt2800_rf_init_calibration ? */
+ rt2800_rfcsr_read(rt2x00dev, 2, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR2_RESCAL_EN, 1);
+ rt2800_rfcsr_write(rt2x00dev, 2, rfcsr);
+
+ rt2800_adjust_freq_offset(rt2x00dev);
+
+ rt2800_rfcsr_read(rt2x00dev, 18, &rfcsr);
+ rt2x00_set_field8(&rfcsr, RFCSR18_XO_TUNE_BYPASS, 1);
+ rt2800_rfcsr_write(rt2x00dev, 18, rfcsr);
+
+ rt2800_register_read(rt2x00dev, LDO_CFG0, ®);
+ rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 3);
+ rt2x00_set_field32(®, LDO_CFG0_BGSEL, 1);
+ rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+ usleep_range(1000, 1500);
+ rt2800_register_read(rt2x00dev, LDO_CFG0, ®);
+ rt2x00_set_field32(®, LDO_CFG0_LDO_CORE_VLEVEL, 0);
+ rt2800_register_write(rt2x00dev, LDO_CFG0, reg);
+
+ /* Set initial values for RX filter calibration */
+ drv_data->calibration_bw20 = 0x1f;
+ drv_data->calibration_bw40 = 0x2f;
+
+ /* Save BBP 25 & 26 values for later use in channel switching */
+ rt2800_bbp_read(rt2x00dev, 25, &drv_data->bbp25);
+ rt2800_bbp_read(rt2x00dev, 26, &drv_data->bbp26);
+
+ rt2800_led_open_drain_enable(rt2x00dev);
+ rt2800_normal_mode_setup_3593(rt2x00dev);
+
+ rt3593_post_bbp_init(rt2x00dev);
+
+ /* TODO: enable stream mode support */
+}
+
static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
{
rt2800_rf_init_calibration(rt2x00dev, 2);
case RT3572:
rt2800_init_rfcsr_3572(rt2x00dev);
break;
+ case RT3593:
+ rt2800_init_rfcsr_3593(rt2x00dev);
+ break;
case RT5390:
rt2800_init_rfcsr_5390(rt2x00dev);
break;
/*
* Initialize LED control
*/
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_LED_AG_CONF, &word);
rt2800_mcu_request(rt2x00dev, MCU_LED_AG_CONF, 0xff,
word & 0xff, (word >> 8) & 0xff);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_LED_ACT_CONF, &word);
rt2800_mcu_request(rt2x00dev, MCU_LED_ACT_CONF, 0xff,
word & 0xff, (word >> 8) & 0xff);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_LED_POLARITY, &word);
rt2800_mcu_request(rt2x00dev, MCU_LED_LED_POLARITY, 0xff,
word & 0xff, (word >> 8) & 0xff);
}
EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
+static u8 rt2800_get_txmixer_gain_24g(struct rt2x00_dev *rt2x00dev)
+{
+ u16 word;
+
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ return 0;
+
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word);
+ if ((word & 0x00ff) != 0x00ff)
+ return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL);
+
+ return 0;
+}
+
+static u8 rt2800_get_txmixer_gain_5g(struct rt2x00_dev *rt2x00dev)
+{
+ u16 word;
+
+ if (rt2x00_rt(rt2x00dev, RT3593))
+ return 0;
+
+ rt2800_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word);
+ if ((word & 0x00ff) != 0x00ff)
+ return rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL);
+
+ return 0;
+}
+
static int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev)
{
struct rt2800_drv_data *drv_data = rt2x00dev->drv_data;
/*
* Start validation of the data that has been read.
*/
- mac = rt2x00_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
+ mac = rt2800_eeprom_addr(rt2x00dev, EEPROM_MAC_ADDR_0);
if (!is_valid_ether_addr(mac)) {
eth_random_addr(mac);
rt2x00_eeprom_dbg(rt2x00dev, "MAC: %pM\n", mac);
}
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &word);
if (word == 0xffff) {
rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2);
rt2x00_set_field16(&word, EEPROM_NIC_CONF0_TXPATH, 1);
rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RF_TYPE, RF2820);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
rt2x00_eeprom_dbg(rt2x00dev, "Antenna: 0x%04x\n", word);
} else if (rt2x00_rt(rt2x00dev, RT2860) ||
rt2x00_rt(rt2x00dev, RT2872)) {
*/
if (rt2x00_get_field16(word, EEPROM_NIC_CONF0_RXPATH) > 2)
rt2x00_set_field16(&word, EEPROM_NIC_CONF0_RXPATH, 2);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF0, word);
}
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &word);
if (word == 0xffff) {
rt2x00_set_field16(&word, EEPROM_NIC_CONF1_HW_RADIO, 0);
rt2x00_set_field16(&word, EEPROM_NIC_CONF1_EXTERNAL_TX_ALC, 0);
rt2x00_set_field16(&word, EEPROM_NIC_CONF1_INTERNAL_TX_ALC, 0);
rt2x00_set_field16(&word, EEPROM_NIC_CONF1_BT_COEXIST, 0);
rt2x00_set_field16(&word, EEPROM_NIC_CONF1_DAC_TEST, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_NIC_CONF1, word);
rt2x00_eeprom_dbg(rt2x00dev, "NIC: 0x%04x\n", word);
}
- rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &word);
if ((word & 0x00ff) == 0x00ff) {
rt2x00_set_field16(&word, EEPROM_FREQ_OFFSET, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
rt2x00_eeprom_dbg(rt2x00dev, "Freq: 0x%04x\n", word);
}
if ((word & 0xff00) == 0xff00) {
rt2x00_set_field16(&word, EEPROM_FREQ_LED_MODE,
LED_MODE_TXRX_ACTIVITY);
rt2x00_set_field16(&word, EEPROM_FREQ_LED_POLARITY, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_FREQ, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_LED_AG_CONF, 0x5555);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_LED_ACT_CONF, 0x2221);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_LED_POLARITY, 0xa9f8);
rt2x00_eeprom_dbg(rt2x00dev, "Led Mode: 0x%04x\n", word);
}
* lna0 as correct value. Note that EEPROM_LNA
* is never validated.
*/
- rt2x00_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_LNA, &word);
default_lna_gain = rt2x00_get_field16(word, EEPROM_LNA_A0);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &word);
if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET0)) > 10)
rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET0, 0);
if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG_OFFSET1)) > 10)
rt2x00_set_field16(&word, EEPROM_RSSI_BG_OFFSET1, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG, word);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_BG, &word);
- if ((word & 0x00ff) != 0x00ff) {
- drv_data->txmixer_gain_24g =
- rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_BG_VAL);
- } else {
- drv_data->txmixer_gain_24g = 0;
- }
+ drv_data->txmixer_gain_24g = rt2800_get_txmixer_gain_24g(rt2x00dev);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &word);
if (abs(rt2x00_get_field16(word, EEPROM_RSSI_BG2_OFFSET2)) > 10)
rt2x00_set_field16(&word, EEPROM_RSSI_BG2_OFFSET2, 0);
- if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
- rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
- rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
- default_lna_gain);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
-
- rt2x00_eeprom_read(rt2x00dev, EEPROM_TXMIXER_GAIN_A, &word);
- if ((word & 0x00ff) != 0x00ff) {
- drv_data->txmixer_gain_5g =
- rt2x00_get_field16(word, EEPROM_TXMIXER_GAIN_A_VAL);
- } else {
- drv_data->txmixer_gain_5g = 0;
+ if (!rt2x00_rt(rt2x00dev, RT3593)) {
+ if (rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0x00 ||
+ rt2x00_get_field16(word, EEPROM_RSSI_BG2_LNA_A1) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_RSSI_BG2_LNA_A1,
+ default_lna_gain);
}
+ rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_BG2, word);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
+ drv_data->txmixer_gain_5g = rt2800_get_txmixer_gain_5g(rt2x00dev);
+
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &word);
if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET0)) > 10)
rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET0, 0);
if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A_OFFSET1)) > 10)
rt2x00_set_field16(&word, EEPROM_RSSI_A_OFFSET1, 0);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A, word);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &word);
if (abs(rt2x00_get_field16(word, EEPROM_RSSI_A2_OFFSET2)) > 10)
rt2x00_set_field16(&word, EEPROM_RSSI_A2_OFFSET2, 0);
- if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
- rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
- rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
- default_lna_gain);
- rt2x00_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+ if (!rt2x00_rt(rt2x00dev, RT3593)) {
+ if (rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0x00 ||
+ rt2x00_get_field16(word, EEPROM_RSSI_A2_LNA_A2) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_RSSI_A2_LNA_A2,
+ default_lna_gain);
+ }
+ rt2800_eeprom_write(rt2x00dev, EEPROM_RSSI_A2, word);
+
+ if (rt2x00_rt(rt2x00dev, RT3593)) {
+ rt2800_eeprom_read(rt2x00dev, EEPROM_EXT_LNA2, &word);
+ if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0x00 ||
+ rt2x00_get_field16(word, EEPROM_EXT_LNA2_A1) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1,
+ default_lna_gain);
+ if (rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0x00 ||
+ rt2x00_get_field16(word, EEPROM_EXT_LNA2_A2) == 0xff)
+ rt2x00_set_field16(&word, EEPROM_EXT_LNA2_A1,
+ default_lna_gain);
+ rt2800_eeprom_write(rt2x00dev, EEPROM_EXT_LNA2, word);
+ }
return 0;
}
/*
* Read EEPROM word for configuration.
*/
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
/*
* Identify RF chipset by EEPROM value
if (rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392))
- rt2x00_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_CHIP_ID, &rf);
else
rf = rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RF_TYPE);
case RF3021:
case RF3022:
case RF3052:
+ case RF3053:
case RF3290:
case RF3320:
case RF3322:
rt2x00dev->default_ant.rx_chain_num =
rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH);
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF1, &eeprom);
if (rt2x00_rt(rt2x00dev, RT3070) ||
rt2x00_rt(rt2x00dev, RT3090) ||
/*
* Read frequency offset and RF programming sequence.
*/
- rt2x00_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_FREQ, &eeprom);
rt2x00dev->freq_offset = rt2x00_get_field16(eeprom, EEPROM_FREQ_OFFSET);
/*
/*
* Check if support EIRP tx power limit feature.
*/
- rt2x00_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_EIRP_MAX_TX_POWER, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_EIRP_MAX_TX_POWER_2GHZ) <
EIRP_MAX_TX_POWER_LIMIT)
{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;
struct channel_info *info;
char *default_power1;
char *default_power2;
+ char *default_power3;
unsigned int i;
u16 eeprom;
u32 reg;
SET_IEEE80211_DEV(rt2x00dev->hw, rt2x00dev->dev);
SET_IEEE80211_PERM_ADDR(rt2x00dev->hw,
- rt2x00_eeprom_addr(rt2x00dev,
+ rt2800_eeprom_addr(rt2x00dev,
EEPROM_MAC_ADDR_0));
/*
rt2x00dev->hw->max_report_rates = 7;
rt2x00dev->hw->max_rate_tries = 1;
- rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
+ rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom);
/*
* Initialize hw_mode information.
spec->supported_bands |= SUPPORT_BAND_5GHZ;
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;
spec->channels_info = info;
- default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
- default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+ default_power1 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG1);
+ default_power2 = rt2800_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_BG2);
+
+ if (rt2x00dev->default_ant.tx_chain_num > 2)
+ default_power3 = rt2800_eeprom_addr(rt2x00dev,
+ EEPROM_EXT_TXPOWER_BG3);
+ else
+ default_power3 = NULL;
for (i = 0; i < 14; i++) {
info[i].default_power1 = default_power1[i];
info[i].default_power2 = default_power2[i];
+ if (default_power3)
+ info[i].default_power3 = default_power3[i];
}
if (spec->num_channels > 14) {
- default_power1 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A1);
- default_power2 = rt2x00_eeprom_addr(rt2x00dev, EEPROM_TXPOWER_A2);
+ default_power1 = rt2800_eeprom_addr(rt2x00dev,
+ EEPROM_TXPOWER_A1);
+ default_power2 = rt2800_eeprom_addr(rt2x00dev,
+ EEPROM_TXPOWER_A2);
+
+ if (rt2x00dev->default_ant.tx_chain_num > 2)
+ default_power3 =
+ rt2800_eeprom_addr(rt2x00dev,
+ EEPROM_EXT_TXPOWER_A3);
+ else
+ default_power3 = NULL;
for (i = 14; i < spec->num_channels; i++) {
info[i].default_power1 = default_power1[i - 14];
info[i].default_power2 = default_power2[i - 14];
+ if (default_power3)
+ info[i].default_power3 = default_power3[i - 14];
}
}
case RF3022:
case RF3320:
case RF3052:
+ case RF3053:
case RF3290:
case RF5360:
case RF5370:
case RT3352:
case RT3390:
case RT3572:
+ case RT3593:
case RT5390:
case RT5392:
case RT5592:
struct survey_info *survey);
void rt2800_disable_wpdma(struct rt2x00_dev *rt2x00dev);
+void rt2800_get_txwi_rxwi_size(struct rt2x00_dev *rt2x00dev,
+ unsigned short *txwi_size,
+ unsigned short *rxwi_size);
+
#endif /* RT2800LIB_H */
rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00);
if (rt2x00_is_pcie(rt2x00dev) &&
- (rt2x00_rt(rt2x00dev, RT3572) ||
+ (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, 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);
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_DESC_SIZE_4WORDS;
+ queue->winfo_size = rxwi_size;
queue->priv_size = sizeof(struct queue_entry_priv_mmio);
break;
queue->limit = 64;
queue->data_size = AGGREGATION_SIZE;
queue->desc_size = TXD_DESC_SIZE;
- queue->winfo_size = TXWI_DESC_SIZE_4WORDS;
+ queue->winfo_size = txwi_size;
queue->priv_size = sizeof(struct queue_entry_priv_mmio);
break;
queue->limit = 8;
queue->data_size = 0; /* No DMA required for beacons */
queue->desc_size = TXD_DESC_SIZE;
- queue->winfo_size = TXWI_DESC_SIZE_4WORDS;
+ queue->winfo_size = txwi_size;
queue->priv_size = sizeof(struct queue_entry_priv_mmio);
break;
struct rt2x00_dev *rt2x00dev = queue->rt2x00dev;
unsigned short txwi_size, rxwi_size;
- if (rt2x00_rt(rt2x00dev, RT5592)) {
- txwi_size = TXWI_DESC_SIZE_5WORDS;
- rxwi_size = RXWI_DESC_SIZE_6WORDS;
- } else {
- txwi_size = TXWI_DESC_SIZE_4WORDS;
- rxwi_size = RXWI_DESC_SIZE_4WORDS;
- }
+ rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size);
switch (queue->qid) {
case QID_RX:
/* Zinwell */
{ USB_DEVICE(0x5a57, 0x0284) },
#endif
+#ifdef CONFIG_RT2800USB_RT3573
+ /* AirLive */
+ { USB_DEVICE(0x1b75, 0x7733) },
+ /* ASUS */
+ { USB_DEVICE(0x0b05, 0x17bc) },
+ { USB_DEVICE(0x0b05, 0x17ad) },
+ /* Belkin */
+ { USB_DEVICE(0x050d, 0x1103) },
+ /* Cameo */
+ { USB_DEVICE(0x148f, 0xf301) },
+ /* Edimax */
+ { USB_DEVICE(0x7392, 0x7733) },
+ /* Hawking */
+ { USB_DEVICE(0x0e66, 0x0020) },
+ { USB_DEVICE(0x0e66, 0x0021) },
+ /* I-O DATA */
+ { USB_DEVICE(0x04bb, 0x094e) },
+ /* Linksys */
+ { USB_DEVICE(0x13b1, 0x003b) },
+ /* Logitec */
+ { USB_DEVICE(0x0789, 0x016b) },
+ /* NETGEAR */
+ { USB_DEVICE(0x0846, 0x9012) },
+ { USB_DEVICE(0x0846, 0x9019) },
+ /* Planex */
+ { USB_DEVICE(0x2019, 0xed19) },
+ /* Ralink */
+ { USB_DEVICE(0x148f, 0x3573) },
+ /* Sitecom */
+ { USB_DEVICE(0x0df6, 0x0067) },
+ { USB_DEVICE(0x0df6, 0x006a) },
+ /* ZyXEL */
+ { USB_DEVICE(0x0586, 0x3421) },
+#endif
#ifdef CONFIG_RT2800USB_RT53XX
/* Arcadyan */
{ USB_DEVICE(0x043e, 0x7a12) },
short max_power;
short default_power1;
short default_power2;
+ short default_power3;
};
/*
spin_unlock_irqrestore(&queue->index_lock, irqflags);
}
-void rt2x00queue_pause_queue_nocheck(struct data_queue *queue)
+static void rt2x00queue_pause_queue_nocheck(struct data_queue *queue)
{
switch (queue->qid) {
case QID_AC_VO:
* Linux device driver for RTL8180 / RTL8185
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* Thanks to Realtek for their support!
*
#include "grf5101.h"
MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
MODULE_DESCRIPTION("RTL8180 / RTL8185 PCI wireless driver");
MODULE_LICENSE("GPL");
/*
* Radio tuning for GCT GRF5101 on RTL8180
*
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Code from the BSD driver and the rtl8181 project have been
* very useful to understand certain things
/*
* Radio tuning for GCT GRF5101 on RTL8180
*
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Code from the BSD driver and the rtl8181 project have been
* very useful to understand certain things
/*
* Radio tuning for Maxim max2820 on RTL8180
*
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Code from the BSD driver and the rtl8181 project have been
* very useful to understand certain things
/*
* Radio tuning for Maxim max2820 on RTL8180
*
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Code from the BSD driver and the rtl8181 project have been
* very useful to understand certain things
* Radio tuning for RTL8225 on RTL8180
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8180 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* Thanks to Realtek for their support!
*
/*
* Radio tuning for Philips SA2400 on RTL8180
*
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Code from the BSD driver and the rtl8181 project have been
* very useful to understand certain things
/*
* Radio tuning for Philips SA2400 on RTL8180
*
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Code from the BSD driver and the rtl8181 project have been
* very useful to understand certain things
* Linux device driver for RTL8187
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* The driver was extended to the RTL8187B in 2008 by:
* Herton Ronaldo Krzesinski <herton@mandriva.com.br>
#include "rfkill.h"
MODULE_AUTHOR("Michael Wu <flamingice@sourmilk.net>");
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
MODULE_AUTHOR("Herton Ronaldo Krzesinski <herton@mandriva.com.br>");
MODULE_AUTHOR("Hin-Tak Leung <htl10@users.sourceforge.net>");
MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>");
* Definitions for RTL8187 hardware
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* 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
* Radio tuning for RTL8225 on RTL8187
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* Magic delays, register offsets, and phy value tables below are
* taken from the original r8187 driver sources. Thanks to Realtek
* Radio tuning definitions for RTL8225 on RTL8187
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* 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
* Definitions for RTL818x hardware
*
* Copyright 2007 Michael Wu <flamingice@sourmilk.net>
- * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright 2007 Andrea Merello <andrea.merello@gmail.com>
*
* Based on the r8187 driver, which is:
- * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2005 Andrea Merello <andrea.merello@gmail.com>, et al.
*
* 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
find_p2p_ie = true;
/*to find noa ie*/
while (ie + 1 < end) {
- noa_len = READEF2BYTE(&ie[1]);
+ noa_len = READEF2BYTE((__le16 *)&ie[1]);
if (ie + 3 + ie[1] > end)
return;
READEF1BYTE(ie+index);
index += 1;
p2pinfo->noa_duration[i] =
- READEF4BYTE(ie+index);
+ READEF4BYTE((__le32 *)ie+index);
index += 4;
p2pinfo->noa_interval[i] =
- READEF4BYTE(ie+index);
+ READEF4BYTE((__le32 *)ie+index);
index += 4;
p2pinfo->noa_start_time[i] =
- READEF4BYTE(ie+index);
+ READEF4BYTE((__le32 *)ie+index);
index += 4;
}
RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
/*to find noa ie*/
while (ie + 1 < end) {
- noa_len = READEF2BYTE(&ie[1]);
+ noa_len = READEF2BYTE((__le16 *)&ie[1]);
if (ie + 3 + ie[1] > end)
return;
READEF1BYTE(ie+index);
index += 1;
p2pinfo->noa_duration[i] =
- READEF4BYTE(ie+index);
+ READEF4BYTE((__le32 *)ie+index);
index += 4;
p2pinfo->noa_interval[i] =
- READEF4BYTE(ie+index);
+ READEF4BYTE((__le32 *)ie+index);
index += 4;
p2pinfo->noa_start_time[i] =
- READEF4BYTE(ie+index);
+ READEF4BYTE((__le32 *)ie+index);
index += 4;
}
static void rtl_rate_init(void *ppriv,
struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{
}
void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
u8 element_id, u32 cmd_len, u8 *p_cmdbuffer);
bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw);
+void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw,
+ struct ieee80211_sta *sta,
+ u8 rssi_level);
#endif
u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw,
enum radio_path rfpath, u32 regaddr, u32 bitmask);
void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw);
-void rtl92cu_update_hal_rate_tbl(struct ieee80211_hw *hw,
- struct ieee80211_sta *sta,
- u8 rssi_level);
#endif
#ifdef CONFIG_PM
static int
-wl1271_validate_wowlan_pattern(struct cfg80211_wowlan_trig_pkt_pattern *p)
+wl1271_validate_wowlan_pattern(struct cfg80211_pkt_pattern *p)
{
int num_fields = 0, in_field = 0, fields_size = 0;
int i, pattern_len = 0;
* Allocates an RX filter returned through f
* which needs to be freed using rx_filter_free()
*/
-static int wl1271_convert_wowlan_pattern_to_rx_filter(
- struct cfg80211_wowlan_trig_pkt_pattern *p,
- struct wl12xx_rx_filter **f)
+static int
+wl1271_convert_wowlan_pattern_to_rx_filter(struct cfg80211_pkt_pattern *p,
+ struct wl12xx_rx_filter **f)
{
int i, j, ret = 0;
struct wl12xx_rx_filter *filter;
/* Translate WoWLAN patterns into filters */
for (i = 0; i < wow->n_patterns; i++) {
- struct cfg80211_wowlan_trig_pkt_pattern *p;
+ struct cfg80211_pkt_pattern *p;
struct wl12xx_rx_filter *filter = NULL;
p = &wow->patterns[i];
return ret;
}
-int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len)
+int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len)
{
struct wl1271 *wl = hw->priv;
struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
#include <net/mac80211.h>
-int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len);
+int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len);
#endif /* __WL1271_TESTMODE_H__ */
len = fw_entry->size;
buf = kmalloc(1024, GFP_ATOMIC);
- if (!buf)
+ if (!buf) {
+ err = -ENOMEM;
goto exit;
+ }
while (len > 0) {
int translen = (len > 1024) ? 1024 : len;
zd->endp_out2 = 2;
zd->rx_urb = usb_alloc_urb(0, GFP_KERNEL);
zd->tx_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!zd->rx_urb || !zd->tx_urb)
+ if (!zd->rx_urb || !zd->tx_urb) {
+ err = -ENOMEM;
goto err_zd;
+ }
mdelay(100);
err = zd1201_drvr_start(zd);
static struct nfcsim *dev0;
static struct nfcsim *dev1;
-struct workqueue_struct *wq;
+static struct workqueue_struct *wq;
static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown)
{
kfree(dev);
}
-int __init nfcsim_init(void)
+static int __init nfcsim_init(void)
{
int rc;
return rc;
}
-void __exit nfcsim_exit(void)
+static void __exit nfcsim_exit(void)
{
nfcsim_cleanup_dev(dev0, 1);
nfcsim_cleanup_dev(dev1, 1);
/* How much time we spend listening for initiators */
#define PN533_LISTEN_TIME 2
+/* Delay between each poll frame (ms) */
+#define PN533_POLL_INTERVAL 10
-/* Standard pn533 frame definitions */
+/* Standard pn533 frame definitions (standard and extended)*/
#define PN533_STD_FRAME_HEADER_LEN (sizeof(struct pn533_std_frame) \
+ 2) /* data[0] TFI, data[1] CC */
#define PN533_STD_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/
+#define PN533_EXT_FRAME_HEADER_LEN (sizeof(struct pn533_ext_frame) \
+ + 2) /* data[0] TFI, data[1] CC */
+
+#define PN533_CMD_DATAEXCH_DATA_MAXLEN 262
+#define PN533_CMD_DATAFRAME_MAXLEN 240 /* max data length (send) */
+
/*
* Max extended frame payload len, excluding TFI and CC
* which are already in PN533_FRAME_HEADER_LEN.
Postamble (1) */
#define PN533_STD_FRAME_CHECKSUM(f) (f->data[f->datalen])
#define PN533_STD_FRAME_POSTAMBLE(f) (f->data[f->datalen + 1])
+/* Half start code (3), LEN (4) should be 0xffff for extended frame */
+#define PN533_STD_IS_EXTENDED(hdr) ((hdr)->datalen == 0xFF \
+ && (hdr)->datalen_checksum == 0xFF)
+#define PN533_EXT_FRAME_CHECKSUM(f) (f->data[be16_to_cpu(f->datalen)])
/* start of frame */
#define PN533_STD_FRAME_SOF 0x00FF
#define PN533_ACR122_RDR_TO_PC_ESCAPE 0x83
/* PN533 Commands */
-#define PN533_STD_FRAME_CMD(f) (f->data[1])
+#define PN533_FRAME_CMD(f) (f->data[1])
#define PN533_CMD_GET_FIRMWARE_VERSION 0x02
#define PN533_CMD_RF_CONFIGURATION 0x32
#define PN533_CFGITEM_MAX_RETRIES 0x05
#define PN533_CFGITEM_PASORI 0x82
-#define PN533_CFGITEM_RF_FIELD_ON 0x1
-#define PN533_CFGITEM_RF_FIELD_OFF 0x0
+#define PN533_CFGITEM_RF_FIELD_AUTO_RFCA 0x2
+#define PN533_CFGITEM_RF_FIELD_ON 0x1
+#define PN533_CFGITEM_RF_FIELD_OFF 0x0
#define PN533_CONFIG_TIMING_102 0xb
#define PN533_CONFIG_TIMING_204 0xc
.initiator_data.felica = {
.opcode = PN533_FELICA_OPC_SENSF_REQ,
.sc = PN533_FELICA_SENSF_SC_ALL,
- .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+ .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
.tsn = 0x03,
},
},
.initiator_data.felica = {
.opcode = PN533_FELICA_OPC_SENSF_REQ,
.sc = PN533_FELICA_SENSF_SC_ALL,
- .rc = PN533_FELICA_SENSF_RC_NO_SYSTEM_CODE,
+ .rc = PN533_FELICA_SENSF_RC_SYSTEM_CODE,
.tsn = 0x03,
},
},
struct urb *in_urb;
struct sk_buff_head resp_q;
+ struct sk_buff_head fragment_skb;
struct workqueue_struct *wq;
struct work_struct cmd_work;
struct work_struct cmd_complete_work;
- struct work_struct poll_work;
- struct work_struct mi_work;
+ struct delayed_work poll_work;
+ struct work_struct mi_rx_work;
+ struct work_struct mi_tx_work;
struct work_struct tg_work;
+ struct work_struct rf_work;
struct list_head cmd_queue;
struct pn533_cmd *cmd;
struct mutex cmd_lock; /* protects cmd queue */
void *cmd_complete_mi_arg;
+ void *cmd_complete_dep_arg;
struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1];
u8 poll_mod_count;
u8 data[];
} __packed;
+struct pn533_ext_frame { /* Extended Information frame */
+ u8 preamble;
+ __be16 start_frame;
+ __be16 eif_flag; /* fixed to 0xFFFF */
+ __be16 datalen;
+ u8 datalen_checksum;
+ u8 data[];
+} __packed;
+
struct pn533_frame_ops {
void (*tx_frame_init)(void *frame, u8 cmd_code);
void (*tx_frame_finish)(void *frame);
int tx_header_len;
int tx_tail_len;
- bool (*rx_is_frame_valid)(void *frame);
+ bool (*rx_is_frame_valid)(void *frame, struct pn533 *dev);
int (*rx_frame_size)(void *frame);
int rx_header_len;
int rx_tail_len;
frame->datalen += len;
}
-static bool pn533_acr122_is_rx_frame_valid(void *_frame)
+static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)
{
struct pn533_acr122_rx_frame *frame = _frame;
{
struct pn533_acr122_rx_frame *f = frame;
- return PN533_STD_FRAME_CMD(f);
+ return PN533_FRAME_CMD(f);
}
static struct pn533_frame_ops pn533_acr122_frame_ops = {
.get_cmd_code = pn533_acr122_get_cmd_code,
};
+/* The rule: value(high byte) + value(low byte) + checksum = 0 */
+static inline u8 pn533_ext_checksum(u16 value)
+{
+ return ~(u8)(((value & 0xFF00) >> 8) + (u8)(value & 0xFF)) + 1;
+}
+
/* The rule: value + checksum = 0 */
static inline u8 pn533_std_checksum(u8 value)
{
frame->preamble = 0;
frame->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF);
PN533_STD_FRAME_IDENTIFIER(frame) = PN533_STD_FRAME_DIR_OUT;
- PN533_STD_FRAME_CMD(frame) = cmd_code;
+ PN533_FRAME_CMD(frame) = cmd_code;
frame->datalen = 2;
}
frame->datalen += len;
}
-static bool pn533_std_rx_frame_is_valid(void *_frame)
+static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev)
{
u8 checksum;
- struct pn533_std_frame *frame = _frame;
+ struct pn533_std_frame *stdf = _frame;
- if (frame->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
+ if (stdf->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF))
return false;
- checksum = pn533_std_checksum(frame->datalen);
- if (checksum != frame->datalen_checksum)
- return false;
+ if (likely(!PN533_STD_IS_EXTENDED(stdf))) {
+ /* Standard frame code */
+ dev->ops->rx_header_len = PN533_STD_FRAME_HEADER_LEN;
- checksum = pn533_std_data_checksum(frame->data, frame->datalen);
- if (checksum != PN533_STD_FRAME_CHECKSUM(frame))
- return false;
+ checksum = pn533_std_checksum(stdf->datalen);
+ if (checksum != stdf->datalen_checksum)
+ return false;
+
+ checksum = pn533_std_data_checksum(stdf->data, stdf->datalen);
+ if (checksum != PN533_STD_FRAME_CHECKSUM(stdf))
+ return false;
+ } else {
+ /* Extended */
+ struct pn533_ext_frame *eif = _frame;
+
+ dev->ops->rx_header_len = PN533_EXT_FRAME_HEADER_LEN;
+
+ checksum = pn533_ext_checksum(be16_to_cpu(eif->datalen));
+ if (checksum != eif->datalen_checksum)
+ return false;
+
+ /* check data checksum */
+ checksum = pn533_std_data_checksum(eif->data,
+ be16_to_cpu(eif->datalen));
+ if (checksum != PN533_EXT_FRAME_CHECKSUM(eif))
+ return false;
+ }
return true;
}
{
struct pn533_std_frame *f = frame;
+ /* check for Extended Information frame */
+ if (PN533_STD_IS_EXTENDED(f)) {
+ struct pn533_ext_frame *eif = frame;
+
+ return sizeof(struct pn533_ext_frame)
+ + be16_to_cpu(eif->datalen) + PN533_STD_FRAME_TAIL_LEN;
+ }
+
return sizeof(struct pn533_std_frame) + f->datalen +
PN533_STD_FRAME_TAIL_LEN;
}
static u8 pn533_std_get_cmd_code(void *frame)
{
struct pn533_std_frame *f = frame;
+ struct pn533_ext_frame *eif = frame;
- return PN533_STD_FRAME_CMD(f);
+ if (PN533_STD_IS_EXTENDED(f))
+ return PN533_FRAME_CMD(eif);
+ else
+ return PN533_FRAME_CMD(f);
}
static struct pn533_frame_ops pn533_std_frame_ops = {
print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame,
dev->ops->rx_frame_size(in_frame), false);
- if (!dev->ops->rx_is_frame_valid(in_frame)) {
+ if (!dev->ops->rx_is_frame_valid(in_frame, dev)) {
nfc_dev_err(&dev->interface->dev, "Received an invalid frame");
cmd->status = -EIO;
goto sched_wq;
pn533_poll_next_mod(dev);
- queue_work(dev->wq, &dev->poll_work);
+ queue_delayed_work(dev->wq, &dev->poll_work,
+ msecs_to_jiffies(PN533_POLL_INTERVAL));
+}
+
+static int pn533_rf_complete(struct pn533 *dev, void *arg,
+ struct sk_buff *resp)
+{
+ int rc = 0;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+
+ nfc_dev_err(&dev->interface->dev, "%s RF setting error %d",
+ __func__, rc);
+
+ return rc;
+ }
+
+ queue_delayed_work(dev->wq, &dev->poll_work,
+ msecs_to_jiffies(PN533_POLL_INTERVAL));
+
+ dev_kfree_skb(resp);
+ return rc;
+}
+
+static void pn533_wq_rf(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, rf_work);
+ struct sk_buff *skb;
+ int rc;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ skb = pn533_alloc_skb(dev, 2);
+ if (!skb)
+ return;
+
+ *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD;
+ *skb_put(skb, 1) = PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
+
+ rc = pn533_send_cmd_async(dev, PN533_CMD_RF_CONFIGURATION, skb,
+ pn533_rf_complete, NULL);
+ if (rc < 0) {
+ dev_kfree_skb(skb);
+ nfc_dev_err(&dev->interface->dev, "RF setting error %d", rc);
+ }
+
+ return;
}
static int pn533_poll_complete(struct pn533 *dev, void *arg,
}
pn533_poll_next_mod(dev);
- queue_work(dev->wq, &dev->poll_work);
+ /* Not target found, turn radio off */
+ queue_work(dev->wq, &dev->rf_work);
done:
dev_kfree_skb(resp);
static void pn533_wq_poll(struct work_struct *work)
{
- struct pn533 *dev = container_of(work, struct pn533, poll_work);
+ struct pn533 *dev = container_of(work, struct pn533, poll_work.work);
struct pn533_poll_modulations *cur_mod;
int rc;
u32 im_protocols, u32 tm_protocols)
{
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
+ u8 rand_mod;
nfc_dev_dbg(&dev->interface->dev,
"%s: im protocols 0x%x tm protocols 0x%x",
tm_protocols = 0;
}
- dev->poll_mod_curr = 0;
pn533_poll_create_mod_list(dev, im_protocols, tm_protocols);
dev->poll_protocols = im_protocols;
dev->listen_protocols = tm_protocols;
+ /* Do not always start polling from the same modulation */
+ get_random_bytes(&rand_mod, sizeof(rand_mod));
+ rand_mod %= dev->poll_mod_count;
+ dev->poll_mod_curr = rand_mod;
+
return pn533_send_poll_frame(dev);
}
}
pn533_abort_cmd(dev, GFP_KERNEL);
+ flush_delayed_work(&dev->poll_work);
pn533_poll_reset_mod_list(dev);
}
return rc;
}
-static int pn533_mod_to_baud(struct pn533 *dev)
-{
- switch (dev->poll_mod_curr) {
- case PN533_POLL_MOD_106KBPS_A:
- return 0;
- case PN533_POLL_MOD_212KBPS_FELICA:
- return 1;
- case PN533_POLL_MOD_424KBPS_FELICA:
- return 2;
- default:
- return -EINVAL;
- }
-}
-
+static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf);
#define PASSIVE_DATA_LEN 5
static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
u8 comm_mode, u8 *gb, size_t gb_len)
{
struct pn533 *dev = nfc_get_drvdata(nfc_dev);
struct sk_buff *skb;
- int rc, baud, skb_len;
- u8 *next, *arg;
+ int rc, skb_len;
+ u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE];
u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3};
return -EBUSY;
}
- baud = pn533_mod_to_baud(dev);
- if (baud < 0) {
- nfc_dev_err(&dev->interface->dev,
- "Invalid curr modulation %d", dev->poll_mod_curr);
- return baud;
- }
-
skb_len = 3 + gb_len; /* ActPass + BR + Next */
- if (comm_mode == NFC_COMM_PASSIVE)
- skb_len += PASSIVE_DATA_LEN;
+ skb_len += PASSIVE_DATA_LEN;
- if (target && target->nfcid2_len)
- skb_len += NFC_NFCID3_MAXSIZE;
+ /* NFCID3 */
+ skb_len += NFC_NFCID3_MAXSIZE;
+ if (target && !target->nfcid2_len) {
+ nfcid3[0] = 0x1;
+ nfcid3[1] = 0xfe;
+ get_random_bytes(nfcid3 + 2, 6);
+ }
skb = pn533_alloc_skb(dev, skb_len);
if (!skb)
return -ENOMEM;
*skb_put(skb, 1) = !comm_mode; /* ActPass */
- *skb_put(skb, 1) = baud; /* Baud rate */
+ *skb_put(skb, 1) = 0x02; /* 424 kbps */
next = skb_put(skb, 1); /* Next */
*next = 0;
- if (comm_mode == NFC_COMM_PASSIVE && baud > 0) {
- memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data,
- PASSIVE_DATA_LEN);
- *next |= 1;
- }
+ /* Copy passive data */
+ memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN);
+ *next |= 1;
- if (target && target->nfcid2_len) {
+ /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */
+ if (target && target->nfcid2_len)
memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), target->nfcid2,
target->nfcid2_len);
- *next |= 2;
- }
+ else
+ memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3,
+ NFC_NFCID3_MAXSIZE);
+ *next |= 2;
if (gb != NULL && gb_len > 0) {
memcpy(skb_put(skb, gb_len), gb, gb_len);
*arg = !comm_mode;
+ pn533_rf_field(dev->nfc_dev, 0);
+
rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb,
pn533_in_dep_link_up_complete, arg);
if (mi) {
dev->cmd_complete_mi_arg = arg;
- queue_work(dev->wq, &dev->mi_work);
+ queue_work(dev->wq, &dev->mi_rx_work);
+ return -EINPROGRESS;
+ }
+
+ /* Prepare for the next round */
+ if (skb_queue_len(&dev->fragment_skb) > 0) {
+ dev->cmd_complete_dep_arg = arg;
+ queue_work(dev->wq, &dev->mi_tx_work);
+
return -EINPROGRESS;
}
return rc;
}
+/* Split the Tx skb into small chunks */
+static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb)
+{
+ struct sk_buff *frag;
+ int frag_size;
+
+ do {
+ /* Remaining size */
+ if (skb->len > PN533_CMD_DATAFRAME_MAXLEN)
+ frag_size = PN533_CMD_DATAFRAME_MAXLEN;
+ else
+ frag_size = skb->len;
+
+ /* Allocate and reserve */
+ frag = pn533_alloc_skb(dev, frag_size);
+ if (!frag) {
+ skb_queue_purge(&dev->fragment_skb);
+ break;
+ }
+
+ /* Reserve the TG/MI byte */
+ skb_reserve(frag, 1);
+
+ /* MI + TG */
+ if (frag_size == PN533_CMD_DATAFRAME_MAXLEN)
+ *skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1);
+ else
+ *skb_push(frag, sizeof(u8)) = 1; /* TG */
+
+ memcpy(skb_put(frag, frag_size), skb->data, frag_size);
+
+ /* Reduce the size of incoming buffer */
+ skb_pull(skb, frag_size);
+
+ /* Add this to skb_queue */
+ skb_queue_tail(&dev->fragment_skb, frag);
+
+ } while (skb->len > 0);
+
+ dev_kfree_skb(skb);
+
+ return skb_queue_len(&dev->fragment_skb);
+}
+
static int pn533_transceive(struct nfc_dev *nfc_dev,
struct nfc_target *target, struct sk_buff *skb,
data_exchange_cb_t cb, void *cb_context)
nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
- if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
- /* TODO: Implement support to multi-part data exchange */
- nfc_dev_err(&dev->interface->dev,
- "Data length greater than the max allowed: %d",
- PN533_CMD_DATAEXCH_DATA_MAXLEN);
- rc = -ENOSYS;
- goto error;
- }
-
if (!dev->tgt_active_prot) {
nfc_dev_err(&dev->interface->dev,
"Can't exchange data if there is no active target");
break;
}
default:
- *skb_push(skb, sizeof(u8)) = 1; /*TG*/
+ /* jumbo frame ? */
+ if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) {
+ rc = pn533_fill_fragment_skbs(dev, skb);
+ if (rc <= 0)
+ goto error;
+
+ skb = skb_dequeue(&dev->fragment_skb);
+ if (!skb) {
+ rc = -EIO;
+ goto error;
+ }
+ } else {
+ *skb_push(skb, sizeof(u8)) = 1; /* TG */
+ }
rc = pn533_send_data_async(dev, PN533_CMD_IN_DATA_EXCHANGE,
skb, pn533_data_exchange_complete,
static void pn533_wq_mi_recv(struct work_struct *work)
{
- struct pn533 *dev = container_of(work, struct pn533, mi_work);
+ struct pn533 *dev = container_of(work, struct pn533, mi_rx_work);
struct sk_buff *skb;
int rc;
queue_work(dev->wq, &dev->cmd_work);
}
+static void pn533_wq_mi_send(struct work_struct *work)
+{
+ struct pn533 *dev = container_of(work, struct pn533, mi_tx_work);
+ struct sk_buff *skb;
+ int rc;
+
+ nfc_dev_dbg(&dev->interface->dev, "%s", __func__);
+
+ /* Grab the first skb in the queue */
+ skb = skb_dequeue(&dev->fragment_skb);
+
+ if (skb == NULL) { /* No more data */
+ /* Reset the queue for future use */
+ skb_queue_head_init(&dev->fragment_skb);
+ goto error;
+ }
+
+ switch (dev->device_type) {
+ case PN533_DEVICE_PASORI:
+ if (dev->tgt_active_prot != NFC_PROTO_FELICA) {
+ rc = -EIO;
+ break;
+ }
+
+ rc = pn533_send_cmd_direct_async(dev, PN533_CMD_IN_COMM_THRU,
+ skb,
+ pn533_data_exchange_complete,
+ dev->cmd_complete_dep_arg);
+
+ break;
+
+ default:
+ /* Still some fragments? */
+ rc = pn533_send_cmd_direct_async(dev,PN533_CMD_IN_DATA_EXCHANGE,
+ skb,
+ pn533_data_exchange_complete,
+ dev->cmd_complete_dep_arg);
+
+ break;
+ }
+
+ if (rc == 0) /* success */
+ return;
+
+ nfc_dev_err(&dev->interface->dev,
+ "Error %d when trying to perform data_exchange", rc);
+
+ dev_kfree_skb(skb);
+ kfree(dev->cmd_complete_dep_arg);
+
+error:
+ pn533_send_ack(dev, GFP_KERNEL);
+ queue_work(dev->wq, &dev->cmd_work);
+}
+
static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata,
u8 cfgdata_len)
{
u8 rf_field = !!rf;
int rc;
+ rf_field |= PN533_CFGITEM_RF_FIELD_AUTO_RFCA;
+
rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD,
(u8 *)&rf_field, 1);
if (rc) {
switch (dev->device_type) {
case PN533_DEVICE_STD:
- max_retries.mx_rty_atr = PN533_CONFIG_MAX_RETRIES_ENDLESS;
- max_retries.mx_rty_psl = 2;
- max_retries.mx_rty_passive_act =
- PN533_CONFIG_MAX_RETRIES_NO_RETRY;
-
- timing.rfu = PN533_CONFIG_TIMING_102;
- timing.atr_res_timeout = PN533_CONFIG_TIMING_204;
- timing.dep_timeout = PN533_CONFIG_TIMING_409;
-
- break;
-
case PN533_DEVICE_PASORI:
case PN533_DEVICE_ACR122U:
max_retries.mx_rty_atr = 0x2;
INIT_WORK(&dev->cmd_work, pn533_wq_cmd);
INIT_WORK(&dev->cmd_complete_work, pn533_wq_cmd_complete);
- INIT_WORK(&dev->mi_work, pn533_wq_mi_recv);
+ INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv);
+ INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send);
INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data);
- INIT_WORK(&dev->poll_work, pn533_wq_poll);
+ INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll);
+ INIT_WORK(&dev->rf_work, pn533_wq_rf);
dev->wq = alloc_ordered_workqueue("pn533", 0);
if (dev->wq == NULL)
goto error;
dev->listen_timer.function = pn533_listen_mode_timer;
skb_queue_head_init(&dev->resp_q);
+ skb_queue_head_init(&dev->fragment_skb);
INIT_LIST_HEAD(&dev->cmd_queue);
usb_kill_urb(dev->in_urb);
usb_kill_urb(dev->out_urb);
+ flush_delayed_work(&dev->poll_work);
destroy_workqueue(dev->wq);
skb_queue_purge(&dev->resp_q);
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
-
+#include <linux/nfc.h>
+#include <linux/firmware.h>
+#include <linux/unaligned/access_ok.h>
#include <linux/platform_data/pn544.h>
#include <net/nfc/hci.h>
#include <net/nfc/llc.h>
+#include <net/nfc/nfc.h>
#include "pn544.h"
#define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c"
+#define PN544_FW_CMD_WRITE 0x08
+#define PN544_FW_CMD_CHECK 0x06
+
+struct pn544_i2c_fw_frame_write {
+ u8 cmd;
+ u16 be_length;
+ u8 be_dest_addr[3];
+ u16 be_datalen;
+ u8 data[];
+} __packed;
+
+struct pn544_i2c_fw_frame_check {
+ u8 cmd;
+ u16 be_length;
+ u8 be_start_addr[3];
+ u16 be_datalen;
+ u16 be_crc;
+} __packed;
+
+struct pn544_i2c_fw_frame_response {
+ u8 status;
+ u16 be_length;
+} __packed;
+
+struct pn544_i2c_fw_blob {
+ u32 be_size;
+ u32 be_destaddr;
+ u8 data[];
+};
+
+#define PN544_FW_CMD_RESULT_TIMEOUT 0x01
+#define PN544_FW_CMD_RESULT_BAD_CRC 0x02
+#define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08
+#define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B
+#define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11
+#define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18
+#define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74
+
+#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
+
+#define PN544_FW_WRITE_BUFFER_MAX_LEN 0x9f7
+#define PN544_FW_I2C_MAX_PAYLOAD PN544_HCI_I2C_LLC_MAX_SIZE
+#define PN544_FW_I2C_WRITE_FRAME_HEADER_LEN 8
+#define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\
+ PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\
+ PN544_FW_WRITE_BUFFER_MAX_LEN)
+
+#define FW_WORK_STATE_IDLE 1
+#define FW_WORK_STATE_START 2
+#define FW_WORK_STATE_WAIT_WRITE_ANSWER 3
+#define FW_WORK_STATE_WAIT_CHECK_ANSWER 4
+
struct pn544_i2c_phy {
struct i2c_client *i2c_dev;
struct nfc_hci_dev *hdev;
unsigned int gpio_fw;
unsigned int en_polarity;
+ struct work_struct fw_work;
+ int fw_work_state;
+ char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+ const struct firmware *fw;
+ u32 fw_blob_dest_addr;
+ size_t fw_blob_size;
+ const u8 *fw_blob_data;
+ size_t fw_written;
+ int fw_cmd_result;
+
int powered;
+ int run_mode;
int hard_fault; /*
* < 0 if hardware error occured (e.g. i2c err)
gpio_set_value(phy->gpio_en, !phy->en_polarity);
}
+static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode)
+{
+ gpio_set_value(phy->gpio_fw, run_mode == PN544_FW_MODE ? 1 : 0);
+ gpio_set_value(phy->gpio_en, phy->en_polarity);
+ usleep_range(10000, 15000);
+
+ phy->run_mode = run_mode;
+}
+
static int pn544_hci_i2c_enable(void *phy_id)
{
struct pn544_i2c_phy *phy = phy_id;
pr_info(DRIVER_DESC ": %s\n", __func__);
- gpio_set_value(phy->gpio_fw, 0);
- gpio_set_value(phy->gpio_en, phy->en_polarity);
- usleep_range(10000, 15000);
+ pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE);
phy->powered = 1;
return r;
}
+static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy)
+{
+ int r;
+ struct pn544_i2c_fw_frame_response response;
+ struct i2c_client *client = phy->i2c_dev;
+
+ r = i2c_master_recv(client, (char *) &response, sizeof(response));
+ if (r != sizeof(response)) {
+ dev_err(&client->dev, "cannot read fw status\n");
+ return -EIO;
+ }
+
+ usleep_range(3000, 6000);
+
+ switch (response.status) {
+ case 0:
+ return 0;
+ case PN544_FW_CMD_RESULT_TIMEOUT:
+ return -ETIMEDOUT;
+ case PN544_FW_CMD_RESULT_BAD_CRC:
+ return -ENODATA;
+ case PN544_FW_CMD_RESULT_ACCESS_DENIED:
+ return -EACCES;
+ case PN544_FW_CMD_RESULT_PROTOCOL_ERROR:
+ return -EPROTO;
+ case PN544_FW_CMD_RESULT_INVALID_PARAMETER:
+ return -EINVAL;
+ case PN544_FW_CMD_RESULT_INVALID_LENGTH:
+ return -EBADMSG;
+ case PN544_FW_CMD_RESULT_WRITE_FAILED:
+ return -EIO;
+ default:
+ return -EIO;
+ }
+}
+
/*
* Reads an shdlc frame from the chip. This is not as straightforward as it
* seems. There are cases where we could loose the frame start synchronization.
if (phy->hard_fault != 0)
return IRQ_HANDLED;
- r = pn544_hci_i2c_read(phy, &skb);
- if (r == -EREMOTEIO) {
- phy->hard_fault = r;
+ if (phy->run_mode == PN544_FW_MODE) {
+ phy->fw_cmd_result = pn544_hci_i2c_fw_read_status(phy);
+ schedule_work(&phy->fw_work);
+ } else {
+ r = pn544_hci_i2c_read(phy, &skb);
+ if (r == -EREMOTEIO) {
+ phy->hard_fault = r;
- nfc_hci_recv_frame(phy->hdev, NULL);
+ nfc_hci_recv_frame(phy->hdev, NULL);
- return IRQ_HANDLED;
- } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
- return IRQ_HANDLED;
- }
-
- nfc_hci_recv_frame(phy->hdev, skb);
+ return IRQ_HANDLED;
+ } else if ((r == -ENOMEM) || (r == -EBADMSG)) {
+ return IRQ_HANDLED;
+ }
+ nfc_hci_recv_frame(phy->hdev, skb);
+ }
return IRQ_HANDLED;
}
.disable = pn544_hci_i2c_disable,
};
+static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name)
+{
+ struct pn544_i2c_phy *phy = phy_id;
+
+ pr_info(DRIVER_DESC ": Starting Firmware Download (%s)\n",
+ firmware_name);
+
+ strcpy(phy->firmware_name, firmware_name);
+
+ phy->fw_work_state = FW_WORK_STATE_START;
+
+ schedule_work(&phy->fw_work);
+
+ return 0;
+}
+
+static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy,
+ int result)
+{
+ pr_info(DRIVER_DESC ": Firmware Download Complete, result=%d\n", result);
+
+ pn544_hci_i2c_disable(phy);
+
+ phy->fw_work_state = FW_WORK_STATE_IDLE;
+
+ if (phy->fw) {
+ release_firmware(phy->fw);
+ phy->fw = NULL;
+ }
+
+ nfc_fw_download_done(phy->hdev->ndev, phy->firmware_name, (u32) -result);
+}
+
+static int pn544_hci_i2c_fw_write_cmd(struct i2c_client *client, u32 dest_addr,
+ const u8 *data, u16 datalen)
+{
+ u8 frame[PN544_FW_I2C_MAX_PAYLOAD];
+ struct pn544_i2c_fw_frame_write *framep;
+ u16 params_len;
+ int framelen;
+ int r;
+
+ if (datalen > PN544_FW_I2C_WRITE_DATA_MAX_LEN)
+ datalen = PN544_FW_I2C_WRITE_DATA_MAX_LEN;
+
+ framep = (struct pn544_i2c_fw_frame_write *) frame;
+
+ params_len = sizeof(framep->be_dest_addr) +
+ sizeof(framep->be_datalen) + datalen;
+ framelen = params_len + sizeof(framep->cmd) +
+ sizeof(framep->be_length);
+
+ framep->cmd = PN544_FW_CMD_WRITE;
+
+ put_unaligned_be16(params_len, &framep->be_length);
+
+ framep->be_dest_addr[0] = (dest_addr & 0xff0000) >> 16;
+ framep->be_dest_addr[1] = (dest_addr & 0xff00) >> 8;
+ framep->be_dest_addr[2] = dest_addr & 0xff;
+
+ put_unaligned_be16(datalen, &framep->be_datalen);
+
+ memcpy(framep->data, data, datalen);
+
+ r = i2c_master_send(client, frame, framelen);
+
+ if (r == framelen)
+ return datalen;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+}
+
+static int pn544_hci_i2c_fw_check_cmd(struct i2c_client *client, u32 start_addr,
+ const u8 *data, u16 datalen)
+{
+ struct pn544_i2c_fw_frame_check frame;
+ int r;
+ u16 crc;
+
+ /* calculate local crc for the data we want to check */
+ crc = crc_ccitt(0xffff, data, datalen);
+
+ frame.cmd = PN544_FW_CMD_CHECK;
+
+ put_unaligned_be16(sizeof(frame.be_start_addr) +
+ sizeof(frame.be_datalen) + sizeof(frame.be_crc),
+ &frame.be_length);
+
+ /* tell the chip the memory region to which our crc applies */
+ frame.be_start_addr[0] = (start_addr & 0xff0000) >> 16;
+ frame.be_start_addr[1] = (start_addr & 0xff00) >> 8;
+ frame.be_start_addr[2] = start_addr & 0xff;
+
+ put_unaligned_be16(datalen, &frame.be_datalen);
+
+ /*
+ * and give our local crc. Chip will calculate its own crc for the
+ * region and compare with ours.
+ */
+ put_unaligned_be16(crc, &frame.be_crc);
+
+ r = i2c_master_send(client, (const char *) &frame, sizeof(frame));
+
+ if (r == sizeof(frame))
+ return 0;
+ else if (r < 0)
+ return r;
+ else
+ return -EIO;
+}
+
+static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy)
+{
+ int r;
+
+ r = pn544_hci_i2c_fw_write_cmd(phy->i2c_dev,
+ phy->fw_blob_dest_addr + phy->fw_written,
+ phy->fw_blob_data + phy->fw_written,
+ phy->fw_blob_size - phy->fw_written);
+ if (r < 0)
+ return r;
+
+ phy->fw_written += r;
+ phy->fw_work_state = FW_WORK_STATE_WAIT_WRITE_ANSWER;
+
+ return 0;
+}
+
+static void pn544_hci_i2c_fw_work(struct work_struct *work)
+{
+ struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy,
+ fw_work);
+ int r;
+ struct pn544_i2c_fw_blob *blob;
+
+ switch (phy->fw_work_state) {
+ case FW_WORK_STATE_START:
+ pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE);
+
+ r = request_firmware(&phy->fw, phy->firmware_name,
+ &phy->i2c_dev->dev);
+ if (r < 0)
+ goto exit_state_start;
+
+ blob = (struct pn544_i2c_fw_blob *) phy->fw->data;
+ phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+ phy->fw_blob_dest_addr = get_unaligned_be32(&blob->be_destaddr);
+ phy->fw_blob_data = blob->data;
+
+ phy->fw_written = 0;
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+
+exit_state_start:
+ if (r < 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ case FW_WORK_STATE_WAIT_WRITE_ANSWER:
+ r = phy->fw_cmd_result;
+ if (r < 0)
+ goto exit_state_wait_write_answer;
+
+ if (phy->fw_written == phy->fw_blob_size) {
+ r = pn544_hci_i2c_fw_check_cmd(phy->i2c_dev,
+ phy->fw_blob_dest_addr,
+ phy->fw_blob_data,
+ phy->fw_blob_size);
+ if (r < 0)
+ goto exit_state_wait_write_answer;
+ phy->fw_work_state = FW_WORK_STATE_WAIT_CHECK_ANSWER;
+ break;
+ }
+
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+
+exit_state_wait_write_answer:
+ if (r < 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ case FW_WORK_STATE_WAIT_CHECK_ANSWER:
+ r = phy->fw_cmd_result;
+ if (r < 0)
+ goto exit_state_wait_check_answer;
+
+ blob = (struct pn544_i2c_fw_blob *) (phy->fw_blob_data +
+ phy->fw_blob_size);
+ phy->fw_blob_size = get_unaligned_be32(&blob->be_size);
+ if (phy->fw_blob_size != 0) {
+ phy->fw_blob_dest_addr =
+ get_unaligned_be32(&blob->be_destaddr);
+ phy->fw_blob_data = blob->data;
+
+ phy->fw_written = 0;
+ r = pn544_hci_i2c_fw_write_chunk(phy);
+ }
+
+exit_state_wait_check_answer:
+ if (r < 0 || phy->fw_blob_size == 0)
+ pn544_hci_i2c_fw_work_complete(phy, r);
+ break;
+
+ default:
+ break;
+ }
+}
+
static int pn544_hci_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return -ENOMEM;
}
+ INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work);
+ phy->fw_work_state = FW_WORK_STATE_IDLE;
+
phy->i2c_dev = client;
i2c_set_clientdata(client, phy);
r = pn544_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME,
PN544_I2C_FRAME_HEADROOM, PN544_I2C_FRAME_TAILROOM,
- PN544_HCI_I2C_LLC_MAX_PAYLOAD, &phy->hdev);
+ PN544_HCI_I2C_LLC_MAX_PAYLOAD,
+ pn544_hci_i2c_fw_download, &phy->hdev);
if (r < 0)
goto err_hci;
dev_dbg(&client->dev, "%s\n", __func__);
+ cancel_work_sync(&phy->fw_work);
+ if (phy->fw_work_state != FW_WORK_STATE_IDLE)
+ pn544_hci_i2c_fw_work_complete(phy, -ENODEV);
+
pn544_hci_remove(phy->hdev);
if (phy->powered)
r = pn544_hci_probe(phy, &mei_phy_ops, LLC_NOP_NAME,
MEI_NFC_HEADER_SIZE, 0, MEI_NFC_MAX_HCI_PAYLOAD,
- &phy->hdev);
+ NULL, &phy->hdev);
if (r < 0) {
nfc_mei_phy_free(phy);
/* Timing restrictions (ms) */
#define PN544_HCI_RESETVEN_TIME 30
-#define HCI_MODE 0
-#define FW_MODE 1
-
enum pn544_state {
PN544_ST_COLD,
PN544_ST_FW_READY,
int async_cb_type;
data_exchange_cb_t async_cb;
void *async_cb_context;
+
+ fw_download_t fw_download;
};
static int pn544_hci_open(struct nfc_hci_dev *hdev)
return r;
}
+static int pn544_hci_fw_download(struct nfc_hci_dev *hdev,
+ const char *firmware_name)
+{
+ struct pn544_hci_info *info = nfc_hci_get_clientdata(hdev);
+
+ if (info->fw_download == NULL)
+ return -ENOTSUPP;
+
+ return info->fw_download(info->phy_id, firmware_name);
+}
+
static struct nfc_hci_ops pn544_hci_ops = {
.open = pn544_hci_open,
.close = pn544_hci_close,
.tm_send = pn544_hci_tm_send,
.check_presence = pn544_hci_check_presence,
.event_received = pn544_hci_event_received,
+ .fw_download = pn544_hci_fw_download,
};
int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
int phy_headroom, int phy_tailroom, int phy_payload,
- struct nfc_hci_dev **hdev)
+ fw_download_t fw_download, struct nfc_hci_dev **hdev)
{
struct pn544_hci_info *info;
u32 protocols;
info->phy_ops = phy_ops;
info->phy_id = phy_id;
+ info->fw_download = fw_download;
info->state = PN544_ST_COLD;
mutex_init(&info->info_lock);
#define DRIVER_DESC "HCI NFC driver for PN544"
+#define PN544_HCI_MODE 0
+#define PN544_FW_MODE 1
+
+typedef int (*fw_download_t)(void *context, const char *firmware_name);
+
int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name,
int phy_headroom, int phy_tailroom, int phy_payload,
- struct nfc_hci_dev **hdev);
+ fw_download_t fw_download, struct nfc_hci_dev **hdev);
void pn544_hci_remove(struct nfc_hci_dev *hdev);
#endif /* __LOCAL_PN544_H_ */
config SSB_SFLASH
bool "SSB serial flash support"
- depends on SSB_DRIVER_MIPS && BROKEN
+ depends on SSB_DRIVER_MIPS
default y
# Assumption: We are on embedded, if we compile the MIPS core.
sflash->size = sflash->blocksize * sflash->numblocks;
sflash->present = true;
- pr_info("Found %s serial flash (blocksize: 0x%X, blocks: %d)\n",
- e->name, e->blocksize, e->numblocks);
+ pr_info("Found %s serial flash (size: %dKiB, blocksize: 0x%X, blocks: %d)\n",
+ e->name, sflash->size / 1024, e->blocksize, e->numblocks);
/* Prepare platform device, but don't register it yet. It's too early,
* malloc (required by device_private_init) is not available yet. */
sflash->size;
ssb_sflash_dev.dev.platform_data = sflash;
- pr_err("Serial flash support is not implemented yet!\n");
-
- return -ENOTSUPP;
+ return 0;
}
* Copyright (c) 2004, Intel Corporation
*
* Modified for Realtek's wi-fi cards by Andrea Merello
- * <andreamrl@tiscali.it>
+ * <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
******************************************************************************
Few modifications for Realtek's Wi-Fi drivers by
- Andrea Merello <andreamrl@tiscali.it>
+ Andrea Merello <andrea.merello@gmail.com>
A special thanks goes to Realtek for their support !
/* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* Mostly extracted from the rtl8180-sa2400 driver for the
* in-kernel generic ieee802.11 stack.
/* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* Mostly extracted from the rtl8180-sa2400 driver for the
* in-kernel generic ieee802.11 stack.
******************************************************************************
Few modifications for Realtek's Wi-Fi drivers by
- Andrea Merello <andreamrl@tiscali.it>
+ Andrea Merello <andrea.merello@gmail.com>
A special thanks goes to Realtek for their support !
/*
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the
/*
This is part of rtl8180 OpenSource driver
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the official realtek driver
/*
This is part of rtl818x pci OpenSource driver - v 0.1
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public License)
Parts of this driver are based on the GPL part of the official
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, rtl8180_pci_id_tbl);
-MODULE_AUTHOR("Andrea Merello <andreamrl@tiscali.it>");
+MODULE_AUTHOR("Andrea Merello <andrea.merello@gmail.com>");
MODULE_DESCRIPTION("Linux driver for Realtek RTL8187SE WiFi cards");
module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
/*
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the
/*
This is part of the rtl8180-sa2400 driver
released under the GPL (See file COPYING for details).
- Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
This files contains programming code for the rtl8225
radio frontend.
/*
* This is part of the rtl8180-sa2400 driver
* released under the GPL (See file COPYING for details).
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* This files contains programming code for the rtl8225
* radio frontend.
This file contains wireless extension handlers.
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part
/*
This is part of rtl8180 OpenSource driver - v 0.3
- Copyright (C) Andrea Merello 2004 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the official realtek driver
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
* Copyright (c) 2004, Intel Corporation
*
* Modified for Realtek's wi-fi cards by Andrea Merello
- * <andreamrl@tiscali.it>
+ * <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
* Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved.
*
* Based on the r8180 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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.
******************************************************************************
Few modifications for Realtek's Wi-Fi drivers by
- Andrea Merello <andreamrl@tiscali.it>
+ Andrea Merello <andrea.merello@gmail.com>
A special thanks goes to Realtek for their support !
/* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* Mostly extracted from the rtl8180-sa2400 driver for the
* in-kernel generic ieee802.11 stack.
/* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* Mostly extracted from the rtl8180-sa2400 driver for the
* in-kernel generic ieee802.11 stack.
******************************************************************************
Few modifications for Realtek's Wi-Fi drivers by
- Andrea Merello <andreamrl@tiscali.it>
+ Andrea Merello <andrea.merello@gmail.com>
A special thanks goes to Realtek for their support !
-Andrea Merello <andreamrl@tiscali.it>
+Andrea Merello <andrea.merello@gmail.com>
* Copyright (c) 2004, Intel Corporation
*
* Modified for Realtek's wi-fi cards by Andrea Merello
- * <andreamrl@tiscali.it>
+ * <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
******************************************************************************
Few modifications for Realtek's Wi-Fi drivers by
- Andrea Merello <andreamrl@tiscali.it>
+ Andrea Merello <andrea.merello@gmail.com>
A special thanks goes to Realtek for their support !
/* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* Mostly extracted from the rtl8180-sa2400 driver for the
* in-kernel generic ieee802.11 stack.
/* IEEE 802.11 SoftMAC layer
- * Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
*
* Mostly extracted from the rtl8180-sa2400 driver for the
* in-kernel generic ieee802.11 stack.
******************************************************************************
Few modifications for Realtek's Wi-Fi drivers by
- Andrea Merello <andreamrl@tiscali.it>
+ Andrea Merello <andrea.merello@gmail.com>
A special thanks goes to Realtek for their support !
memory is addressed by 16 bits words.
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the
/*
This is part of rtl8187 OpenSource driver
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the official realtek driver
does not do anything useful.
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
*/
does not do anything useful.
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
*/
/*
This is part of the rtl8180-sa2400 driver
released under the GPL (See file COPYING for details).
- Copyright (c) 2005 Andrea Merello <andreamrl@tiscali.it>
+ Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
This files contains programming code for the rtl8256
radio frontend.
/*
* This is part of rtl8187 OpenSource driver.
- * Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ * Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
* Released under the terms of GPL (General Public Licence)
*
* Parts of this driver are based on the GPL part of the
* Linux device driver for RTL8192U
*
* Based on the r8187 driver, which is:
- * Copyright 2004-2005 Andrea Merello <andreamrl@tiscali.it>, et al.
+ * Copyright 2004-2005 Andrea Merello <andrea.merello@gmail.com>, et al.
* 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 is part of rtl8187 OpenSource driver.
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the
This file contains wireless extension handlers.
This is part of rtl8180 OpenSource driver.
- Copyright (C) Andrea Merello 2004-2005 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004-2005 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part
/*
This is part of rtl8180 OpenSource driver - v 0.3
- Copyright (C) Andrea Merello 2004 <andreamrl@tiscali.it>
+ Copyright (C) Andrea Merello 2004 <andrea.merello@gmail.com>
Released under the terms of GPL (General Public Licence)
Parts of this driver are based on the GPL part of the official realtek driver
/* Core-ID values. */
#define BCMA_CORE_OOB_ROUTER 0x367 /* Out of band */
#define BCMA_CORE_4706_CHIPCOMMON 0x500
+#define BCMA_CORE_PCIEG2 0x501
+#define BCMA_CORE_DMA 0x502
+#define BCMA_CORE_SDIO3 0x503
+#define BCMA_CORE_USB20 0x504
+#define BCMA_CORE_USB30 0x505
+#define BCMA_CORE_A9JTAG 0x506
+#define BCMA_CORE_DDR23 0x507
+#define BCMA_CORE_ROM 0x508
+#define BCMA_CORE_NAND 0x509
+#define BCMA_CORE_QSPI 0x50A
+#define BCMA_CORE_CHIPCOMMON_B 0x50B
#define BCMA_CORE_4706_SOC_RAM 0x50E
+#define BCMA_CORE_ARMCA9 0x510
#define BCMA_CORE_4706_MAC_GBIT 0x52D
#define BCMA_CORE_AMEMC 0x52E /* DDR1/2 memory controller core */
#define BCMA_CORE_ALTA 0x534 /* I2S core */
#define BCMA_PKG_ID_BCM5357 11
#define BCMA_CHIP_ID_BCM53572 53572
#define BCMA_PKG_ID_BCM47188 9
+#define BCMA_CHIP_ID_BCM4707 53010
+#define BCMA_PKG_ID_BCM4707 1
+#define BCMA_PKG_ID_BCM4708 2
+#define BCMA_PKG_ID_BCM4709 0
+#define BCMA_CHIP_ID_BCM53018 53018
/* Board types (on PCI usually equals to the subsystem dev id) */
/* BCM4313 */
#define BCMA_CORE_PCI_CFG_DEVCTRL 0xd8
+#define BCMA_CORE_PCI_
+
+/* MDIO devices (SERDES modules) */
+#define BCMA_CORE_PCI_MDIO_IEEE0 0x000
+#define BCMA_CORE_PCI_MDIO_IEEE1 0x001
+#define BCMA_CORE_PCI_MDIO_BLK0 0x800
+#define BCMA_CORE_PCI_MDIO_BLK1 0x801
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT0 0x16
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT1 0x17
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT2 0x18
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT3 0x19
+#define BCMA_CORE_PCI_MDIO_BLK1_MGMT4 0x1A
+#define BCMA_CORE_PCI_MDIO_BLK2 0x802
+#define BCMA_CORE_PCI_MDIO_BLK3 0x803
+#define BCMA_CORE_PCI_MDIO_BLK4 0x804
+#define BCMA_CORE_PCI_MDIO_TXPLL 0x808 /* TXPLL register block idx */
+#define BCMA_CORE_PCI_MDIO_TXCTRL0 0x820
+#define BCMA_CORE_PCI_MDIO_SERDESID 0x831
+#define BCMA_CORE_PCI_MDIO_RXCTRL0 0x840
+
/* PCIE Root Capability Register bits (Host mode only) */
#define BCMA_CORE_PCI_RC_CRS_VISIBILITY 0x0001
struct bcma_drv_pci;
+struct bcma_bus;
#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE
struct bcma_drv_pci_host {
extern void bcma_core_pci_init(struct bcma_drv_pci *pc);
extern int bcma_core_pci_irq_ctl(struct bcma_drv_pci *pc,
struct bcma_device *core, bool enable);
-extern void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend);
+extern void bcma_core_pci_up(struct bcma_bus *bus);
+extern void bcma_core_pci_down(struct bcma_bus *bus);
extern int bcma_core_pci_pcibios_map_irq(const struct pci_dev *dev);
extern int bcma_core_pci_plat_dev_init(struct pci_dev *dev);
WLAN_EID_OPMODE_NOTIF = 199,
WLAN_EID_WIDE_BW_CHANNEL_SWITCH = 194,
WLAN_EID_CHANNEL_SWITCH_WRAPPER = 196,
+ WLAN_EID_EXTENDED_BSS_LOAD = 193,
+ WLAN_EID_VHT_TX_POWER_ENVELOPE = 195,
+ WLAN_EID_AID = 197,
+ WLAN_EID_QUIET_CHANNEL = 198,
/* 802.11ad */
WLAN_EID_NON_TX_BSSID_CAP = 83,
WLAN_TDLS_DISCOVERY_REQUEST = 10,
};
+/* Interworking capabilities are set in 7th bit of 4th byte of the
+ * @WLAN_EID_EXT_CAPABILITY information element
+ */
+#define WLAN_EXT_CAPA4_INTERWORKING_ENABLED BIT(7)
+
/*
* TDLS capabililites to be enabled in the 5th byte of the
* @WLAN_EID_EXT_CAPABILITY information element
return !!(tim->virtual_map[index] & mask);
}
+/* convert time units */
+#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
+#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
+
#endif /* LINUX_IEEE80211_H */
* Set this to true if the SDIO host controller has higher align requirement
* than 32 bytes for each scatterlist item.
*
+ * sd_head_align: alignment requirement for start of data buffer
+ *
+ * sd_sgentry_align: length alignment requirement for each sg entry
+ *
* power_on: This function is called by the brcmfmac when the module gets
* loaded. This can be particularly useful for low power devices. The platform
* spcific routine may for example decide to power up the complete device.
unsigned int oob_irq_nr;
unsigned long oob_irq_flags;
bool broken_sg_support;
+ unsigned short sd_head_align;
+ unsigned short sd_sgentry_align;
void (*power_on)(void);
void (*power_off)(void);
void (*reset)(void);
*/
#define BT_CHANNEL_POLICY_AMP_PREFERRED 2
+#define BT_VOICE 11
+struct bt_voice {
+ __u16 setting;
+};
+
+#define BT_VOICE_TRANSPARENT 0x0003
+#define BT_VOICE_CVSD_16BIT 0x0060
+
__printf(1, 2)
int bt_info(const char *fmt, ...);
__printf(1, 2)
#define LMP_CVSD 0x01
#define LMP_PSCHEME 0x02
#define LMP_PCONTROL 0x04
+#define LMP_TRANSPARENT 0x08
#define LMP_RSSI_INQ 0x40
#define LMP_ESCO 0x80
#define HCI_AT_GENERAL_BONDING 0x04
#define HCI_AT_GENERAL_BONDING_MITM 0x05
+/* I/O capabilities */
+#define HCI_IO_DISPLAY_ONLY 0x00
+#define HCI_IO_DISPLAY_YESNO 0x01
+#define HCI_IO_KEYBOARD_ONLY 0x02
+#define HCI_IO_NO_INPUT_OUTPUT 0x03
+
/* Link Key types */
#define HCI_LK_COMBINATION 0x00
#define HCI_LK_LOCAL_UNIT 0x01
__u32 passkey_notify;
__u8 passkey_entered;
__u16 disc_timeout;
+ __u16 setting;
unsigned long flags;
__u8 remote_cap;
}
void hci_disconnect(struct hci_conn *conn, __u8 reason);
-void hci_setup_sync(struct hci_conn *conn, __u16 handle);
+bool hci_setup_sync(struct hci_conn *conn, __u16 handle);
void hci_sco_setup(struct hci_conn *conn, __u8 status);
struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst,
__u8 dst_type, __u8 sec_level, __u8 auth_type);
+struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
+ __u16 setting);
int hci_conn_check_link_mode(struct hci_conn *conn);
int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level);
int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
#define lmp_lsto_capable(dev) ((dev)->features[0][7] & LMP_LSTO)
#define lmp_inq_tx_pwr_capable(dev) ((dev)->features[0][7] & LMP_INQ_TX_PWR)
#define lmp_ext_feat_capable(dev) ((dev)->features[0][7] & LMP_EXTFEATURES)
+#define lmp_transp_capable(dev) ((dev)->features[0][2] & LMP_TRANSPARENT)
/* ----- Extended LMP capabilities ----- */
#define lmp_host_ssp_capable(dev) ((dev)->features[1][0] & LMP_HOST_SSP)
u8 bdaddr_to_le(u8 bdaddr_type);
+#define SCO_AIRMODE_MASK 0x0003
+#define SCO_AIRMODE_CVSD 0x0000
+#define SCO_AIRMODE_TRANSP 0x0003
+
#endif /* __HCI_CORE_H */
struct sco_pinfo {
struct bt_sock bt;
__u32 flags;
+ __u16 setting;
struct sco_conn *conn;
};
return 0;
}
+/**
+ * ieee80211_chandef_max_power - maximum transmission power for the chandef
+ *
+ * In some regulations, the transmit power may depend on the configured channel
+ * bandwidth which may be defined as dBm/MHz. This function returns the actual
+ * max_power for non-standard (20 MHz) channels.
+ *
+ * @chandef: channel definition for the channel
+ *
+ * Returns: maximum allowed transmission power in dBm for the chandef
+ */
+static inline int
+ieee80211_chandef_max_power(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return min(chandef->chan->max_reg_power - 6,
+ chandef->chan->max_power);
+ case NL80211_CHAN_WIDTH_10:
+ return min(chandef->chan->max_reg_power - 3,
+ chandef->chan->max_power);
+ default:
+ break;
+ }
+ return chandef->chan->max_power;
+}
+
/**
* enum survey_info_flags - survey information flags
*
* @channel: the channel this survey record reports, mandatory
* @filled: bitflag of flags from &enum survey_info_flags
* @noise: channel noise in dBm. This and all following fields are
- * optional
+ * optional
* @channel_time: amount of time in ms the radio spent on the channel
* @channel_time_busy: amount of time the primary channel was sensed busy
* @channel_time_ext_busy: amount of time the extension channel was sensed busy
/**
* struct cfg80211_beacon_data - beacon data
* @head: head portion of beacon (before TIM IE)
- * or %NULL if not changed
+ * or %NULL if not changed
* @tail: tail portion of beacon (after TIM IE)
- * or %NULL if not changed
+ * or %NULL if not changed
* @head_len: length of @head
* @tail_len: length of @tail
* @beacon_ies: extra information element(s) to add into Beacon frames or %NULL
bool radar_required;
};
+/**
+ * struct cfg80211_csa_settings - channel switch settings
+ *
+ * Used for channel switch
+ *
+ * @chandef: defines the channel to use after the switch
+ * @beacon_csa: beacon data while performing the switch
+ * @counter_offset_beacon: offset for the counter within the beacon (tail)
+ * @counter_offset_presp: offset for the counter within the probe response
+ * @beacon_after: beacon data to be used on the new channel
+ * @radar_required: whether radar detection is required on the new channel
+ * @block_tx: whether transmissions should be blocked while changing
+ * @count: number of beacons until switch
+ */
+struct cfg80211_csa_settings {
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_beacon_data beacon_csa;
+ u16 counter_offset_beacon, counter_offset_presp;
+ struct cfg80211_beacon_data beacon_after;
+ bool radar_required;
+ bool block_tx;
+ u8 count;
+};
+
/**
* enum station_parameters_apply_mask - station parameter values to apply
* @STATION_PARAM_APPLY_UAPSD: apply new uAPSD parameters (uapsd_queues, max_sp)
* @STATION_INFO_PLINK_STATE: @plink_state filled
* @STATION_INFO_SIGNAL: @signal filled
* @STATION_INFO_TX_BITRATE: @txrate fields are filled
- * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
+ * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs)
* @STATION_INFO_RX_PACKETS: @rx_packets filled with 32-bit value
* @STATION_INFO_TX_PACKETS: @tx_packets filled with 32-bit value
* @STATION_INFO_TX_RETRIES: @tx_retries filled
* @n_ssids: number of SSIDs
* @channels: channels to scan on.
* @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
* @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets
* @flags: bit field of flags controlling operation
struct cfg80211_ssid *ssids;
int n_ssids;
u32 n_channels;
+ enum nl80211_bss_scan_width scan_width;
const u8 *ie;
size_t ie_len;
u32 flags;
* @ssids: SSIDs to scan for (passed in the probe_reqs in active scans)
* @n_ssids: number of SSIDs
* @n_channels: total number of channels to scan
+ * @scan_width: channel width for scanning
* @interval: interval between each scheduled scan cycle
* @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets
struct cfg80211_ssid *ssids;
int n_ssids;
u32 n_channels;
+ enum nl80211_bss_scan_width scan_width;
u32 interval;
const u8 *ie;
size_t ie_len;
* for use in scan results and similar.
*
* @channel: channel this BSS is on
+ * @scan_width: width of the control channel
* @bssid: BSSID of the BSS
* @beacon_interval: the beacon interval as from the frame
* @capability: the capability field in host byte order
*/
struct cfg80211_bss {
struct ieee80211_channel *channel;
+ enum nl80211_bss_scan_width scan_width;
const struct cfg80211_bss_ies __rcu *ies;
const struct cfg80211_bss_ies __rcu *beacon_ies;
* @prev_bssid: previous BSSID, if not %NULL use reassociate frame
* @flags: See &enum cfg80211_assoc_req_flags
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
- * will be used in ht_capa. Un-supported values will be ignored.
+ * will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
* @vht_capa: VHT capability override
* @vht_capa_mask: VHT capability mask indicating which fields to use
* user space. Otherwise, port is marked authorized by default.
* @basic_rates: bitmap of basic rates to use when creating the IBSS
* @mcast_rate: per-band multicast rate index + 1 (0: disabled)
+ * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
+ * will be used in ht_capa. Un-supported values will be ignored.
+ * @ht_capa_mask: The bits of ht_capa which are to be used.
*/
struct cfg80211_ibss_params {
u8 *ssid;
bool privacy;
bool control_port;
int mcast_rate[IEEE80211_NUM_BANDS];
+ struct ieee80211_ht_cap ht_capa;
+ struct ieee80211_ht_cap ht_capa_mask;
};
/**
* @key: WEP key for shared key authentication
* @flags: See &enum cfg80211_assoc_req_flags
* @bg_scan_period: Background scan period in seconds
- * or -1 to indicate that default value is to be used.
+ * or -1 to indicate that default value is to be used.
* @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask
- * will be used in ht_capa. Un-supported values will be ignored.
+ * will be used in ht_capa. Un-supported values will be ignored.
* @ht_capa_mask: The bits of ht_capa which are to be used.
* @vht_capa: VHT Capability overrides
* @vht_capa_mask: The bits of vht_capa which are to be used.
};
/**
- * struct cfg80211_wowlan_trig_pkt_pattern - packet pattern
+ * struct cfg80211_pkt_pattern - packet pattern
* @mask: bitmask where to match pattern and where to ignore bytes,
* one bit per byte, in same format as nl80211
* @pattern: bytes to match where bitmask is 1
* Internal note: @mask and @pattern are allocated in one chunk of
* memory, free @mask only!
*/
-struct cfg80211_wowlan_trig_pkt_pattern {
+struct cfg80211_pkt_pattern {
u8 *mask, *pattern;
int pattern_len;
int pkt_offset;
bool any, disconnect, magic_pkt, gtk_rekey_failure,
eap_identity_req, four_way_handshake,
rfkill_release;
- struct cfg80211_wowlan_trig_pkt_pattern *patterns;
+ struct cfg80211_pkt_pattern *patterns;
struct cfg80211_wowlan_tcp *tcp;
int n_patterns;
};
+/**
+ * struct cfg80211_coalesce_rules - Coalesce rule parameters
+ *
+ * This structure defines coalesce rule for the device.
+ * @delay: maximum coalescing delay in msecs.
+ * @condition: condition for packet coalescence.
+ * see &enum nl80211_coalesce_condition.
+ * @patterns: array of packet patterns
+ * @n_patterns: number of patterns
+ */
+struct cfg80211_coalesce_rules {
+ int delay;
+ enum nl80211_coalesce_condition condition;
+ struct cfg80211_pkt_pattern *patterns;
+ int n_patterns;
+};
+
+/**
+ * struct cfg80211_coalesce - Packet coalescing settings
+ *
+ * This structure defines coalescing settings.
+ * @rules: array of coalesce rules
+ * @n_rules: number of rules
+ */
+struct cfg80211_coalesce {
+ struct cfg80211_coalesce_rules *rules;
+ int n_rules;
+};
+
/**
* struct cfg80211_wowlan_wakeup - wakeup report
* @disconnect: woke up by getting disconnected
* @mgmt_tx_cancel_wait: Cancel the wait time from transmitting a management
* frame on another channel
*
- * @testmode_cmd: run a test mode command
+ * @testmode_cmd: run a test mode command; @wdev may be %NULL
* @testmode_dump: Implement a test mode dump. The cb->args[2] and up may be
* used by the function, but 0 and 1 must not be touched. Additionally,
* return error codes other than -ENOBUFS and -ENOENT will terminate the
* driver can take the most appropriate actions.
* @crit_proto_stop: Indicates critical protocol no longer needs increased link
* reliability. This operation can not fail.
+ * @set_coalesce: Set coalesce parameters.
+ *
+ * @channel_switch: initiate channel-switch procedure (with CSA)
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
void (*rfkill_poll)(struct wiphy *wiphy);
#ifdef CONFIG_NL80211_TESTMODE
- int (*testmode_cmd)(struct wiphy *wiphy, void *data, int len);
+ int (*testmode_cmd)(struct wiphy *wiphy, struct wireless_dev *wdev,
+ void *data, int len);
int (*testmode_dump)(struct wiphy *wiphy, struct sk_buff *skb,
struct netlink_callback *cb,
void *data, int len);
u16 duration);
void (*crit_proto_stop)(struct wiphy *wiphy,
struct wireless_dev *wdev);
+ int (*set_coalesce)(struct wiphy *wiphy,
+ struct cfg80211_coalesce *coalesce);
+
+ int (*channel_switch)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params);
};
/*
* @WIPHY_FLAG_OFFCHAN_TX: Device supports direct off-channel TX.
* @WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL: Device supports remain-on-channel call.
* @WIPHY_FLAG_SUPPORTS_5_10_MHZ: Device supports 5 MHz and 10 MHz channels.
+ * @WIPHY_FLAG_HAS_CHANNEL_SWITCH: Device supports channel switch in
+ * beaconing mode (AP, IBSS, Mesh, ...).
*/
enum wiphy_flags {
WIPHY_FLAG_CUSTOM_REGULATORY = BIT(0),
WIPHY_FLAG_OFFCHAN_TX = BIT(20),
WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL = BIT(21),
WIPHY_FLAG_SUPPORTS_5_10_MHZ = BIT(22),
+ WIPHY_FLAG_HAS_CHANNEL_SWITCH = BIT(23),
};
/**
const struct wiphy_wowlan_tcp_support *tcp;
};
+/**
+ * struct wiphy_coalesce_support - coalesce support data
+ * @n_rules: maximum number of coalesce rules
+ * @max_delay: maximum supported coalescing delay in msecs
+ * @n_patterns: number of supported patterns in a rule
+ * (see nl80211.h for the pattern definition)
+ * @pattern_max_len: maximum length of each pattern
+ * @pattern_min_len: minimum length of each pattern
+ * @max_pkt_offset: maximum Rx packet offset
+ */
+struct wiphy_coalesce_support {
+ int n_rules;
+ int max_delay;
+ int n_patterns;
+ int pattern_max_len;
+ int pattern_min_len;
+ int max_pkt_offset;
+};
+
/**
* struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback,
* 802.11-2012 8.4.2.29 for the defined fields.
* @extended_capabilities_mask: mask of the valid values
* @extended_capabilities_len: length of the extended capabilities
+ * @coalesce: packet coalescing support information
*/
struct wiphy {
/* assign these fields before you register the wiphy */
const struct iw_handler_def *wext;
#endif
+ const struct wiphy_coalesce_support *coalesce;
+
char priv[0] __aligned(NETDEV_ALIGN);
};
/**
* ieee80211_mandatory_rates - get mandatory rates for a given band
* @sband: the band to look for rates in
+ * @scan_width: width of the control channel
*
* This function returns a bitmap of the mandatory rates for the given
* band, bits are set according to the rate position in the bitrates array.
*/
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband);
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+ enum nl80211_bss_scan_width scan_width);
/*
* Radiotap parsing functions -- for controlled injection support
void cfg80211_sched_scan_stopped(struct wiphy *wiphy);
/**
- * cfg80211_inform_bss_frame - inform cfg80211 of a received BSS frame
+ * cfg80211_inform_bss_width_frame - inform cfg80211 of a received BSS frame
*
* @wiphy: the wiphy reporting the BSS
* @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
* @mgmt: the management frame (probe response or beacon)
* @len: length of the management frame
* @signal: the signal strength, type depends on the wiphy's signal_type
* Or %NULL on error.
*/
struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ enum nl80211_bss_scan_width scan_width,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss_frame(struct wiphy *wiphy,
struct ieee80211_channel *channel,
struct ieee80211_mgmt *mgmt, size_t len,
- s32 signal, gfp_t gfp);
+ s32 signal, gfp_t gfp)
+{
+ return cfg80211_inform_bss_width_frame(wiphy, channel,
+ NL80211_BSS_CHAN_WIDTH_20,
+ mgmt, len, signal, gfp);
+}
/**
* cfg80211_inform_bss - inform cfg80211 of a new BSS
*
* @wiphy: the wiphy reporting the BSS
* @channel: The channel the frame was received on
+ * @scan_width: width of the control channel
* @bssid: the BSSID of the BSS
* @tsf: the TSF sent by the peer in the beacon/probe response (or 0)
* @capability: the capability field sent by the peer
* Or %NULL on error.
*/
struct cfg80211_bss * __must_check
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ enum nl80211_bss_scan_width scan_width,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ s32 signal, gfp_t gfp);
+
+static inline struct cfg80211_bss * __must_check
cfg80211_inform_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
const u8 *bssid, u64 tsf, u16 capability,
u16 beacon_interval, const u8 *ie, size_t ielen,
- s32 signal, gfp_t gfp);
+ s32 signal, gfp_t gfp)
+{
+ return cfg80211_inform_bss_width(wiphy, channel,
+ NL80211_BSS_CHAN_WIDTH_20,
+ bssid, tsf, capability,
+ beacon_interval, ie, ielen, signal,
+ gfp);
+}
struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
struct ieee80211_channel *channel,
*/
void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *bss);
+static inline enum nl80211_bss_scan_width
+cfg80211_chandef_to_scan_width(const struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return NL80211_BSS_CHAN_WIDTH_5;
+ case NL80211_CHAN_WIDTH_10:
+ return NL80211_BSS_CHAN_WIDTH_10;
+ default:
+ return NL80211_BSS_CHAN_WIDTH_20;
+ }
+}
+
/**
* cfg80211_rx_mlme_mgmt - notification of processed MLME management frame
* @dev: network device
#define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */
#define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */
#define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GSM 0x1000 /* GSM (900 MHz) */
+#define IEEE80211_CHAN_STURBO 0x2000 /* Static Turbo */
+#define IEEE80211_CHAN_HALF 0x4000 /* Half channel (10 MHz wide) */
+#define IEEE80211_CHAN_QUARTER 0x8000 /* Quarter channel (5 MHz wide) */
/* For IEEE80211_RADIOTAP_FLAGS */
#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received
* @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed
* @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed
* @IEEE80211_CHANCTX_CHANGE_RADAR: radar detection flag changed
+ * @IEEE80211_CHANCTX_CHANGE_CHANNEL: switched to another operating channel,
+ * this is used only with channel switching with CSA
*/
enum ieee80211_chanctx_change {
IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0),
IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1),
IEEE80211_CHANCTX_CHANGE_RADAR = BIT(2),
+ IEEE80211_CHANCTX_CHANGE_CHANNEL = BIT(3),
};
/**
};
/**
- * enum mac80211_tx_control_flags - flags to describe transmission information/status
+ * enum mac80211_tx_info_flags - flags to describe transmission information/status
*
* These flags are used with the @flags member of &ieee80211_tx_info.
*
* Note: If you have to add new flags to the enumeration, then don't
* forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary.
*/
-enum mac80211_tx_control_flags {
+enum mac80211_tx_info_flags {
IEEE80211_TX_CTL_REQ_TX_STATUS = BIT(0),
IEEE80211_TX_CTL_ASSIGN_SEQ = BIT(1),
IEEE80211_TX_CTL_NO_ACK = BIT(2),
#define IEEE80211_TX_CTL_STBC_SHIFT 23
+/**
+ * enum mac80211_tx_control_flags - flags to describe transmit control
+ *
+ * @IEEE80211_TX_CTRL_PORT_CTRL_PROTO: this frame is a port control
+ * protocol frame (e.g. EAP)
+ *
+ * These flags are used in tx_info->control.flags.
+ */
+enum mac80211_tx_control_flags {
+ IEEE80211_TX_CTRL_PORT_CTRL_PROTO = BIT(0),
+};
+
/*
* This definition is used as a mask to clear all temporary flags, which are
* set by the tx handlers for each transmission attempt by the mac80211 stack.
/* NB: vif can be NULL for injected frames */
struct ieee80211_vif *vif;
struct ieee80211_key_conf *hw_key;
- /* 8 bytes free */
+ u32 flags;
+ /* 4 bytes free */
} control;
struct {
struct ieee80211_tx_rate rates[IEEE80211_TX_MAX_RATES];
* @RX_FLAG_AMPDU_DELIM_CRC_KNOWN: The delimiter CRC field is known (the CRC
* is stored in the @ampdu_delimiter_crc field)
* @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
+ * @RX_FLAG_10MHZ: 10 MHz (half channel) was used
+ * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
RX_FLAG_80P80MHZ = BIT(24),
RX_FLAG_160MHZ = BIT(25),
RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
+ RX_FLAG_10MHZ = BIT(28),
+ RX_FLAG_5MHZ = BIT(29),
};
#define RX_FLAG_STBC_SHIFT 26
* @radar_enabled: whether radar detection is enabled
*
* @long_frame_max_tx_count: Maximum number of transmissions for a "long" frame
- * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
- * but actually means the number of transmissions not the number of retries
+ * (a frame not RTS protected), called "dot11LongRetryLimit" in 802.11,
+ * but actually means the number of transmissions not the number of retries
* @short_frame_max_tx_count: Maximum number of transmissions for a "short"
- * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
- * number of transmissions not the number of retries
+ * frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
+ * number of transmissions not the number of retries
*
* @smps_mode: spatial multiplexing powersave mode; note that
* %IEEE80211_SMPS_STATIC is used when the device is not
* @addr: address of this interface
* @p2p: indicates whether this AP or STA interface is a p2p
* interface, i.e. a GO or p2p-sta respectively
+ * @csa_active: marks whether a channel switch is going on
* @driver_flags: flags/capabilities the driver has for this interface,
* these need to be set (or cleared) when the interface is added
* or, if supported by the driver, the interface type is changed
* be off when it is %NULL there can still be races and packets could be
* processed after it switches back to %NULL.
* @debugfs_dir: debugfs dentry, can be used by drivers to create own per
- * interface debug files. Note that it will be NULL for the virtual
+ * interface debug files. Note that it will be NULL for the virtual
* monitor interface (if that is requested.)
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void *).
struct ieee80211_bss_conf bss_conf;
u8 addr[ETH_ALEN];
bool p2p;
+ bool csa_active;
u8 cab_queue;
u8 hw_queue[IEEE80211_NUM_ACS];
* the stack.
*
* @IEEE80211_HW_CONNECTION_MONITOR:
- * The hardware performs its own connection monitoring, including
- * periodic keep-alives to the AP and probing the AP on beacon loss.
- * When this flag is set, signaling beacon-loss will cause an immediate
- * change to disassociated state.
+ * The hardware performs its own connection monitoring, including
+ * periodic keep-alives to the AP and probing the AP on beacon loss.
+ * When this flag is set, signaling beacon-loss will cause an immediate
+ * change to disassociated state.
*
* @IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC:
* This device needs to get data from beacon before association (i.e.
* @channel_change_time: time (in microseconds) it takes to change channels.
*
* @max_signal: Maximum value for signal (rssi) in RX information, used
- * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
+ * only when @IEEE80211_HW_SIGNAL_UNSPEC or @IEEE80211_HW_SIGNAL_DB
*
* @max_listen_interval: max listen interval in units of beacon interval
- * that HW supports
+ * that HW supports
*
* @queues: number of available hardware transmit queues for
* data packets. WMM/QoS requires at least four, these
* The callback can sleep.
*
* @set_tsf: Set the TSF timer to the specified value in the firmware/hardware.
- * Currently, this is only used for IBSS mode debugging. Is not a
+ * Currently, this is only used for IBSS mode debugging. Is not a
* required function.
* The callback can sleep.
*
* in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout
* accordingly. This callback is not required and may sleep.
*
- * @testmode_cmd: Implement a cfg80211 test mode command.
- * The callback can sleep.
+ * @testmode_cmd: Implement a cfg80211 test mode command. The passed @vif may
+ * be %NULL. The callback can sleep.
* @testmode_dump: Implement a cfg80211 test mode dump. The callback can sleep.
*
* @flush: Flush all pending frames from the hardware queue, making sure
* @ipv6_addr_change: IPv6 address assignment on the given interface changed.
* Currently, this is only called for managed or P2P client interfaces.
* This callback is optional; it must not sleep.
+ *
+ * @channel_switch_beacon: Starts a channel switch to a new channel.
+ * Beacons are modified to include CSA or ECSA IEs before calling this
+ * function. The corresponding count fields in these IEs must be
+ * decremented, and when they reach zero the driver must call
+ * ieee80211_csa_finish(). Drivers which use ieee80211_beacon_get()
+ * get the csa counter decremented by mac80211, but must check if it is
+ * zero using ieee80211_csa_is_complete() after the beacon has been
+ * transmitted and then call ieee80211_csa_finish().
+ *
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
void (*rfkill_poll)(struct ieee80211_hw *hw);
void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class);
#ifdef CONFIG_NL80211_TESTMODE
- int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len);
+ int (*testmode_cmd)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ void *data, int len);
int (*testmode_dump)(struct ieee80211_hw *hw, struct sk_buff *skb,
struct netlink_callback *cb,
void *data, int len);
struct ieee80211_vif *vif,
struct inet6_dev *idev);
#endif
+ void (*channel_switch_beacon)(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_chan_def *chandef);
};
/**
return ieee80211_beacon_get_tim(hw, vif, NULL, NULL);
}
+/**
+ * ieee80211_csa_finish - notify mac80211 about channel switch
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * After a channel switch announcement was scheduled and the counter in this
+ * announcement hit zero, this function must be called by the driver to
+ * notify mac80211 that the channel can be changed.
+ */
+void ieee80211_csa_finish(struct ieee80211_vif *vif);
+
+/**
+ * ieee80211_csa_is_complete - find out if counters reached zero
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ *
+ * This function returns whether the channel switch counters reached zero.
+ */
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif);
+
+
/**
* ieee80211_proberesp_get - retrieve a Probe Response template
* @hw: pointer obtained from ieee80211_alloc_hw().
void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf,
int tid, struct ieee80211_key_seq *seq);
+/**
+ * ieee80211_set_key_tx_seq - set key TX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current TX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and the
+ * device may have transmitted frames using the PTK, e.g. replies to
+ * ARP requests.
+ *
+ * Note that this function may only be called when no TX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
+ struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_set_key_rx_seq - set key RX sequence counter
+ *
+ * @keyconf: the parameter passed with the set key
+ * @tid: The TID, or -1 for the management frame value (CCMP only);
+ * the value on TID 0 is also used for non-QoS frames. For
+ * CMAC, only TID 0 is valid.
+ * @seq: new sequence data
+ *
+ * This function allows a driver to set the current RX IV/PNs for the
+ * given key. This is useful when resuming from WoWLAN sleep and GTK
+ * rekey may have been done while suspended. It should not be called
+ * if IV checking is done by the device and not by mac80211.
+ *
+ * Note that this function may only be called when no RX processing
+ * can be done concurrently.
+ */
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+ int tid, struct ieee80211_key_seq *seq);
+
+/**
+ * ieee80211_remove_key - remove the given key
+ * @keyconf: the parameter passed with the set key
+ *
+ * Remove the given key. If the key was uploaded to the hardware at the
+ * time this function is called, it is not deleted in the hardware but
+ * instead assumed to have been removed already.
+ *
+ * Note that due to locking considerations this function can (currently)
+ * only be called during key iteration (ieee80211_iter_keys().)
+ */
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf);
+
+/**
+ * ieee80211_gtk_rekey_add - add a GTK key from rekeying during WoWLAN
+ * @vif: the virtual interface to add the key on
+ * @keyconf: new key data
+ *
+ * When GTK rekeying was done while the system was suspended, (a) new
+ * key(s) will be available. These will be needed by mac80211 for proper
+ * RX processing, so this function allows setting them.
+ *
+ * The function returns the newly allocated key structure, which will
+ * have similar contents to the passed key configuration but point to
+ * mac80211-owned memory. In case of errors, the function returns an
+ * ERR_PTR(), use IS_ERR() etc.
+ *
+ * Note that this function assumes the key isn't added to hardware
+ * acceleration, so no TX will be done with the key. Since it's a GTK
+ * on managed (station) networks, this is true anyway. If the driver
+ * calls this function from the resume callback and subsequently uses
+ * the return code 1 to reconfigure the device, this key will be part
+ * of the reconfiguration.
+ *
+ * Note that the driver should also call ieee80211_set_key_rx_seq()
+ * for the new key for each TID to set up sequence counters properly.
+ *
+ * IMPORTANT: If this replaces a key that is present in the hardware,
+ * then it will attempt to remove it during this call. In many cases
+ * this isn't what you want, so call ieee80211_remove_key() first for
+ * the key that's being replaced.
+ */
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf);
+
/**
* ieee80211_gtk_rekey_notify - notify userspace supplicant of rekeying
* @vif: virtual interface the rekeying was done on
void *(*alloc_sta)(void *priv, struct ieee80211_sta *sta, gfp_t gfp);
void (*rate_init)(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta);
void (*rate_update)(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta,
u32 changed);
void (*free_sta)(void *priv, struct ieee80211_sta *sta,
u8 *gt, u8 gt_len);
u8 *nfc_get_local_general_bytes(struct nfc_dev *dev, size_t *gb_len);
+int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+ u32 result);
+
int nfc_targets_found(struct nfc_dev *dev,
struct nfc_target *targets, int ntargets);
int nfc_target_lost(struct nfc_dev *dev, u32 target_idx);
* @NFC_CMD_DISABLE_SE: Disable the physical link to a specific secure element.
* @NFC_CMD_FW_DOWNLOAD: Request to Load/flash firmware, or event to inform
* that some firmware was loaded
+ * @NFC_EVENT_SE_ADDED: Event emitted when a new secure element is discovered.
+ * This typically will be sent whenever a new NFC controller with either
+ * an embedded SE or an UICC one connected to it through SWP.
+ * @NFC_EVENT_SE_REMOVED: Event emitted when a secure element is removed from
+ * the system, as a consequence of e.g. an NFC controller being unplugged.
+ * @NFC_EVENT_SE_CONNECTIVITY: This event is emitted whenever a secure element
+ * is requesting connectivity access. For example a UICC SE may need to
+ * talk with a sleeping modem and will notify this need by sending this
+ * event. It is then up to userspace to decide if it will wake the modem
+ * up or not.
+ * @NFC_EVENT_SE_TRANSACTION: This event is sent when an application running on
+ * a specific SE notifies us about the end of a transaction. The parameter
+ * for this event is the application ID (AID).
+ * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller.
*/
enum nfc_commands {
NFC_CMD_UNSPEC,
NFC_CMD_FW_DOWNLOAD,
NFC_EVENT_SE_ADDED,
NFC_EVENT_SE_REMOVED,
+ NFC_EVENT_SE_CONNECTIVITY,
+ NFC_EVENT_SE_TRANSACTION,
+ NFC_CMD_GET_SE,
/* private: internal use only */
__NFC_CMD_AFTER_LAST
};
* @NFC_ATTR_FIRMWARE_NAME: Free format firmware version
* @NFC_ATTR_SE_INDEX: Secure element index
* @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED)
+ * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status
*/
enum nfc_attrs {
NFC_ATTR_UNSPEC,
NFC_ATTR_FIRMWARE_NAME,
NFC_ATTR_SE_INDEX,
NFC_ATTR_SE_TYPE,
+ NFC_ATTR_SE_AID,
+ NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS,
/* private: internal use only */
__NFC_ATTR_AFTER_LAST
};
* interfaces that a given device supports.
*/
+/**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
* return back to normal.
*
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ * the new channel information (Channel Switch Announcement - CSA)
+ * in the beacon for some time (as defined in the
+ * %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ * new channel. Userspace provides the new channel information (using
+ * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ * width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ * other station that transmission must be blocked until the channel
+ * switch is complete.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
NL80211_CMD_CRIT_PROTOCOL_START,
NL80211_CMD_CRIT_PROTOCOL_STOP,
+ NL80211_CMD_GET_COALESCE,
+ NL80211_CMD_SET_COALESCE,
+
+ NL80211_CMD_CHANNEL_SWITCH,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
* allowed to be used with the first @NL80211_CMD_SET_STATION command to
* update a TDLS peer STA entry.
*
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ * until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ * must be blocked on the current channel (before the channel switch
+ * operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ * for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
+ * field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
+ * field in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
NL80211_ATTR_PEER_AID,
+ NL80211_ATTR_COALESCE_RULE,
+
+ NL80211_ATTR_CH_SWITCH_COUNT,
+ NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+ NL80211_ATTR_CSA_IES,
+ NL80211_ATTR_CSA_C_OFF_BEACON,
+ NL80211_ATTR_CSA_C_OFF_PRESP,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
NL80211_CHAN_WIDTH_10,
};
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+ NL80211_BSS_CHAN_WIDTH_20,
+ NL80211_BSS_CHAN_WIDTH_10,
+ NL80211_BSS_CHAN_WIDTH_5,
+};
+
/**
* enum nl80211_bss - netlink attributes for a BSS
*
* @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
* elements from a Beacon frame (bin); not present if no Beacon frame has
* yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ * (u32, enum nl80211_bss_scan_width)
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
NL80211_BSS_STATUS,
NL80211_BSS_SEEN_MS_AGO,
NL80211_BSS_BEACON_IES,
+ NL80211_BSS_CHAN_WIDTH,
/* keep last */
__NL80211_BSS_AFTER_LAST,
};
/**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
* a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
* a bit for each byte in the pattern. The lowest-order bit corresponds
* to the first byte of the pattern, but the bytes of the pattern are
* in a little-endian-like format, i.e. the 9th byte of the pattern
* Note that the pattern matching is done as though frames were not
* 802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
* first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
* these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
*/
-enum nl80211_wowlan_packet_pattern_attr {
- __NL80211_WOWLAN_PKTPAT_INVALID,
- NL80211_WOWLAN_PKTPAT_MASK,
- NL80211_WOWLAN_PKTPAT_PATTERN,
- NL80211_WOWLAN_PKTPAT_OFFSET,
+enum nl80211_packet_pattern_attr {
+ __NL80211_PKTPAT_INVALID,
+ NL80211_PKTPAT_MASK,
+ NL80211_PKTPAT_PATTERN,
+ NL80211_PKTPAT_OFFSET,
- NUM_NL80211_WOWLAN_PKTPAT,
- MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+ NUM_NL80211_PKTPAT,
+ MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
};
/**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
* @max_patterns: maximum number of patterns supported
* @min_pattern_len: minimum length of each pattern
* @max_pattern_len: maximum length of each pattern
* @max_pkt_offset: maximum Rx packet offset
*
* This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
*/
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
__u32 max_patterns;
__u32 min_pattern_len;
__u32 max_pattern_len;
__u32 max_pkt_offset;
} __attribute__((packed));
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
/**
* enum nl80211_wowlan_triggers - WoWLAN trigger definitions
* @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
* pattern matching is done after the packet is converted to the MSDU.
*
* In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- * carrying a &struct nl80211_wowlan_pattern_support.
+ * carrying a &struct nl80211_pattern_support.
*
* When reporting wakeup. it is a u32 attribute containing the 0-based
* index of the pattern that caused the wakeup, in the patterns passed
* @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
* u32 attribute holding the maximum length
* @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- * feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ * feature advertising. The mask works like @NL80211_PKTPAT_MASK
* but on the TCP payload only.
* @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
* @MAX_NL80211_WOWLAN_TCP: highest attribute number
MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
};
+/**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+ __u32 max_rules;
+ struct nl80211_pattern_support pat;
+ __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ * see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ * after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+ __NL80211_COALESCE_RULE_INVALID,
+ NL80211_ATTR_COALESCE_RULE_DELAY,
+ NL80211_ATTR_COALESCE_RULE_CONDITION,
+ NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+ /* keep last */
+ NUM_NL80211_ATTR_COALESCE_RULE,
+ NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ * in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ * in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+ NL80211_COALESCE_CONDITION_MATCH,
+ NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
/**
* enum nl80211_iface_limit_attrs - limit attributes
* @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
#include <net/bluetooth/a2mp.h>
#include <net/bluetooth/smp.h>
+struct sco_param {
+ u16 pkt_type;
+ u16 max_latency;
+};
+
+static const struct sco_param sco_param_cvsd[] = {
+ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a }, /* S3 */
+ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007 }, /* S2 */
+ { EDR_ESCO_MASK | ESCO_EV3, 0x0007 }, /* S1 */
+ { EDR_ESCO_MASK | ESCO_HV3, 0xffff }, /* D1 */
+ { EDR_ESCO_MASK | ESCO_HV1, 0xffff }, /* D0 */
+};
+
+static const struct sco_param sco_param_wideband[] = {
+ { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d }, /* T2 */
+ { EDR_ESCO_MASK | ESCO_EV3, 0x0008 }, /* T1 */
+};
+
static void hci_le_create_connection(struct hci_conn *conn)
{
struct hci_dev *hdev = conn->hdev;
hci_send_cmd(hdev, HCI_OP_ADD_SCO, sizeof(cp), &cp);
}
-void hci_setup_sync(struct hci_conn *conn, __u16 handle)
+bool hci_setup_sync(struct hci_conn *conn, __u16 handle)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_setup_sync_conn cp;
+ const struct sco_param *param;
BT_DBG("hcon %p", conn);
conn->attempt++;
cp.handle = cpu_to_le16(handle);
- cp.pkt_type = cpu_to_le16(conn->pkt_type);
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
- cp.max_latency = __constant_cpu_to_le16(0xffff);
- cp.voice_setting = cpu_to_le16(hdev->voice_setting);
- cp.retrans_effort = 0xff;
+ cp.voice_setting = cpu_to_le16(conn->setting);
+
+ switch (conn->setting & SCO_AIRMODE_MASK) {
+ case SCO_AIRMODE_TRANSP:
+ if (conn->attempt > ARRAY_SIZE(sco_param_wideband))
+ return false;
+ cp.retrans_effort = 0x02;
+ param = &sco_param_wideband[conn->attempt - 1];
+ break;
+ case SCO_AIRMODE_CVSD:
+ if (conn->attempt > ARRAY_SIZE(sco_param_cvsd))
+ return false;
+ cp.retrans_effort = 0x01;
+ param = &sco_param_cvsd[conn->attempt - 1];
+ break;
+ default:
+ return false;
+ }
- hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp);
+ cp.pkt_type = __cpu_to_le16(param->pkt_type);
+ cp.max_latency = __cpu_to_le16(param->max_latency);
+
+ if (hci_send_cmd(hdev, HCI_OP_SETUP_SYNC_CONN, sizeof(cp), &cp) < 0)
+ return false;
+
+ return true;
}
void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max,
return acl;
}
-static struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type,
- bdaddr_t *dst, u8 sec_level, u8 auth_type)
+struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst,
+ __u16 setting)
{
struct hci_conn *acl;
struct hci_conn *sco;
- acl = hci_connect_acl(hdev, dst, sec_level, auth_type);
+ acl = hci_connect_acl(hdev, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
if (IS_ERR(acl))
return acl;
hci_conn_hold(sco);
+ sco->setting = setting;
+
if (acl->state == BT_CONNECTED &&
(sco->state == BT_OPEN || sco->state == BT_CLOSED)) {
set_bit(HCI_CONN_POWER_SAVE, &acl->flags);
return hci_connect_le(hdev, dst, dst_type, sec_level, auth_type);
case ACL_LINK:
return hci_connect_acl(hdev, dst, sec_level, auth_type);
- case SCO_LINK:
- case ESCO_LINK:
- return hci_connect_sco(hdev, type, dst, sec_level, auth_type);
}
return ERR_PTR(-EINVAL);
events[4] |= 0x04; /* Read Remote Extended Features Complete */
events[5] |= 0x08; /* Synchronous Connection Complete */
events[5] |= 0x10; /* Synchronous Connection Changed */
+ } else {
+ /* Use a different default for LE-only devices */
+ memset(events, 0, sizeof(events));
+ events[0] |= 0x10; /* Disconnection Complete */
+ events[0] |= 0x80; /* Encryption Change */
+ events[1] |= 0x08; /* Read Remote Version Information Complete */
+ events[1] |= 0x20; /* Command Complete */
+ events[1] |= 0x40; /* Command Status */
+ events[1] |= 0x80; /* Hardware Error */
+ events[2] |= 0x04; /* Number of Completed Packets */
+ events[3] |= 0x02; /* Data Buffer Overflow */
+ events[5] |= 0x80; /* Encryption Key Refresh Complete */
}
if (lmp_inq_rssi_capable(hdev))
* as supported send it. If not supported assume that the controller
* does not have actual support for stored link keys which makes this
* command redundant anyway.
- */
+ */
if (hdev->commands[6] & 0x80) {
struct hci_cp_delete_stored_link_key cp;
hci_conn_add_sysfs(conn);
break;
+ case 0x0d: /* Connection Rejected due to Limited Resources */
case 0x11: /* Unsupported Feature or Parameter Value */
case 0x1c: /* SCO interval rejected */
case 0x1a: /* Unsupported Remote Feature */
case 0x1f: /* Unspecified error */
- if (conn->out && conn->attempt < 2) {
+ if (conn->out) {
conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
(hdev->esco_type & EDR_ESCO_MASK);
- hci_setup_sync(conn, conn->link->handle);
- goto unlock;
+ if (hci_setup_sync(conn, conn->link->handle))
+ goto unlock;
}
/* fall through */
static u8 hci_get_auth_req(struct hci_conn *conn)
{
/* If remote requests dedicated bonding follow that lead */
- if (conn->remote_auth == 0x02 || conn->remote_auth == 0x03) {
+ if (conn->remote_auth == HCI_AT_DEDICATED_BONDING ||
+ conn->remote_auth == HCI_AT_DEDICATED_BONDING_MITM) {
/* If both remote and local IO capabilities allow MITM
* protection then require it, otherwise don't */
- if (conn->remote_cap == 0x03 || conn->io_capability == 0x03)
- return 0x02;
+ if (conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT ||
+ conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+ return HCI_AT_DEDICATED_BONDING;
else
- return 0x03;
+ return HCI_AT_DEDICATED_BONDING_MITM;
}
/* If remote requests no-bonding follow that lead */
- if (conn->remote_auth == 0x00 || conn->remote_auth == 0x01)
+ if (conn->remote_auth == HCI_AT_NO_BONDING ||
+ conn->remote_auth == HCI_AT_NO_BONDING_MITM)
return conn->remote_auth | (conn->auth_type & 0x01);
return conn->auth_type;
/* Change the IO capability from KeyboardDisplay
* to DisplayYesNo as it is not supported by BT spec. */
cp.capability = (conn->io_capability == 0x04) ?
- 0x01 : conn->io_capability;
+ HCI_IO_DISPLAY_YESNO : conn->io_capability;
conn->auth_type = hci_get_auth_req(conn);
cp.authentication = conn->auth_type;
* request. The only exception is when we're dedicated bonding
* initiators (connect_cfm_cb set) since then we always have the MITM
* bit set. */
- if (!conn->connect_cfm_cb && loc_mitm && conn->remote_cap == 0x03) {
+ if (!conn->connect_cfm_cb && loc_mitm &&
+ conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) {
BT_DBG("Rejecting request: remote device can't provide MITM");
hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY,
sizeof(ev->bdaddr), &ev->bdaddr);
}
/* If no side requires MITM protection; auto-accept */
- if ((!loc_mitm || conn->remote_cap == 0x03) &&
- (!rem_mitm || conn->io_capability == 0x03)) {
+ if ((!loc_mitm || conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) &&
+ (!rem_mitm || conn->io_capability == HCI_IO_NO_INPUT_OUTPUT)) {
/* If we're not the initiators request authorization to
* proceed from user space (mgmt_user_confirm with
return hidp_send_intr_message(session, hdr, buf, rsize);
}
+static int hidp_hidinput_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct hid_device *hid = input_get_drvdata(dev);
+ struct hidp_session *session = hid->driver_data;
+ struct hid_field *field;
+ int offset;
+
+ BT_DBG("session %p type %d code %d value %d",
+ session, type, code, value);
+
+ if (type != EV_LED)
+ return -1;
+
+ offset = hidinput_find_field(hid, type, code, &field);
+ if (offset == -1) {
+ hid_warn(dev, "event field not found\n");
+ return -1;
+ }
+
+ hid_set_field(field, offset, value);
+
+ return hidp_send_report(session, field->report);
+}
+
static int hidp_get_raw_report(struct hid_device *hid,
unsigned char report_number,
unsigned char *data, size_t count,
static int hidp_start(struct hid_device *hid)
{
- struct hidp_session *session = hid->driver_data;
- struct hid_report *report;
-
- if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS)
- return 0;
-
- list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].
- report_list, list)
- hidp_send_report(session, report);
-
- list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].
- report_list, list)
- hidp_send_report(session, report);
-
return 0;
}
.stop = hidp_stop,
.open = hidp_open,
.close = hidp_close,
+ .hidinput_input_event = hidp_hidinput_event,
};
/* This function sets up the hid device. It does not add it
sk->sk_state_change(sk);
release_sock(sk);
- } else if (chan->state == BT_CONNECT)
+ } else if (chan->state == BT_CONNECT) {
l2cap_do_start(chan);
+ }
l2cap_chan_unlock(chan);
}
uint modem_status;
struct rfcomm_dlc *dlc;
- wait_queue_head_t wait;
struct device *tty_dev;
/* ---- Device functions ---- */
-/*
- * The reason this isn't actually a race, as you no doubt have a little voice
- * screaming at you in your head, is that the refcount should never actually
- * reach zero unless the device has already been taken off the list, in
- * rfcomm_dev_del(). And if that's not true, we'll hit the BUG() in
- * rfcomm_dev_destruct() anyway.
- */
static void rfcomm_dev_destruct(struct tty_port *port)
{
struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
BT_DBG("dev %p dlc %p", dev, dlc);
- /* Refcount should only hit zero when called from rfcomm_dev_del()
- which will have taken us off the list. Everything else are
- refcounting bugs. */
- BUG_ON(!list_empty(&dev->list));
+ spin_lock(&rfcomm_dev_lock);
+ list_del(&dev->list);
+ spin_unlock(&rfcomm_dev_lock);
rfcomm_dlc_lock(dlc);
/* Detach DLC if it's owned by this dev */
module_put(THIS_MODULE);
}
+/* device-specific initialization: open the dlc */
+static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+
+ return rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
+}
+
+/* we block the open until the dlc->state becomes BT_CONNECTED */
+static int rfcomm_dev_carrier_raised(struct tty_port *port)
+{
+ struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+
+ return (dev->dlc->state == BT_CONNECTED);
+}
+
+/* device-specific cleanup: close the dlc */
+static void rfcomm_dev_shutdown(struct tty_port *port)
+{
+ struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+
+ if (dev->tty_dev->parent)
+ device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
+
+ /* close the dlc */
+ rfcomm_dlc_close(dev->dlc, 0);
+}
+
static const struct tty_port_operations rfcomm_port_ops = {
.destruct = rfcomm_dev_destruct,
+ .activate = rfcomm_dev_activate,
+ .shutdown = rfcomm_dev_shutdown,
+ .carrier_raised = rfcomm_dev_carrier_raised,
};
static struct rfcomm_dev *__rfcomm_dev_get(int id)
tty_port_init(&dev->port);
dev->port.ops = &rfcomm_port_ops;
- init_waitqueue_head(&dev->wait);
skb_queue_head_init(&dev->pending);
dev->id, NULL);
if (IS_ERR(dev->tty_dev)) {
err = PTR_ERR(dev->tty_dev);
+ spin_lock(&rfcomm_dev_lock);
list_del(&dev->list);
+ spin_unlock(&rfcomm_dev_lock);
goto free;
}
return err;
}
-static void rfcomm_dev_del(struct rfcomm_dev *dev)
-{
- unsigned long flags;
- BT_DBG("dev %p", dev);
-
- BUG_ON(test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags));
-
- spin_lock_irqsave(&dev->port.lock, flags);
- if (dev->port.count > 0) {
- spin_unlock_irqrestore(&dev->port.lock, flags);
- return;
- }
- spin_unlock_irqrestore(&dev->port.lock, flags);
-
- spin_lock(&rfcomm_dev_lock);
- list_del_init(&dev->list);
- spin_unlock(&rfcomm_dev_lock);
-
- tty_port_put(&dev->port);
-}
-
/* ---- Send buffer ---- */
static inline unsigned int rfcomm_room(struct rfcomm_dlc *dlc)
{
static void rfcomm_wfree(struct sk_buff *skb)
{
struct rfcomm_dev *dev = (void *) skb->sk;
- struct tty_struct *tty = dev->port.tty;
atomic_sub(skb->truesize, &dev->wmem_alloc);
- if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags) && tty)
- tty_wakeup(tty);
+ if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
+ tty_port_tty_wakeup(&dev->port);
tty_port_put(&dev->port);
}
{
struct rfcomm_dev_req req;
struct rfcomm_dev *dev;
+ struct tty_struct *tty;
if (copy_from_user(&req, arg, sizeof(req)))
return -EFAULT;
rfcomm_dlc_close(dev->dlc, 0);
/* Shut down TTY synchronously before freeing rfcomm_dev */
- if (dev->port.tty)
- tty_vhangup(dev->port.tty);
+ tty = tty_port_tty_get(&dev->port);
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
+
+ if (!test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+ tty_port_put(&dev->port);
- if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
- rfcomm_dev_del(dev);
tty_port_put(&dev->port);
return 0;
}
static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
{
struct rfcomm_dev *dev = dlc->owner;
+ struct tty_struct *tty;
if (!dev)
return;
BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
dev->err = err;
- wake_up_interruptible(&dev->wait);
+ if (dlc->state == BT_CONNECTED) {
+ device_move(dev->tty_dev, rfcomm_get_device(dev),
+ DPM_ORDER_DEV_AFTER_PARENT);
- if (dlc->state == BT_CLOSED) {
- if (!dev->port.tty) {
+ wake_up_interruptible(&dev->port.open_wait);
+ } else if (dlc->state == BT_CLOSED) {
+ tty = tty_port_tty_get(&dev->port);
+ if (!tty) {
if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
/* Drop DLC lock here to avoid deadlock
* 1. rfcomm_dev_get will take rfcomm_dev_lock
* rfcomm_dev_lock -> dlc lock
* 2. tty_port_put will deadlock if it's
* the last reference
+ *
+ * FIXME: when we release the lock anything
+ * could happen to dev, even its destruction
*/
rfcomm_dlc_unlock(dlc);
if (rfcomm_dev_get(dev->id) == NULL) {
return;
}
- rfcomm_dev_del(dev);
+ if (!test_and_set_bit(RFCOMM_TTY_RELEASED,
+ &dev->flags))
+ tty_port_put(&dev->port);
+
tty_port_put(&dev->port);
rfcomm_dlc_lock(dlc);
}
- } else
- tty_hangup(dev->port.tty);
+ } else {
+ tty_hangup(tty);
+ tty_kref_put(tty);
+ }
}
}
BT_DBG("dlc %p dev %p v24_sig 0x%02x", dlc, dev, v24_sig);
- if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV)) {
- if (dev->port.tty && !C_CLOCAL(dev->port.tty))
- tty_hangup(dev->port.tty);
- }
+ if ((dev->modem_status & TIOCM_CD) && !(v24_sig & RFCOMM_V24_DV))
+ tty_port_tty_hangup(&dev->port, true);
dev->modem_status =
((v24_sig & RFCOMM_V24_RTC) ? (TIOCM_DSR | TIOCM_DTR) : 0) |
tty_flip_buffer_push(&dev->port);
}
-static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
+/* do the reverse of install, clearing the tty fields and releasing the
+ * reference to tty_port
+ */
+static void rfcomm_tty_cleanup(struct tty_struct *tty)
{
- DECLARE_WAITQUEUE(wait, current);
- struct rfcomm_dev *dev;
- struct rfcomm_dlc *dlc;
- unsigned long flags;
- int err, id;
+ struct rfcomm_dev *dev = tty->driver_data;
- id = tty->index;
+ clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
- BT_DBG("tty %p id %d", tty, id);
+ rfcomm_dlc_lock(dev->dlc);
+ tty->driver_data = NULL;
+ rfcomm_dlc_unlock(dev->dlc);
- /* We don't leak this refcount. For reasons which are not entirely
- clear, the TTY layer will call our ->close() method even if the
- open fails. We decrease the refcount there, and decreasing it
- here too would cause breakage. */
- dev = rfcomm_dev_get(id);
- if (!dev)
- return -ENODEV;
+ /*
+ * purge the dlc->tx_queue to avoid circular dependencies
+ * between dev and dlc
+ */
+ skb_queue_purge(&dev->dlc->tx_queue);
- BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
- dev->channel, dev->port.count);
+ tty_port_put(&dev->port);
+}
- spin_lock_irqsave(&dev->port.lock, flags);
- if (++dev->port.count > 1) {
- spin_unlock_irqrestore(&dev->port.lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&dev->port.lock, flags);
+/* we acquire the tty_port reference since it's here the tty is first used
+ * by setting the termios. We also populate the driver_data field and install
+ * the tty port
+ */
+static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+ struct rfcomm_dev *dev;
+ struct rfcomm_dlc *dlc;
+ int err;
+
+ dev = rfcomm_dev_get(tty->index);
+ if (!dev)
+ return -ENODEV;
dlc = dev->dlc;
/* Attach TTY and open DLC */
-
rfcomm_dlc_lock(dlc);
tty->driver_data = dev;
- dev->port.tty = tty;
rfcomm_dlc_unlock(dlc);
set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
- err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
- if (err < 0)
- return err;
-
- /* Wait for DLC to connect */
- add_wait_queue(&dev->wait, &wait);
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
+ /* install the tty_port */
+ err = tty_port_install(&dev->port, driver, tty);
+ if (err)
+ rfcomm_tty_cleanup(tty);
- if (dlc->state == BT_CLOSED) {
- err = -dev->err;
- break;
- }
+ return err;
+}
- if (dlc->state == BT_CONNECTED)
- break;
+static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ struct rfcomm_dev *dev = tty->driver_data;
+ int err;
- if (signal_pending(current)) {
- err = -EINTR;
- break;
- }
+ BT_DBG("tty %p id %d", tty, tty->index);
- tty_unlock(tty);
- schedule();
- tty_lock(tty);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&dev->wait, &wait);
+ BT_DBG("dev %p dst %pMR channel %d opened %d", dev, &dev->dst,
+ dev->channel, dev->port.count);
- if (err == 0)
- device_move(dev->tty_dev, rfcomm_get_device(dev),
- DPM_ORDER_DEV_AFTER_PARENT);
+ err = tty_port_open(&dev->port, tty, filp);
+ if (err)
+ return err;
+ /*
+ * FIXME: rfcomm should use proper flow control for
+ * received data. This hack will be unnecessary and can
+ * be removed when that's implemented
+ */
rfcomm_tty_copy_pending(dev);
rfcomm_dlc_unthrottle(dev->dlc);
- return err;
+ return 0;
}
static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
{
struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
- unsigned long flags;
-
- if (!dev)
- return;
BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc,
dev->port.count);
- spin_lock_irqsave(&dev->port.lock, flags);
- if (!--dev->port.count) {
- spin_unlock_irqrestore(&dev->port.lock, flags);
- if (dev->tty_dev->parent)
- device_move(dev->tty_dev, NULL, DPM_ORDER_DEV_LAST);
-
- /* Close DLC and dettach TTY */
- rfcomm_dlc_close(dev->dlc, 0);
-
- clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
-
- rfcomm_dlc_lock(dev->dlc);
- tty->driver_data = NULL;
- dev->port.tty = NULL;
- rfcomm_dlc_unlock(dev->dlc);
-
- if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) {
- spin_lock(&rfcomm_dev_lock);
- list_del_init(&dev->list);
- spin_unlock(&rfcomm_dev_lock);
-
- tty_port_put(&dev->port);
- }
- } else
- spin_unlock_irqrestore(&dev->port.lock, flags);
-
- tty_port_put(&dev->port);
+ tty_port_close(&dev->port, tty, filp);
}
static int rfcomm_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
BT_DBG("tty %p dev %p", tty, dev);
- if (!dev)
- return;
-
- rfcomm_tty_flush_buffer(tty);
+ tty_port_hangup(&dev->port);
- if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
- if (rfcomm_dev_get(dev->id) == NULL)
- return;
- rfcomm_dev_del(dev);
+ if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
+ !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
tty_port_put(&dev->port);
- }
}
static int rfcomm_tty_tiocmget(struct tty_struct *tty)
.wait_until_sent = rfcomm_tty_wait_until_sent,
.tiocmget = rfcomm_tty_tiocmget,
.tiocmset = rfcomm_tty_tiocmset,
+ .install = rfcomm_tty_install,
+ .cleanup = rfcomm_tty_cleanup,
};
int __init rfcomm_init_ttys(void)
rfcomm_tty_driver->subtype = SERIAL_TYPE_NORMAL;
rfcomm_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
rfcomm_tty_driver->init_termios = tty_std_termios;
- rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
else
type = SCO_LINK;
- hcon = hci_connect(hdev, type, dst, BDADDR_BREDR, BT_SECURITY_LOW,
- HCI_AT_NO_BONDING);
+ if (sco_pi(sk)->setting == BT_VOICE_TRANSPARENT &&
+ (!lmp_transp_capable(hdev) || !lmp_esco_capable(hdev))) {
+ err = -EOPNOTSUPP;
+ goto done;
+ }
+
+ hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting);
if (IS_ERR(hcon)) {
err = PTR_ERR(hcon);
goto done;
sk->sk_protocol = proto;
sk->sk_state = BT_OPEN;
+ sco_pi(sk)->setting = BT_VOICE_CVSD_16BIT;
+
setup_timer(&sk->sk_timer, sco_sock_timeout, (unsigned long)sk);
bt_sock_link(&sco_sk_list, sk);
return err;
}
-static void sco_conn_defer_accept(struct hci_conn *conn, int mask)
+static void sco_conn_defer_accept(struct hci_conn *conn, u16 setting)
{
struct hci_dev *hdev = conn->hdev;
struct hci_cp_accept_conn_req cp;
bacpy(&cp.bdaddr, &conn->dst);
-
- if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
- cp.role = 0x00; /* Become master */
- else
- cp.role = 0x01; /* Remain slave */
+ cp.role = 0x00; /* Ignored */
hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp);
} else {
cp.tx_bandwidth = __constant_cpu_to_le32(0x00001f40);
cp.rx_bandwidth = __constant_cpu_to_le32(0x00001f40);
- cp.max_latency = __constant_cpu_to_le16(0xffff);
- cp.content_format = cpu_to_le16(hdev->voice_setting);
- cp.retrans_effort = 0xff;
+ cp.content_format = cpu_to_le16(setting);
+
+ switch (setting & SCO_AIRMODE_MASK) {
+ case SCO_AIRMODE_TRANSP:
+ if (conn->pkt_type & ESCO_2EV3)
+ cp.max_latency = __constant_cpu_to_le16(0x0008);
+ else
+ cp.max_latency = __constant_cpu_to_le16(0x000D);
+ cp.retrans_effort = 0x02;
+ break;
+ case SCO_AIRMODE_CVSD:
+ cp.max_latency = __constant_cpu_to_le16(0xffff);
+ cp.retrans_effort = 0xff;
+ break;
+ }
hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ,
sizeof(cp), &cp);
if (sk->sk_state == BT_CONNECT2 &&
test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) {
- sco_conn_defer_accept(pi->conn->hcon, 0);
+ sco_conn_defer_accept(pi->conn->hcon, pi->setting);
sk->sk_state = BT_CONFIG;
msg->msg_namelen = 0;
static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
- int err = 0;
+ int len, err = 0;
+ struct bt_voice voice;
u32 opt;
BT_DBG("sk %p", sk);
clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags);
break;
+ case BT_VOICE:
+ if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND &&
+ sk->sk_state != BT_CONNECT2) {
+ err = -EINVAL;
+ break;
+ }
+
+ voice.setting = sco_pi(sk)->setting;
+
+ len = min_t(unsigned int, sizeof(voice), optlen);
+ if (copy_from_user((char *) &voice, optval, len)) {
+ err = -EFAULT;
+ break;
+ }
+
+ /* Explicitly check for these values */
+ if (voice.setting != BT_VOICE_TRANSPARENT &&
+ voice.setting != BT_VOICE_CVSD_16BIT) {
+ err = -EINVAL;
+ break;
+ }
+
+ sco_pi(sk)->setting = voice.setting;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
switch (optname) {
case SCO_OPTIONS:
- if (sk->sk_state != BT_CONNECTED) {
+ if (sk->sk_state != BT_CONNECTED &&
+ !(sk->sk_state == BT_CONNECT2 &&
+ test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
err = -ENOTCONN;
break;
}
break;
case SCO_CONNINFO:
- if (sk->sk_state != BT_CONNECTED) {
+ if (sk->sk_state != BT_CONNECTED &&
+ !(sk->sk_state == BT_CONNECT2 &&
+ test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags))) {
err = -ENOTCONN;
break;
}
{
struct sock *sk = sock->sk;
int len, err = 0;
+ struct bt_voice voice;
BT_DBG("sk %p", sk);
break;
+ case BT_VOICE:
+ voice.setting = sco_pi(sk)->setting;
+
+ len = min_t(unsigned int, len, sizeof(voice));
+ if (copy_to_user(optval, (char *)&voice, len))
+ err = -EFAULT;
+
+ break;
+
default:
err = -ENOPROTOOPT;
break;
rinfo->nss = ieee80211_rate_get_vht_nss(rate);
} else {
struct ieee80211_supported_band *sband;
+ int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+ u16 brate;
+
sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
- rinfo->legacy = sband->bitrates[rate->idx].bitrate;
+ brate = sband->bitrates[rate->idx].bitrate;
+ rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
}
if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH;
rinfo->mcs = sta->last_rx_rate_idx;
} else {
struct ieee80211_supported_band *sband;
+ int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+ u16 brate;
sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
- rinfo->legacy =
- sband->bitrates[sta->last_rx_rate_idx].bitrate;
+ brate = sband->bitrates[sta->last_rx_rate_idx].bitrate;
+ rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
}
if (sta->last_rx_rate_flag & RX_FLAG_40MHZ)
return 0;
}
-static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_beacon_data *params)
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_beacon_data *params)
{
struct beacon_data *new, *old;
int new_head_len, new_tail_len;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ /* don't allow changing the beacon while CSA is in place - offset
+ * of channel switch counter may change
+ */
+ if (sdata->vif.csa_active)
+ return -EBUSY;
+
old = rtnl_dereference(sdata->u.ap.beacon);
if (!old)
return -ENOENT;
return -ENOENT;
old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+ /* abort any running channel switch */
+ sdata->vif.csa_active = false;
+ cancel_work_sync(&sdata->csa_finalize_work);
+
/* turn off carrier for this interface and dependent VLANs */
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
netif_carrier_off(vlan->dev);
struct station_parameters *params)
{
int ret = 0;
- u32 rates;
- int i, j;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = sta->sdata;
enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
sta->listen_interval = params->listen_interval;
if (params->supported_rates) {
- rates = 0;
-
- for (i = 0; i < params->supported_rates_len; i++) {
- int rate = (params->supported_rates[i] & 0x7f) * 5;
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
- rates |= BIT(j);
- }
- }
- sta->sta.supp_rates[band] = rates;
+ ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+ sband, params->supported_rates,
+ params->supported_rates_len,
+ &sta->sta.supp_rates[band]);
}
if (params->ht_capa)
}
if (params->basic_rates) {
- int i, j;
- u32 rates = 0;
- struct ieee80211_supported_band *sband = wiphy->bands[band];
-
- for (i = 0; i < params->basic_rates_len; i++) {
- int rate = (params->basic_rates[i] & 0x7f) * 5;
- for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate)
- rates |= BIT(j);
- }
- }
- sdata->vif.bss_conf.basic_rates = rates;
+ ieee80211_parse_bitrates(&sdata->vif.bss_conf.chandef,
+ wiphy->bands[band],
+ params->basic_rates,
+ params->basic_rates_len,
+ &sdata->vif.bss_conf.basic_rates);
changed |= BSS_CHANGED_BASIC_RATES;
}
}
#ifdef CONFIG_NL80211_TESTMODE
-static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
+static int ieee80211_testmode_cmd(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ void *data, int len)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_vif *vif = NULL;
if (!local->ops->testmode_cmd)
return -EOPNOTSUPP;
- return local->ops->testmode_cmd(&local->hw, data, len);
+ if (wdev) {
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (sdata->flags & IEEE80211_SDATA_IN_DRIVER)
+ vif = &sdata->vif;
+ }
+
+ return local->ops->testmode_cmd(&local->hw, vif, data, len);
}
static int ieee80211_testmode_dump(struct wiphy *wiphy,
return 0;
}
+static struct cfg80211_beacon_data *
+cfg80211_beacon_dup(struct cfg80211_beacon_data *beacon)
+{
+ struct cfg80211_beacon_data *new_beacon;
+ u8 *pos;
+ int len;
+
+ len = beacon->head_len + beacon->tail_len + beacon->beacon_ies_len +
+ beacon->proberesp_ies_len + beacon->assocresp_ies_len +
+ beacon->probe_resp_len;
+
+ new_beacon = kzalloc(sizeof(*new_beacon) + len, GFP_KERNEL);
+ if (!new_beacon)
+ return NULL;
+
+ pos = (u8 *)(new_beacon + 1);
+ if (beacon->head_len) {
+ new_beacon->head_len = beacon->head_len;
+ new_beacon->head = pos;
+ memcpy(pos, beacon->head, beacon->head_len);
+ pos += beacon->head_len;
+ }
+ if (beacon->tail_len) {
+ new_beacon->tail_len = beacon->tail_len;
+ new_beacon->tail = pos;
+ memcpy(pos, beacon->tail, beacon->tail_len);
+ pos += beacon->tail_len;
+ }
+ if (beacon->beacon_ies_len) {
+ new_beacon->beacon_ies_len = beacon->beacon_ies_len;
+ new_beacon->beacon_ies = pos;
+ memcpy(pos, beacon->beacon_ies, beacon->beacon_ies_len);
+ pos += beacon->beacon_ies_len;
+ }
+ if (beacon->proberesp_ies_len) {
+ new_beacon->proberesp_ies_len = beacon->proberesp_ies_len;
+ new_beacon->proberesp_ies = pos;
+ memcpy(pos, beacon->proberesp_ies, beacon->proberesp_ies_len);
+ pos += beacon->proberesp_ies_len;
+ }
+ if (beacon->assocresp_ies_len) {
+ new_beacon->assocresp_ies_len = beacon->assocresp_ies_len;
+ new_beacon->assocresp_ies = pos;
+ memcpy(pos, beacon->assocresp_ies, beacon->assocresp_ies_len);
+ pos += beacon->assocresp_ies_len;
+ }
+ if (beacon->probe_resp_len) {
+ new_beacon->probe_resp_len = beacon->probe_resp_len;
+ beacon->probe_resp = pos;
+ memcpy(pos, beacon->probe_resp, beacon->probe_resp_len);
+ pos += beacon->probe_resp_len;
+ }
+
+ return new_beacon;
+}
+
+void ieee80211_csa_finalize_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ csa_finalize_work);
+ struct ieee80211_local *local = sdata->local;
+ int err, changed;
+
+ if (!ieee80211_sdata_running(sdata))
+ return;
+
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
+ return;
+
+ sdata->radar_required = sdata->csa_radar_required;
+ err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
+ &changed);
+ if (WARN_ON(err < 0))
+ return;
+
+ err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
+ if (err < 0)
+ return;
+
+ changed |= err;
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+ sdata->vif.csa_active = false;
+
+ ieee80211_wake_queues_by_reason(&sdata->local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+}
+
+static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_chanctx *chanctx;
+ int err, num_chanctx;
+
+ if (!list_empty(&local->roc_list) || local->scanning)
+ return -EBUSY;
+
+ if (sdata->wdev.cac_started)
+ return -EBUSY;
+
+ if (cfg80211_chandef_identical(¶ms->chandef,
+ &sdata->vif.bss_conf.chandef))
+ return -EINVAL;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+
+ /* don't handle for multi-VIF cases */
+ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+ if (chanctx->refcount > 1) {
+ rcu_read_unlock();
+ return -EBUSY;
+ }
+ num_chanctx = 0;
+ list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
+ num_chanctx++;
+ rcu_read_unlock();
+
+ if (num_chanctx > 1)
+ return -EBUSY;
+
+ /* don't allow another channel switch if one is already active. */
+ if (sdata->vif.csa_active)
+ return -EBUSY;
+
+ /* only handle AP for now. */
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_AP:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after);
+ if (!sdata->u.ap.next_beacon)
+ return -ENOMEM;
+
+ sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
+ sdata->csa_counter_offset_presp = params->counter_offset_presp;
+ sdata->csa_radar_required = params->radar_required;
+
+ if (params->block_tx)
+ ieee80211_stop_queues_by_reason(&local->hw,
+ IEEE80211_MAX_QUEUE_MAP,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+
+ err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
+ if (err < 0)
+ return err;
+
+ local->csa_chandef = params->chandef;
+ sdata->vif.csa_active = true;
+
+ ieee80211_bss_info_change_notify(sdata, err);
+ drv_channel_switch_beacon(sdata, ¶ms->chandef);
+
+ return 0;
+}
+
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
struct ieee80211_channel *chan, bool offchan,
unsigned int wait, const u8 *buf, size_t len,
.get_et_strings = ieee80211_get_et_strings,
.get_channel = ieee80211_cfg_get_channel,
.start_radar_detection = ieee80211_start_radar_detection,
+ .channel_switch = ieee80211_channel_switch,
};
return ret;
}
+int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *conf;
+ struct ieee80211_chanctx *ctx;
+ int ret;
+ u32 chanctx_changed = 0;
+
+ /* should never be called if not performing a channel switch. */
+ if (WARN_ON(!sdata->vif.csa_active))
+ return -EINVAL;
+
+ if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
+ IEEE80211_CHAN_DISABLED))
+ return -EINVAL;
+
+ mutex_lock(&local->chanctx_mtx);
+ conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+ if (!conf) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ctx = container_of(conf, struct ieee80211_chanctx, conf);
+ if (ctx->refcount != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (sdata->vif.bss_conf.chandef.width != chandef->width) {
+ chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
+ *changed |= BSS_CHANGED_BANDWIDTH;
+ }
+
+ sdata->vif.bss_conf.chandef = *chandef;
+ ctx->conf.def = *chandef;
+
+ chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
+ drv_change_chanctx(local, ctx, chanctx_changed);
+
+ if (!local->use_chanctx) {
+ local->_oper_chandef = *chandef;
+ ieee80211_hw_config(local, 0);
+ }
+
+ ieee80211_recalc_chanctx_chantype(local, ctx);
+ ieee80211_recalc_smps_chanctx(local, ctx);
+ ieee80211_recalc_radar_chanctx(local, ctx);
+
+ ret = 0;
+ out:
+ mutex_unlock(&local->chanctx_mtx);
+ return ret;
+}
+
int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
u32 *changed)
DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count);
DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count);
+ if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
+ debugfs_create_x32("driver_buffered_tids", 0400,
+ sta->debugfs.dir,
+ (u32 *)&sta->driver_buffered_tids);
+ else
+ debugfs_create_x64("driver_buffered_tids", 0400,
+ sta->debugfs.dir,
+ (u64 *)&sta->driver_buffered_tids);
+
drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir);
}
}
#endif
+static inline void
+drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_local *local = sdata->local;
+
+ if (local->ops->channel_switch_beacon) {
+ trace_drv_channel_switch_beacon(local, sdata, chandef);
+ local->ops->channel_switch_beacon(&local->hw, &sdata->vif,
+ chandef);
+ }
+}
+
#endif /* __MAC80211_DRIVER_OPS */
#include "ieee80211_i.h"
#include "rate.h"
-static void __check_htcap_disable(struct ieee80211_sub_if_data *sdata,
+static void __check_htcap_disable(struct ieee80211_ht_cap *ht_capa,
+ struct ieee80211_ht_cap *ht_capa_mask,
struct ieee80211_sta_ht_cap *ht_cap,
u16 flag)
{
__le16 le_flag = cpu_to_le16(flag);
- if (sdata->u.mgd.ht_capa_mask.cap_info & le_flag) {
- if (!(sdata->u.mgd.ht_capa.cap_info & le_flag))
+ if (ht_capa_mask->cap_info & le_flag) {
+ if (!(ht_capa->cap_info & le_flag))
ht_cap->cap &= ~flag;
}
}
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta_ht_cap *ht_cap)
{
- u8 *scaps = (u8 *)(&sdata->u.mgd.ht_capa.mcs.rx_mask);
- u8 *smask = (u8 *)(&sdata->u.mgd.ht_capa_mask.mcs.rx_mask);
+ struct ieee80211_ht_cap *ht_capa, *ht_capa_mask;
+ u8 *scaps, *smask;
int i;
if (!ht_cap->ht_supported)
return;
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ ht_capa = &sdata->u.mgd.ht_capa;
+ ht_capa_mask = &sdata->u.mgd.ht_capa_mask;
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ ht_capa = &sdata->u.ibss.ht_capa;
+ ht_capa_mask = &sdata->u.ibss.ht_capa_mask;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ scaps = (u8 *)(&ht_capa->mcs.rx_mask);
+ smask = (u8 *)(&ht_capa_mask->mcs.rx_mask);
+
/* NOTE: If you add more over-rides here, update register_hw
* ht_capa_mod_msk logic in main.c as well.
* And, if this method can ever change ht_cap.ht_supported, fix
}
/* Force removal of HT-40 capabilities? */
- __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SUP_WIDTH_20_40);
- __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_40);
+ __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40);
+ __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+ IEEE80211_HT_CAP_SGI_40);
/* Allow user to disable SGI-20 (SGI-40 is handled above) */
- __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_SGI_20);
+ __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+ IEEE80211_HT_CAP_SGI_20);
/* Allow user to disable the max-AMSDU bit. */
- __check_htcap_disable(sdata, ht_cap, IEEE80211_HT_CAP_MAX_AMSDU);
+ __check_htcap_disable(ht_capa, ht_capa_mask, ht_cap,
+ IEEE80211_HT_CAP_MAX_AMSDU);
/* Allow user to decrease AMPDU factor */
- if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+ if (ht_capa_mask->ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_FACTOR) {
- u8 n = sdata->u.mgd.ht_capa.ampdu_params_info
- & IEEE80211_HT_AMPDU_PARM_FACTOR;
+ u8 n = ht_capa->ampdu_params_info &
+ IEEE80211_HT_AMPDU_PARM_FACTOR;
if (n < ht_cap->ampdu_factor)
ht_cap->ampdu_factor = n;
}
/* Allow the user to increase AMPDU density. */
- if (sdata->u.mgd.ht_capa_mask.ampdu_params_info &
+ if (ht_capa_mask->ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_DENSITY) {
- u8 n = (sdata->u.mgd.ht_capa.ampdu_params_info &
+ u8 n = (ht_capa->ampdu_params_info &
IEEE80211_HT_AMPDU_PARM_DENSITY)
>> IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT;
if (n > ht_cap->ampdu_density)
* we advertised a restricted capability set to. Override
* our own capabilities and then use those below.
*/
- if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+ if ((sdata->vif.type == NL80211_IFTYPE_STATION ||
+ sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
!test_sta_flag(sta, WLAN_STA_TDLS_PEER))
ieee80211_apply_htcap_overrides(sdata, &own_cap);
#define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ)
#define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ)
+#define IEEE80211_IBSS_RSN_INACTIVITY_LIMIT (10 * HZ)
#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
-
-static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
- const u8 *bssid, const int beacon_int,
- struct cfg80211_chan_def *req_chandef,
- const u32 basic_rates,
- const u16 capability, u64 tsf,
- bool creator)
+static struct beacon_data *
+ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
+ const int beacon_int, const u32 basic_rates,
+ const u16 capability, u64 tsf,
+ struct cfg80211_chan_def *chandef,
+ bool *have_higher_than_11mbit)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
- int rates, i;
+ int rates_n = 0, i, ri;
struct ieee80211_mgmt *mgmt;
u8 *pos;
struct ieee80211_supported_band *sband;
- struct cfg80211_bss *bss;
- u32 bss_change;
- u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
- struct cfg80211_chan_def chandef;
- struct ieee80211_channel *chan;
+ u32 rate_flags, rates = 0, rates_added = 0;
struct beacon_data *presp;
int frame_len;
-
- sdata_assert_lock(sdata);
-
- /* Reset own TSF to allow time synchronization work. */
- drv_reset_tsf(local, sdata);
-
- if (!ether_addr_equal(ifibss->bssid, bssid))
- sta_info_flush(sdata);
-
- /* if merging, indicate to driver that we leave the old IBSS */
- if (sdata->vif.bss_conf.ibss_joined) {
- sdata->vif.bss_conf.ibss_joined = false;
- sdata->vif.bss_conf.ibss_creator = false;
- sdata->vif.bss_conf.enable_beacon = false;
- netif_carrier_off(sdata->dev);
- ieee80211_bss_info_change_notify(sdata,
- BSS_CHANGED_IBSS |
- BSS_CHANGED_BEACON_ENABLED);
- }
-
- presp = rcu_dereference_protected(ifibss->presp,
- lockdep_is_held(&sdata->wdev.mtx));
- rcu_assign_pointer(ifibss->presp, NULL);
- if (presp)
- kfree_rcu(presp, rcu_head);
-
- sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
-
- /* make a copy of the chandef, it could be modified below. */
- chandef = *req_chandef;
- chan = chandef.chan;
- if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
- chandef.width = NL80211_CHAN_WIDTH_20;
- chandef.center_freq1 = chan->center_freq;
- }
-
- ieee80211_vif_release_channel(sdata);
- if (ieee80211_vif_use_channel(sdata, &chandef,
- ifibss->fixed_channel ?
- IEEE80211_CHANCTX_SHARED :
- IEEE80211_CHANCTX_EXCLUSIVE)) {
- sdata_info(sdata, "Failed to join IBSS, no channel context\n");
- return;
- }
-
- memcpy(ifibss->bssid, bssid, ETH_ALEN);
-
- sband = local->hw.wiphy->bands[chan->band];
+ int shift;
/* Build IBSS probe response */
frame_len = sizeof(struct ieee80211_hdr_3addr) +
ifibss->ie_len;
presp = kzalloc(sizeof(*presp) + frame_len, GFP_KERNEL);
if (!presp)
- return;
+ return NULL;
presp->head = (void *)(presp + 1);
memcpy(pos, ifibss->ssid, ifibss->ssid_len);
pos += ifibss->ssid_len;
- rates = min_t(int, 8, sband->n_bitrates);
+ sband = local->hw.wiphy->bands[chandef->chan->band];
+ rate_flags = ieee80211_chandef_rate_flags(chandef);
+ shift = ieee80211_chandef_get_shift(chandef);
+ rates_n = 0;
+ if (have_higher_than_11mbit)
+ *have_higher_than_11mbit = false;
+
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+ if (sband->bitrates[i].bitrate > 110 &&
+ have_higher_than_11mbit)
+ *have_higher_than_11mbit = true;
+
+ rates |= BIT(i);
+ rates_n++;
+ }
+
*pos++ = WLAN_EID_SUPP_RATES;
- *pos++ = rates;
- for (i = 0; i < rates; i++) {
- int rate = sband->bitrates[i].bitrate;
+ *pos++ = min_t(int, 8, rates_n);
+ for (ri = 0; ri < sband->n_bitrates; ri++) {
+ int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+ 5 * (1 << shift));
u8 basic = 0;
- if (basic_rates & BIT(i))
+ if (!(rates & BIT(ri)))
+ continue;
+
+ if (basic_rates & BIT(ri))
basic = 0x80;
- *pos++ = basic | (u8) (rate / 5);
+ *pos++ = basic | (u8) rate;
+ if (++rates_added == 8) {
+ ri++; /* continue at next rate for EXT_SUPP_RATES */
+ break;
+ }
}
if (sband->band == IEEE80211_BAND_2GHZ) {
*pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1;
- *pos++ = ieee80211_frequency_to_channel(chan->center_freq);
+ *pos++ = ieee80211_frequency_to_channel(
+ chandef->chan->center_freq);
}
*pos++ = WLAN_EID_IBSS_PARAMS;
*pos++ = 0;
*pos++ = 0;
- if (sband->n_bitrates > 8) {
+ /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
+ if (rates_n > 8) {
*pos++ = WLAN_EID_EXT_SUPP_RATES;
- *pos++ = sband->n_bitrates - 8;
- for (i = 8; i < sband->n_bitrates; i++) {
- int rate = sband->bitrates[i].bitrate;
+ *pos++ = rates_n - 8;
+ for (; ri < sband->n_bitrates; ri++) {
+ int rate = DIV_ROUND_UP(sband->bitrates[ri].bitrate,
+ 5 * (1 << shift));
u8 basic = 0;
- if (basic_rates & BIT(i))
+ if (!(rates & BIT(ri)))
+ continue;
+
+ if (basic_rates & BIT(ri))
basic = 0x80;
- *pos++ = basic | (u8) (rate / 5);
+ *pos++ = basic | (u8) rate;
}
}
}
/* add HT capability and information IEs */
- if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
- chandef.width != NL80211_CHAN_WIDTH_5 &&
- chandef.width != NL80211_CHAN_WIDTH_10 &&
+ if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef->width != NL80211_CHAN_WIDTH_5 &&
+ chandef->width != NL80211_CHAN_WIDTH_10 &&
sband->ht_cap.ht_supported) {
- pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap,
- sband->ht_cap.cap);
+ struct ieee80211_sta_ht_cap ht_cap;
+
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
+ ieee80211_apply_htcap_overrides(sdata, &ht_cap);
+
+ pos = ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap);
/*
* Note: According to 802.11n-2009 9.13.3.1, HT Protection
* field and RIFS Mode are reserved in IBSS mode, therefore
* keep them at 0
*/
pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap,
- &chandef, 0);
+ chandef, 0);
}
if (local->hw.queues >= IEEE80211_NUM_ACS) {
presp->head_len = pos - presp->head;
if (WARN_ON(presp->head_len > frame_len))
+ goto error;
+
+ return presp;
+error:
+ kfree(presp);
+ return NULL;
+}
+
+static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const int beacon_int,
+ struct cfg80211_chan_def *req_chandef,
+ const u32 basic_rates,
+ const u16 capability, u64 tsf,
+ bool creator)
+{
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_mgmt *mgmt;
+ struct cfg80211_bss *bss;
+ u32 bss_change;
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_channel *chan;
+ struct beacon_data *presp;
+ enum nl80211_bss_scan_width scan_width;
+ bool have_higher_than_11mbit;
+
+ sdata_assert_lock(sdata);
+
+ /* Reset own TSF to allow time synchronization work. */
+ drv_reset_tsf(local, sdata);
+
+ if (!ether_addr_equal(ifibss->bssid, bssid))
+ sta_info_flush(sdata);
+
+ /* if merging, indicate to driver that we leave the old IBSS */
+ if (sdata->vif.bss_conf.ibss_joined) {
+ sdata->vif.bss_conf.ibss_joined = false;
+ sdata->vif.bss_conf.ibss_creator = false;
+ sdata->vif.bss_conf.enable_beacon = false;
+ netif_carrier_off(sdata->dev);
+ ieee80211_bss_info_change_notify(sdata,
+ BSS_CHANGED_IBSS |
+ BSS_CHANGED_BEACON_ENABLED);
+ }
+
+ presp = rcu_dereference_protected(ifibss->presp,
+ lockdep_is_held(&sdata->wdev.mtx));
+ rcu_assign_pointer(ifibss->presp, NULL);
+ if (presp)
+ kfree_rcu(presp, rcu_head);
+
+ sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0;
+
+ /* make a copy of the chandef, it could be modified below. */
+ chandef = *req_chandef;
+ chan = chandef.chan;
+ if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) {
+ if (chandef.width == NL80211_CHAN_WIDTH_5 ||
+ chandef.width == NL80211_CHAN_WIDTH_10 ||
+ chandef.width == NL80211_CHAN_WIDTH_20_NOHT ||
+ chandef.width == NL80211_CHAN_WIDTH_20) {
+ sdata_info(sdata,
+ "Failed to join IBSS, beacons forbidden\n");
+ return;
+ }
+ chandef.width = NL80211_CHAN_WIDTH_20;
+ chandef.center_freq1 = chan->center_freq;
+ }
+
+ ieee80211_vif_release_channel(sdata);
+ if (ieee80211_vif_use_channel(sdata, &chandef,
+ ifibss->fixed_channel ?
+ IEEE80211_CHANCTX_SHARED :
+ IEEE80211_CHANCTX_EXCLUSIVE)) {
+ sdata_info(sdata, "Failed to join IBSS, no channel context\n");
+ return;
+ }
+
+ memcpy(ifibss->bssid, bssid, ETH_ALEN);
+
+ sband = local->hw.wiphy->bands[chan->band];
+
+ presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
+ capability, tsf, &chandef,
+ &have_higher_than_11mbit);
+ if (!presp)
return;
rcu_assign_pointer(ifibss->presp, presp);
+ mgmt = (void *)presp->head;
sdata->vif.bss_conf.enable_beacon = true;
sdata->vif.bss_conf.beacon_int = beacon_int;
sdata->vif.bss_conf.use_short_slot = chan->band == IEEE80211_BAND_5GHZ;
bss_change |= BSS_CHANGED_ERP_SLOT;
+ /* cf. IEEE 802.11 9.2.12 */
+ if (chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit)
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ else
+ sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
+
sdata->vif.bss_conf.ibss_joined = true;
sdata->vif.bss_conf.ibss_creator = creator;
ieee80211_bss_info_change_notify(sdata, bss_change);
- ieee80211_sta_def_wmm_params(sdata, sband->n_bitrates, supp_rates);
+ ieee80211_set_wmm_default(sdata, true);
ifibss->state = IEEE80211_IBSS_MLME_JOINED;
mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
- bss = cfg80211_inform_bss_frame(local->hw.wiphy, chan,
- mgmt, presp->head_len, 0, GFP_KERNEL);
+ scan_width = cfg80211_chandef_to_scan_width(&chandef);
+ bss = cfg80211_inform_bss_width_frame(local->hw.wiphy, chan,
+ scan_width, mgmt,
+ presp->head_len, 0, GFP_KERNEL);
cfg80211_put_bss(local->hw.wiphy, bss);
netif_carrier_on(sdata->dev);
cfg80211_ibss_joined(sdata->dev, ifibss->bssid, GFP_KERNEL);
const struct cfg80211_bss_ies *ies;
enum nl80211_channel_type chan_type;
u64 tsf;
+ u32 rate_flags;
+ int shift;
sdata_assert_lock(sdata);
}
sband = sdata->local->hw.wiphy->bands[cbss->channel->band];
+ rate_flags = ieee80211_chandef_rate_flags(&sdata->u.ibss.chandef);
+ shift = ieee80211_vif_get_shift(&sdata->vif);
basic_rates = 0;
for (i = 0; i < bss->supp_rates_len; i++) {
- int rate = (bss->supp_rates[i] & 0x7f) * 5;
+ int rate = bss->supp_rates[i] & 0x7f;
bool is_basic = !!(bss->supp_rates[i] & 0x80);
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate) {
+ int brate;
+ if ((rate_flags & sband->bitrates[j].flags)
+ != rate_flags)
+ continue;
+
+ brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+ 5 * (1 << shift));
+ if (brate == rate) {
if (is_basic)
basic_rates |= BIT(j);
break;
struct sta_info *sta;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_supported_band *sband;
+ enum nl80211_bss_scan_width scan_width;
int band;
/*
if (WARN_ON_ONCE(!chanctx_conf))
return NULL;
band = chanctx_conf->def.chan->band;
+ scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
rcu_read_unlock();
sta = sta_info_alloc(sdata, addr, GFP_KERNEL);
/* make sure mandatory rates are always added */
sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(sband);
+ ieee80211_mandatory_rates(sband, scan_width);
return ieee80211_ibss_finish_sta(sta);
}
u64 beacon_timestamp, rx_timestamp;
u32 supp_rates = 0;
enum ieee80211_band band = rx_status->band;
+ enum nl80211_bss_scan_width scan_width;
struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band];
bool rates_updated = false;
sta = sta_info_get(sdata, mgmt->sa);
if (elems->supp_rates) {
- supp_rates = ieee80211_sta_get_rates(local, elems,
+ supp_rates = ieee80211_sta_get_rates(sdata, elems,
band, NULL);
if (sta) {
u32 prev_rates;
prev_rates = sta->sta.supp_rates[band];
/* make sure mandatory rates are always added */
- sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(sband);
+ scan_width = NL80211_BSS_CHAN_WIDTH_20;
+ if (rx_status->flag & RX_FLAG_5MHZ)
+ scan_width = NL80211_BSS_CHAN_WIDTH_5;
+ if (rx_status->flag & RX_FLAG_10MHZ)
+ scan_width = NL80211_BSS_CHAN_WIDTH_10;
+ sta->sta.supp_rates[band] = supp_rates |
+ ieee80211_mandatory_rates(sband,
+ scan_width);
if (sta->sta.supp_rates[band] != prev_rates) {
ibss_dbg(sdata,
"updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n",
"beacon TSF higher than local TSF - IBSS merge with BSSID %pM\n",
mgmt->bssid);
ieee80211_sta_join_ibss(sdata, bss);
- supp_rates = ieee80211_sta_get_rates(local, elems, band, NULL);
+ supp_rates = ieee80211_sta_get_rates(sdata, elems, band, NULL);
ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa,
supp_rates);
rcu_read_unlock();
struct sta_info *sta;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_supported_band *sband;
+ enum nl80211_bss_scan_width scan_width;
int band;
/*
return;
}
band = chanctx_conf->def.chan->band;
+ scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
rcu_read_unlock();
sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
/* make sure mandatory rates are always added */
sband = local->hw.wiphy->bands[band];
sta->sta.supp_rates[band] = supp_rates |
- ieee80211_mandatory_rates(sband);
+ ieee80211_mandatory_rates(sband, scan_width);
spin_lock(&ifibss->incomplete_lock);
list_add(&sta->list, &ifibss->incomplete_stations);
return active;
}
+static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta, *tmp;
+ unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
+ unsigned long exp_rsn_time = IEEE80211_IBSS_RSN_INACTIVITY_LIMIT;
+
+ mutex_lock(&local->sta_mtx);
+
+ list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
+ if (sdata != sta->sdata)
+ continue;
+
+ if (time_after(jiffies, sta->last_rx + exp_time) ||
+ (time_after(jiffies, sta->last_rx + exp_rsn_time) &&
+ sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
+ sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
+ sta->sta_state != IEEE80211_STA_AUTHORIZED ?
+ "not authorized " : "", sta->sta.addr);
+
+ WARN_ON(__sta_info_destroy(sta));
+ }
+ }
+
+ mutex_unlock(&local->sta_mtx);
+}
+
/*
* This function is called with state == IEEE80211_IBSS_MLME_JOINED
*/
static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ enum nl80211_bss_scan_width scan_width;
sdata_assert_lock(sdata);
mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
- ieee80211_sta_expire(sdata, IEEE80211_IBSS_INACTIVITY_LIMIT);
+ ieee80211_ibss_sta_expire(sdata);
if (time_before(jiffies, ifibss->last_scan_completed +
IEEE80211_IBSS_MERGE_INTERVAL))
sdata_info(sdata,
"No active IBSS STAs - trying to scan for other IBSS networks with same SSID (merge)\n");
+ scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len,
- NULL);
+ NULL, scan_width);
}
static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
struct cfg80211_bss *cbss;
struct ieee80211_channel *chan = NULL;
const u8 *bssid = NULL;
+ enum nl80211_bss_scan_width scan_width;
int active_ibss;
u16 capability;
return;
}
+ /* if a fixed bssid and a fixed freq have been provided create the IBSS
+ * directly and do not waste time scanning
+ */
+ if (ifibss->fixed_bssid && ifibss->fixed_channel) {
+ sdata_info(sdata, "Created IBSS using preconfigured BSSID %pM\n",
+ bssid);
+ ieee80211_sta_create_ibss(sdata);
+ return;
+ }
+
+
ibss_dbg(sdata, "sta_find_ibss: did not try to join ibss\n");
/* Selected IBSS not found in current scan results - try to scan */
IEEE80211_SCAN_INTERVAL)) {
sdata_info(sdata, "Trigger new scan to find an IBSS to join\n");
+ scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef);
ieee80211_request_ibss_scan(sdata, ifibss->ssid,
- ifibss->ssid_len, chan);
+ ifibss->ssid_len, chan,
+ scan_width);
} else {
int interval = IEEE80211_SCAN_INTERVAL;
struct cfg80211_ibss_params *params)
{
u32 changed = 0;
+ u32 rate_flags;
+ struct ieee80211_supported_band *sband;
+ int i;
if (params->bssid) {
memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
sdata->u.ibss.privacy = params->privacy;
sdata->u.ibss.control_port = params->control_port;
sdata->u.ibss.basic_rates = params->basic_rates;
+
+ /* fix basic_rates if channel does not support these rates */
+ rate_flags = ieee80211_chandef_rate_flags(¶ms->chandef);
+ sband = sdata->local->hw.wiphy->bands[params->chandef.chan->band];
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ sdata->u.ibss.basic_rates &= ~BIT(i);
+ }
memcpy(sdata->vif.bss_conf.mcast_rate, params->mcast_rate,
sizeof(params->mcast_rate));
memcpy(sdata->u.ibss.ssid, params->ssid, params->ssid_len);
sdata->u.ibss.ssid_len = params->ssid_len;
+ memcpy(&sdata->u.ibss.ht_capa, ¶ms->ht_capa,
+ sizeof(sdata->u.ibss.ht_capa));
+ memcpy(&sdata->u.ibss.ht_capa_mask, ¶ms->ht_capa_mask,
+ sizeof(sdata->u.ibss.ht_capa_mask));
+
/*
* 802.11n-2009 9.13.3.1: In an IBSS, the HT Protection field is
* reserved, but an HT STA shall protect HT transmissions as though
presp = rcu_dereference_protected(ifibss->presp,
lockdep_is_held(&sdata->wdev.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
+
+ /* on the next join, re-program HT parameters */
+ memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
+ memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
+
sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false;
sdata->vif.bss_conf.enable_beacon = false;
* increased memory use (about 2 kB of RAM per entry). */
#define IEEE80211_FRAGMENT_MAX 4
-#define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024))
-#define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x))
-
/* power level hasn't been configured (or set to automatic) */
#define IEEE80211_UNSET_POWER_LEVEL INT_MIN
struct beacon_data __rcu *beacon;
struct probe_resp __rcu *probe_resp;
+ /* to be used after channel switch. */
+ struct cfg80211_beacon_data *next_beacon;
struct list_head vlans;
struct ps_data ps;
/* probe response/beacon for IBSS */
struct beacon_data __rcu *presp;
+ struct ieee80211_ht_cap ht_capa; /* configured ht-cap over-rides */
+ struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */
+
spinlock_t incomplete_lock;
struct list_head incomplete_stations;
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
+ struct work_struct csa_finalize_work;
+ int csa_counter_offset_beacon;
+ int csa_counter_offset_presp;
+ bool csa_radar_required;
+
/* used to reconfigure hardware SM PS */
struct work_struct recalc_smps;
return band;
}
+static inline int
+ieee80211_chandef_get_shift(struct cfg80211_chan_def *chandef)
+{
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_5:
+ return 2;
+ case NL80211_CHAN_WIDTH_10:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static inline int
+ieee80211_vif_get_shift(struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ int shift = 0;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ if (chanctx_conf)
+ shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+ rcu_read_unlock();
+
+ return shift;
+}
+
enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0,
IEEE80211_SDATA_QUEUE_AGG_START = 1,
struct cfg80211_ssid scan_ssid;
struct cfg80211_scan_request *int_scan_req;
struct cfg80211_scan_request *scan_req, *hw_scan_req;
- struct ieee80211_channel *scan_channel;
+ struct cfg80211_chan_def scan_chandef;
enum ieee80211_band hw_scan_band;
int scan_channel_idx;
int scan_ies_len;
u32 dot11TransmittedFrameCount;
#ifdef CONFIG_MAC80211_LEDS
- int tx_led_counter, rx_led_counter;
struct led_trigger *tx_led, *rx_led, *assoc_led, *radio_led;
struct tpt_led_trigger *tpt_led_trigger;
char tx_led_name[32], rx_led_name[32],
void ieee80211_scan_work(struct work_struct *work);
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan);
+ struct ieee80211_channel *chan,
+ enum nl80211_bss_scan_width scan_width);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
void ieee80211_scan_cancel(struct ieee80211_local *local);
void ieee80211_sw_roc_work(struct work_struct *work);
void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc);
+/* channel switch handling */
+void ieee80211_csa_finalize_work(struct work_struct *work);
+
/* interface handling */
int ieee80211_iface_init(void);
void ieee80211_iface_exit(void);
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata);
+int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_beacon_data *params);
static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata)
{
u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
enum nl80211_iftype type);
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
- int rate, int erp, int short_preamble);
+ int rate, int erp, int short_preamble,
+ int shift);
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
struct ieee80211_hdr *hdr, const u8 *tsc,
gfp_t gfp);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
- u8 channel);
+ struct cfg80211_chan_def *chandef);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
u8 *dst, u32 ratemask,
struct ieee80211_channel *chan,
u32 ratemask, bool directed, u32 tx_flags,
struct ieee80211_channel *channel, bool scan);
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
- const size_t supp_rates_len,
- const u8 *supp_rates);
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
enum ieee80211_band band, u32 *basic_rates);
int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata,
u16 prot_mode);
u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap,
u32 cap);
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+ const struct ieee80211_supported_band *sband,
+ const u8 *srates, int srates_len, u32 *rates);
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic,
enum ieee80211_band band);
ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
const struct cfg80211_chan_def *chandef,
u32 *changed);
+/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
+int __must_check
+ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef,
+ u32 *changed);
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
return false;
}
- power = chanctx_conf->def.chan->max_power;
+ power = ieee80211_chandef_max_power(&chanctx_conf->def);
rcu_read_unlock();
if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL)
if (iftype == NL80211_IFTYPE_ADHOC &&
nsdata->vif.type == NL80211_IFTYPE_ADHOC)
return -EBUSY;
+ /*
+ * will not add another interface while any channel
+ * switch is active.
+ */
+ if (nsdata->vif.csa_active)
+ return -EBUSY;
/*
* The remaining checks are only performed for interfaces
cancel_work_sync(&local->dynamic_ps_enable_work);
cancel_work_sync(&sdata->recalc_smps);
+ sdata->vif.csa_active = false;
+ cancel_work_sync(&sdata->csa_finalize_work);
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
skb_queue_head_init(&sdata->skb_queue);
INIT_WORK(&sdata->work, ieee80211_iface_work);
INIT_WORK(&sdata->recalc_smps, ieee80211_recalc_smps_work);
+ INIT_WORK(&sdata->csa_finalize_work, ieee80211_csa_finalize_work);
switch (type) {
case NL80211_IFTYPE_P2P_GO:
might_sleep();
+ if (key->flags & KEY_FLAG_TAINTED)
+ return -EINVAL;
+
if (!key->local->ops->set_key)
goto out_unsupported;
struct ieee80211_sub_if_data *sdata,
struct sta_info *sta)
{
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_key *old_key;
int idx, ret;
bool pairwise;
ieee80211_debugfs_key_add(key);
- ret = ieee80211_key_enable_hw_accel(key);
-
- if (ret)
- ieee80211_key_free(key, true);
+ if (!local->wowlan) {
+ ret = ieee80211_key_enable_hw_accel(key);
+ if (ret)
+ ieee80211_key_free(key, true);
+ } else {
+ ret = 0;
+ }
mutex_unlock(&sdata->local->key_mtx);
void *iter_data)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_key *key;
+ struct ieee80211_key *key, *tmp;
struct ieee80211_sub_if_data *sdata;
ASSERT_RTNL();
mutex_lock(&local->key_mtx);
if (vif) {
sdata = vif_to_sdata(vif);
- list_for_each_entry(key, &sdata->key_list, list)
+ list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
iter(hw, &sdata->vif,
key->sta ? &key->sta->sta : NULL,
&key->conf, iter_data);
} else {
list_for_each_entry(sdata, &local->interfaces, list)
- list_for_each_entry(key, &sdata->key_list, list)
+ list_for_each_entry_safe(key, tmp,
+ &sdata->key_list, list)
iter(hw, &sdata->vif,
key->sta ? &key->sta->sta : NULL,
&key->conf, iter_data);
}
}
EXPORT_SYMBOL(ieee80211_get_key_rx_seq);
+
+void ieee80211_set_key_tx_seq(struct ieee80211_key_conf *keyconf,
+ struct ieee80211_key_seq *seq)
+{
+ struct ieee80211_key *key;
+ u64 pn64;
+
+ key = container_of(keyconf, struct ieee80211_key, conf);
+
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ key->u.tkip.tx.iv32 = seq->tkip.iv32;
+ key->u.tkip.tx.iv16 = seq->tkip.iv16;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ pn64 = (u64)seq->ccmp.pn[5] |
+ ((u64)seq->ccmp.pn[4] << 8) |
+ ((u64)seq->ccmp.pn[3] << 16) |
+ ((u64)seq->ccmp.pn[2] << 24) |
+ ((u64)seq->ccmp.pn[1] << 32) |
+ ((u64)seq->ccmp.pn[0] << 40);
+ atomic64_set(&key->u.ccmp.tx_pn, pn64);
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ pn64 = (u64)seq->aes_cmac.pn[5] |
+ ((u64)seq->aes_cmac.pn[4] << 8) |
+ ((u64)seq->aes_cmac.pn[3] << 16) |
+ ((u64)seq->aes_cmac.pn[2] << 24) |
+ ((u64)seq->aes_cmac.pn[1] << 32) |
+ ((u64)seq->aes_cmac.pn[0] << 40);
+ atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_key_tx_seq);
+
+void ieee80211_set_key_rx_seq(struct ieee80211_key_conf *keyconf,
+ int tid, struct ieee80211_key_seq *seq)
+{
+ struct ieee80211_key *key;
+ u8 *pn;
+
+ key = container_of(keyconf, struct ieee80211_key, conf);
+
+ switch (key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS))
+ return;
+ key->u.tkip.rx[tid].iv32 = seq->tkip.iv32;
+ key->u.tkip.rx[tid].iv16 = seq->tkip.iv16;
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS))
+ return;
+ if (tid < 0)
+ pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS];
+ else
+ pn = key->u.ccmp.rx_pn[tid];
+ memcpy(pn, seq->ccmp.pn, IEEE80211_CCMP_PN_LEN);
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ if (WARN_ON(tid != 0))
+ return;
+ pn = key->u.aes_cmac.rx_pn;
+ memcpy(pn, seq->aes_cmac.pn, IEEE80211_CMAC_PN_LEN);
+ break;
+ default:
+ WARN_ON(1);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ieee80211_set_key_rx_seq);
+
+void ieee80211_remove_key(struct ieee80211_key_conf *keyconf)
+{
+ struct ieee80211_key *key;
+
+ key = container_of(keyconf, struct ieee80211_key, conf);
+
+ assert_key_lock(key->local);
+
+ /*
+ * if key was uploaded, we assume the driver will/has remove(d)
+ * it, so adjust bookkeeping accordingly
+ */
+ if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
+
+ if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
+ (key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)))
+ increment_tailroom_need_count(key->sdata);
+ }
+
+ ieee80211_key_free(key, false);
+}
+EXPORT_SYMBOL_GPL(ieee80211_remove_key);
+
+struct ieee80211_key_conf *
+ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
+ struct ieee80211_key_conf *keyconf)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_key *key;
+ int err;
+
+ if (WARN_ON(!local->wowlan))
+ return ERR_PTR(-EINVAL);
+
+ if (WARN_ON(vif->type != NL80211_IFTYPE_STATION))
+ return ERR_PTR(-EINVAL);
+
+ key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
+ keyconf->keylen, keyconf->key,
+ 0, NULL);
+ if (IS_ERR(key))
+ return ERR_PTR(PTR_ERR(key));
+
+ if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
+ key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
+
+ err = ieee80211_key_link(key, sdata, NULL);
+ if (err)
+ return ERR_PTR(err);
+
+ return &key->conf;
+}
+EXPORT_SYMBOL_GPL(ieee80211_gtk_rekey_add);
#include <linux/export.h>
#include "led.h"
+#define MAC80211_BLINK_DELAY 50 /* ms */
+
void ieee80211_led_rx(struct ieee80211_local *local)
{
+ unsigned long led_delay = MAC80211_BLINK_DELAY;
if (unlikely(!local->rx_led))
return;
- if (local->rx_led_counter++ % 2 == 0)
- led_trigger_event(local->rx_led, LED_OFF);
- else
- led_trigger_event(local->rx_led, LED_FULL);
+ led_trigger_blink_oneshot(local->rx_led, &led_delay, &led_delay, 0);
}
-/* q is 1 if a packet was enqueued, 0 if it has been transmitted */
-void ieee80211_led_tx(struct ieee80211_local *local, int q)
+void ieee80211_led_tx(struct ieee80211_local *local)
{
+ unsigned long led_delay = MAC80211_BLINK_DELAY;
if (unlikely(!local->tx_led))
return;
- /* not sure how this is supposed to work ... */
- local->tx_led_counter += 2*q-1;
- if (local->tx_led_counter % 2 == 0)
- led_trigger_event(local->tx_led, LED_OFF);
- else
- led_trigger_event(local->tx_led, LED_FULL);
+ led_trigger_blink_oneshot(local->tx_led, &led_delay, &led_delay, 0);
}
void ieee80211_led_assoc(struct ieee80211_local *local, bool associated)
#ifdef CONFIG_MAC80211_LEDS
void ieee80211_led_rx(struct ieee80211_local *local);
-void ieee80211_led_tx(struct ieee80211_local *local, int q);
+void ieee80211_led_tx(struct ieee80211_local *local);
void ieee80211_led_assoc(struct ieee80211_local *local,
bool associated);
void ieee80211_led_radio(struct ieee80211_local *local,
static inline void ieee80211_led_rx(struct ieee80211_local *local)
{
}
-static inline void ieee80211_led_tx(struct ieee80211_local *local, int q)
+static inline void ieee80211_led_tx(struct ieee80211_local *local)
{
}
static inline void ieee80211_led_assoc(struct ieee80211_local *local,
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
- if (local->scan_channel) {
- chandef.chan = local->scan_channel;
- /* If scanning on oper channel, use whatever channel-type
- * is currently in use.
- */
- if (chandef.chan == local->_oper_chandef.chan) {
- chandef = local->_oper_chandef;
- } else {
- chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
- chandef.center_freq1 = chandef.chan->center_freq;
- }
+ if (local->scan_chandef.chan) {
+ chandef = local->scan_chandef;
} else if (local->tmp_channel) {
chandef.chan = local->tmp_channel;
chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
changed |= IEEE80211_CONF_CHANGE_SMPS;
}
- power = chandef.chan->max_power;
+ power = ieee80211_chandef_max_power(&chandef);
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
struct ieee802_11_elems *ie)
{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- struct ieee80211_local *local = sdata->local;
u32 basic_rates = 0;
struct cfg80211_chan_def sta_chan_def;
(ifmsh->mesh_auth_id == ie->mesh_config->meshconf_auth)))
return false;
- ieee80211_sta_get_rates(local, ie, ieee80211_get_sdata_band(sdata),
+ ieee80211_sta_get_rates(sdata, ie, ieee80211_get_sdata_band(sdata),
&basic_rates);
if (sdata->vif.bss_conf.basic_rates != basic_rates)
neighbors = min_t(int, neighbors, IEEE80211_MAX_MESH_PEERINGS);
*pos++ = neighbors << 1;
/* Mesh capability */
- *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING;
+ *pos = 0x00;
+ *pos |= ifmsh->mshcfg.dot11MeshForwarding ?
+ IEEE80211_MESHCONF_CAPAB_FORWARDING : 0x00;
*pos |= ifmsh->accepting_plinks ?
IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
/* Mesh PS mode. See IEEE802.11-2012 8.4.2.100.8 */
u32 rates, basic_rates = 0, changed = 0;
sband = local->hw.wiphy->bands[band];
- rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates);
+ rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates);
spin_lock_bh(&sta->lock);
sta->last_rx = jiffies;
/* frame sending functions */
-static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
- struct ieee80211_supported_band *sband,
- u32 *rates)
-{
- int i, j, count;
- *rates = 0;
- count = 0;
- for (i = 0; i < supp_rates_len; i++) {
- int rate = (supp_rates[i] & 0x7F) * 5;
-
- for (j = 0; j < sband->n_bitrates; j++)
- if (sband->bitrates[j].bitrate == rate) {
- *rates |= BIT(j);
- count++;
- break;
- }
- }
-
- return count;
-}
-
static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, u8 ap_ht_param,
struct ieee80211_supported_band *sband,
struct ieee80211_mgmt *mgmt;
u8 *pos, qos_info;
size_t offset = 0, noffset;
- int i, count, rates_len, supp_rates_len;
+ int i, count, rates_len, supp_rates_len, shift;
u16 capab;
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_channel *chan;
- u32 rates = 0;
+ u32 rate_flags, rates = 0;
sdata_assert_lock(sdata);
return;
}
chan = chanctx_conf->def.chan;
+ rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
rcu_read_unlock();
sband = local->hw.wiphy->bands[chan->band];
+ shift = ieee80211_vif_get_shift(&sdata->vif);
if (assoc_data->supp_rates_len) {
/*
* in the association request (e.g. D-Link DAP 1353 in
* b-only mode)...
*/
- rates_len = ieee80211_compatible_rates(assoc_data->supp_rates,
- assoc_data->supp_rates_len,
- sband, &rates);
+ rates_len = ieee80211_parse_bitrates(&chanctx_conf->def, sband,
+ assoc_data->supp_rates,
+ assoc_data->supp_rates_len,
+ &rates);
} else {
/*
* In case AP not provide any supported rates information
* before association, we send information element(s) with
* all rates that we support.
*/
- rates = ~0;
- rates_len = sband->n_bitrates;
+ rates_len = 0;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags)
+ != rate_flags)
+ continue;
+ rates |= BIT(i);
+ rates_len++;
+ }
}
skb = alloc_skb(local->hw.extra_tx_headroom +
count = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) {
- int rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
+ int rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+ 5 * (1 << shift));
+ *pos++ = (u8) rate;
if (++count == 8)
break;
}
for (i++; i < sband->n_bitrates; i++) {
if (BIT(i) & rates) {
- int rate = sband->bitrates[i].bitrate;
- *pos++ = (u8) (rate / 5);
+ int rate;
+ rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+ 5 * (1 << shift));
+ *pos++ = (u8) rate;
}
}
}
*pos++ = WLAN_EID_PWR_CAPABILITY;
*pos++ = 2;
*pos++ = 0; /* min tx power */
- *pos++ = chan->max_power; /* max tx power */
+ /* max tx power */
+ *pos++ = ieee80211_chandef_max_power(&chanctx_conf->def);
/* 2. supported channels */
/* TODO: get this in reg domain format */
case -1:
cfg80211_chandef_create(&new_chandef, new_chan,
NL80211_CHAN_NO_HT);
+ /* keep width for 5/10 MHz channels */
+ switch (sdata->vif.bss_conf.chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ new_chandef.width = sdata->vif.bss_conf.chandef.width;
+ break;
+ default:
+ break;
+ }
break;
}
u8 *supp_rates, unsigned int supp_rates_len,
u32 *rates, u32 *basic_rates,
bool *have_higher_than_11mbit,
- int *min_rate, int *min_rate_index)
+ int *min_rate, int *min_rate_index,
+ int shift, u32 rate_flags)
{
int i, j;
for (i = 0; i < supp_rates_len; i++) {
- int rate = (supp_rates[i] & 0x7f) * 5;
+ int rate = supp_rates[i] & 0x7f;
bool is_basic = !!(supp_rates[i] & 0x80);
- if (rate > 110)
+ if ((rate * 5 * (1 << shift)) > 110)
*have_higher_than_11mbit = true;
/*
continue;
for (j = 0; j < sband->n_bitrates; j++) {
- if (sband->bitrates[j].bitrate == rate) {
+ struct ieee80211_rate *br;
+ int brate;
+
+ br = &sband->bitrates[j];
+ if ((rate_flags & br->flags) != rate_flags)
+ continue;
+
+ brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+ if (brate == rate) {
*rates |= BIT(j);
if (is_basic)
*basic_rates |= BIT(j);
- if (rate < *min_rate) {
- *min_rate = rate;
+ if ((rate * 5) < *min_rate) {
+ *min_rate = rate * 5;
*min_rate_index = j;
}
break;
if (!new_sta)
return -ENOMEM;
}
-
if (new_sta) {
u32 rates = 0, basic_rates = 0;
bool have_higher_than_11mbit;
int min_rate = INT_MAX, min_rate_index = -1;
+ struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_supported_band *sband;
const struct cfg80211_bss_ies *ies;
+ int shift;
+ u32 rate_flags;
sband = local->hw.wiphy->bands[cbss->channel->band];
err = ieee80211_prep_channel(sdata, cbss);
if (err) {
sta_info_free(local, new_sta);
- return err;
+ return -EINVAL;
}
+ shift = ieee80211_vif_get_shift(&sdata->vif);
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+ rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+ rcu_read_unlock();
ieee80211_get_rates(sband, bss->supp_rates,
bss->supp_rates_len,
&rates, &basic_rates,
&have_higher_than_11mbit,
- &min_rate, &min_rate_index);
+ &min_rate, &min_rate_index,
+ shift, rate_flags);
/*
* This used to be a workaround for basic rates missing
!ieee80211_is_data(fc);
}
-static void rc_send_low_broadcast(s8 *idx, u32 basic_rates,
+static void rc_send_low_basicrate(s8 *idx, u32 basic_rates,
struct ieee80211_supported_band *sband)
{
u8 i;
/* could not find a basic rate; use original selection */
}
-static inline s8
-rate_lowest_non_cck_index(struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta)
+static void __rate_control_send_low(struct ieee80211_hw *hw,
+ struct ieee80211_supported_band *sband,
+ struct ieee80211_sta *sta,
+ struct ieee80211_tx_info *info)
{
int i;
+ u32 rate_flags =
+ ieee80211_chandef_rate_flags(&hw->conf.chandef);
+
+ if ((sband->band == IEEE80211_BAND_2GHZ) &&
+ (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
+ rate_flags |= IEEE80211_RATE_ERP_G;
+ info->control.rates[0].idx = 0;
for (i = 0; i < sband->n_bitrates; i++) {
- struct ieee80211_rate *srate = &sband->bitrates[i];
- if ((srate->bitrate == 10) || (srate->bitrate == 20) ||
- (srate->bitrate == 55) || (srate->bitrate == 110))
+ if (!rate_supported(sta, sband->band, i))
continue;
- if (rate_supported(sta, sband->band, i))
- return i;
+ info->control.rates[0].idx = i;
+ break;
}
-
- /* No matching rate found */
- return 0;
-}
-
-static void __rate_control_send_low(struct ieee80211_hw *hw,
- struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta,
- struct ieee80211_tx_info *info)
-{
- if ((sband->band != IEEE80211_BAND_2GHZ) ||
- !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
- info->control.rates[0].idx = rate_lowest_index(sband, sta);
- else
- info->control.rates[0].idx =
- rate_lowest_non_cck_index(sband, sta);
+ WARN_ON_ONCE(i == sband->n_bitrates);
info->control.rates[0].count =
(info->flags & IEEE80211_TX_CTL_NO_ACK) ?
}
-bool rate_control_send_low(struct ieee80211_sta *sta,
+bool rate_control_send_low(struct ieee80211_sta *pubsta,
void *priv_sta,
struct ieee80211_tx_rate_control *txrc)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
struct ieee80211_supported_band *sband = txrc->sband;
+ struct sta_info *sta;
int mcast_rate;
+ bool use_basicrate = false;
- if (!sta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
- __rate_control_send_low(txrc->hw, sband, sta, info);
+ if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) {
+ __rate_control_send_low(txrc->hw, sband, pubsta, info);
- if (!sta && txrc->bss) {
+ if (!pubsta && txrc->bss) {
mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
if (mcast_rate > 0) {
info->control.rates[0].idx = mcast_rate - 1;
return true;
}
+ use_basicrate = true;
+ } else if (pubsta) {
+ sta = container_of(pubsta, struct sta_info, sta);
+ if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+ use_basicrate = true;
+ }
- rc_send_low_broadcast(&info->control.rates[0].idx,
+ if (use_basicrate)
+ rc_send_low_basicrate(&info->control.rates[0].idx,
txrc->bss_conf->basic_rates,
sband);
- }
+
return true;
}
return false;
u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
bool has_mcs_mask;
u32 mask;
+ u32 rate_flags;
int i;
/*
*/
mask = sdata->rc_rateidx_mask[info->band];
has_mcs_mask = sdata->rc_has_mcs_mask[info->band];
+ rate_flags =
+ ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+ for (i = 0; i < sband->n_bitrates; i++)
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ mask &= ~BIT(i);
+
if (mask == (1 << sband->n_bitrates) - 1 && !has_mcs_mask)
return;
}
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
- rcu_read_unlock();
ieee80211_sta_set_rx_nss(sta);
- ref->ops->rate_init(ref->priv, sband, ista, priv_sta);
+ ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+ priv_sta);
+ rcu_read_unlock();
set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
}
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ if (ref && ref->ops->rate_update) {
+ rcu_read_lock();
- if (ref && ref->ops->rate_update)
- ref->ops->rate_update(ref->priv, sband, ista,
- priv_sta, changed);
+ chanctx_conf = rcu_dereference(sta->sdata->vif.chanctx_conf);
+ if (WARN_ON(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+ ista, priv_sta, changed);
+ rcu_read_unlock();
+ }
drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
}
static void
calc_rate_durations(enum ieee80211_band band,
struct minstrel_rate *d,
- struct ieee80211_rate *rate)
+ struct ieee80211_rate *rate,
+ struct cfg80211_chan_def *chandef)
{
int erp = !!(rate->flags & IEEE80211_RATE_ERP_G);
+ int shift = ieee80211_chandef_get_shift(chandef);
d->perfect_tx_time = ieee80211_frame_duration(band, 1200,
- rate->bitrate, erp, 1);
+ DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+ shift);
d->ack_time = ieee80211_frame_duration(band, 10,
- rate->bitrate, erp, 1);
+ DIV_ROUND_UP(rate->bitrate, 1 << shift), erp, 1,
+ shift);
}
static void
static void
minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband,
- struct ieee80211_sta *sta, void *priv_sta)
+ struct cfg80211_chan_def *chandef,
+ struct ieee80211_sta *sta, void *priv_sta)
{
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
struct ieee80211_rate *ctl_rate;
unsigned int i, n = 0;
unsigned int t_slot = 9; /* FIXME: get real slot time */
+ u32 rate_flags;
mi->sta = sta;
mi->lowest_rix = rate_lowest_index(sband, sta);
ctl_rate = &sband->bitrates[mi->lowest_rix];
mi->sp_ack_dur = ieee80211_frame_duration(sband->band, 10,
ctl_rate->bitrate,
- !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1);
+ !!(ctl_rate->flags & IEEE80211_RATE_ERP_G), 1,
+ ieee80211_chandef_get_shift(chandef));
+ rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
memset(mi->max_tp_rate, 0, sizeof(mi->max_tp_rate));
mi->max_prob_rate = 0;
unsigned int tx_time = 0, tx_time_cts = 0, tx_time_rtscts = 0;
unsigned int tx_time_single;
unsigned int cw = mp->cw_min;
+ int shift;
if (!rate_supported(sta, sband->band, i))
continue;
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+
n++;
memset(mr, 0, sizeof(*mr));
mr->rix = i;
- mr->bitrate = sband->bitrates[i].bitrate / 5;
- calc_rate_durations(sband->band, mr, &sband->bitrates[i]);
+ shift = ieee80211_chandef_get_shift(chandef);
+ mr->bitrate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+ (1 << shift) * 5);
+ calc_rate_durations(sband->band, mr, &sband->bitrates[i],
+ chandef);
/* calculate maximum number of retransmissions before
* fallback (based on maximum segment size) */
{
static const int bitrates[4] = { 10, 20, 55, 110 };
struct ieee80211_supported_band *sband;
+ u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
int i, j;
sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ];
if (rate->flags & IEEE80211_RATE_ERP_G)
continue;
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+
for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
if (rate->bitrate != bitrates[j])
continue;
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u16 tid;
if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
return;
- if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
+ if (unlikely(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
return;
tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
/* Don't use EAPOL frames for sampling on non-mrr hw */
if (mp->hw->max_rates == 1 &&
- txrc->skb->protocol == cpu_to_be16(ETH_P_PAE))
+ (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
sample_idx = -1;
else
sample_idx = minstrel_get_sample_rate(mp, mi);
static void
minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{
struct minstrel_priv *mp = priv;
mi->sta = sta;
mi->stats_update = jiffies;
- ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1);
- mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1) + ack_dur;
+ ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0);
+ mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0);
+ mi->overhead += ack_dur;
mi->overhead_rtscts = mi->overhead + 2 * ack_dur;
mi->avg_ampdu_len = MINSTREL_FRAC(1, 1);
memset(&msp->legacy, 0, sizeof(msp->legacy));
msp->legacy.r = msp->ratelist;
msp->legacy.sample_table = msp->sample_table;
- return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy);
+ return mac80211_minstrel.rate_init(priv, sband, chandef, sta,
+ &msp->legacy);
}
static void
minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{
- minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+ minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
}
static void
minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta,
u32 changed)
{
- minstrel_ht_update_caps(priv, sband, sta, priv_sta);
+ minstrel_ht_update_caps(priv, sband, chandef, sta, priv_sta);
}
static void *
static void
rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
+ struct cfg80211_chan_def *chandef,
struct ieee80211_sta *sta, void *priv_sta)
{
struct rc_pid_sta_info *spinfo = priv_sta;
int len;
/* always present fields */
- len = sizeof(struct ieee80211_radiotap_header) + 9;
+ len = sizeof(struct ieee80211_radiotap_header) + 8;
- /* allocate extra bitmap */
+ /* allocate extra bitmaps */
if (status->vendor_radiotap_len)
len += 4;
+ if (status->chains)
+ len += 4 * hweight8(status->chains);
if (ieee80211_have_rx_timestamp(status)) {
len = ALIGN(len, 8);
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
len += 1;
+ /* antenna field, if we don't have per-chain info */
+ if (!status->chains)
+ len += 1;
+
/* padding for RX_FLAGS if necessary */
len = ALIGN(len, 2);
len += 12;
}
+ if (status->chains) {
+ /* antenna and antenna signal fields */
+ len += 2 * hweight8(status->chains);
+ }
+
if (status->vendor_radiotap_len) {
if (WARN_ON_ONCE(status->vendor_radiotap_align == 0))
status->vendor_radiotap_align = 1;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
+ __le32 *it_present;
+ u32 it_present_val;
u16 rx_flags = 0;
- int mpdulen;
+ u16 channel_flags = 0;
+ int mpdulen, chain;
+ unsigned long chains = status->chains;
mpdulen = skb->len;
if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
memset(rthdr, 0, rtap_len);
+ it_present = &rthdr->it_present;
/* radiotap header, set always present flags */
- rthdr->it_present =
- cpu_to_le32((1 << IEEE80211_RADIOTAP_FLAGS) |
- (1 << IEEE80211_RADIOTAP_CHANNEL) |
- (1 << IEEE80211_RADIOTAP_ANTENNA) |
- (1 << IEEE80211_RADIOTAP_RX_FLAGS));
rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len);
+ it_present_val = BIT(IEEE80211_RADIOTAP_FLAGS) |
+ BIT(IEEE80211_RADIOTAP_CHANNEL) |
+ BIT(IEEE80211_RADIOTAP_RX_FLAGS);
+
+ if (!status->chains)
+ it_present_val |= BIT(IEEE80211_RADIOTAP_ANTENNA);
- pos = (unsigned char *)(rthdr + 1);
+ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+ it_present_val |=
+ BIT(IEEE80211_RADIOTAP_EXT) |
+ BIT(IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = BIT(IEEE80211_RADIOTAP_ANTENNA) |
+ BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
+ }
if (status->vendor_radiotap_len) {
- rthdr->it_present |=
- cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) |
- cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT));
- put_unaligned_le32(status->vendor_radiotap_bitmap, pos);
- pos += 4;
+ it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+ BIT(IEEE80211_RADIOTAP_EXT);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = status->vendor_radiotap_bitmap;
}
+ put_unaligned_le32(it_present_val, it_present);
+
+ pos = (void *)(it_present + 1);
+
/* the order of the following fields is important */
/* IEEE80211_RADIOTAP_TSFT */
*/
*pos = 0;
} else {
+ int shift = 0;
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
- *pos = rate->bitrate / 5;
+ if (status->flag & RX_FLAG_10MHZ)
+ shift = 1;
+ else if (status->flag & RX_FLAG_5MHZ)
+ shift = 2;
+ *pos = DIV_ROUND_UP(rate->bitrate, 5 * (1 << shift));
}
pos++;
/* IEEE80211_RADIOTAP_CHANNEL */
put_unaligned_le16(status->freq, pos);
pos += 2;
+ if (status->flag & RX_FLAG_10MHZ)
+ channel_flags |= IEEE80211_CHAN_HALF;
+ else if (status->flag & RX_FLAG_5MHZ)
+ channel_flags |= IEEE80211_CHAN_QUARTER;
+
if (status->band == IEEE80211_BAND_5GHZ)
- put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ;
else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT))
- put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
else if (rate && rate->flags & IEEE80211_RATE_ERP_G)
- put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
else if (rate)
- put_unaligned_le16(IEEE80211_CHAN_CCK | IEEE80211_CHAN_2GHZ,
- pos);
+ channel_flags |= IEEE80211_CHAN_OFDM | IEEE80211_CHAN_2GHZ;
else
- put_unaligned_le16(IEEE80211_CHAN_2GHZ, pos);
+ channel_flags |= IEEE80211_CHAN_2GHZ;
+ put_unaligned_le16(channel_flags, pos);
pos += 2;
/* IEEE80211_RADIOTAP_DBM_ANTSIGNAL */
/* IEEE80211_RADIOTAP_LOCK_QUALITY is missing */
- /* IEEE80211_RADIOTAP_ANTENNA */
- *pos = status->antenna;
- pos++;
+ if (!status->chains) {
+ /* IEEE80211_RADIOTAP_ANTENNA */
+ *pos = status->antenna;
+ pos++;
+ }
/* IEEE80211_RADIOTAP_DB_ANTNOISE is not used */
pos += 2;
}
+ for_each_set_bit(chain, &chains, IEEE80211_MAX_CHAINS) {
+ *pos++ = status->chain_signal[chain];
+ *pos++ = chain;
+ }
+
if (status->vendor_radiotap_len) {
/* ensure 2 byte alignment for the vendor field as required */
if ((pos - (u8 *)rthdr) & 1)
}
-static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
-{
- struct sk_buff *skb = rx->skb;
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- int keyidx;
- int hdrlen;
- ieee80211_rx_result result = RX_DROP_UNUSABLE;
- struct ieee80211_key *sta_ptk = NULL;
- int mmie_keyidx = -1;
- __le16 fc;
-
- /*
- * Key selection 101
- *
- * There are four types of keys:
- * - GTK (group keys)
- * - IGTK (group keys for management frames)
- * - PTK (pairwise keys)
- * - STK (station-to-station pairwise keys)
- *
- * When selecting a key, we have to distinguish between multicast
- * (including broadcast) and unicast frames, the latter can only
- * use PTKs and STKs while the former always use GTKs and IGTKs.
- * Unless, of course, actual WEP keys ("pre-RSNA") are used, then
- * unicast frames can also use key indices like GTKs. Hence, if we
- * don't have a PTK/STK we check the key index for a WEP key.
- *
- * Note that in a regular BSS, multicast frames are sent by the
- * AP only, associated stations unicast the frame to the AP first
- * which then multicasts it on their behalf.
- *
- * There is also a slight problem in IBSS mode: GTKs are negotiated
- * with each station, that is something we don't currently handle.
- * The spec seems to expect that one negotiates the same key with
- * every station but there's no such requirement; VLANs could be
- * possible.
- */
-
- /*
- * No point in finding a key and decrypting if the frame is neither
- * addressed to us nor a multicast frame.
- */
- if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
- return RX_CONTINUE;
-
- /* start without a key */
- rx->key = NULL;
-
- if (rx->sta)
- sta_ptk = rcu_dereference(rx->sta->ptk);
-
- fc = hdr->frame_control;
-
- if (!ieee80211_has_protected(fc))
- mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
-
- if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
- rx->key = sta_ptk;
- if ((status->flag & RX_FLAG_DECRYPTED) &&
- (status->flag & RX_FLAG_IV_STRIPPED))
- return RX_CONTINUE;
- /* Skip decryption if the frame is not protected. */
- if (!ieee80211_has_protected(fc))
- return RX_CONTINUE;
- } else if (mmie_keyidx >= 0) {
- /* Broadcast/multicast robust management frame / BIP */
- if ((status->flag & RX_FLAG_DECRYPTED) &&
- (status->flag & RX_FLAG_IV_STRIPPED))
- return RX_CONTINUE;
-
- if (mmie_keyidx < NUM_DEFAULT_KEYS ||
- mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
- return RX_DROP_MONITOR; /* unexpected BIP keyidx */
- if (rx->sta)
- rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
- if (!rx->key)
- rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
- } else if (!ieee80211_has_protected(fc)) {
- /*
- * The frame was not protected, so skip decryption. However, we
- * need to set rx->key if there is a key that could have been
- * used so that the frame may be dropped if encryption would
- * have been expected.
- */
- struct ieee80211_key *key = NULL;
- struct ieee80211_sub_if_data *sdata = rx->sdata;
- int i;
-
- if (ieee80211_is_mgmt(fc) &&
- is_multicast_ether_addr(hdr->addr1) &&
- (key = rcu_dereference(rx->sdata->default_mgmt_key)))
- rx->key = key;
- else {
- if (rx->sta) {
- for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
- key = rcu_dereference(rx->sta->gtk[i]);
- if (key)
- break;
- }
- }
- if (!key) {
- for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
- key = rcu_dereference(sdata->keys[i]);
- if (key)
- break;
- }
- }
- if (key)
- rx->key = key;
- }
- return RX_CONTINUE;
- } else {
- u8 keyid;
- /*
- * The device doesn't give us the IV so we won't be
- * able to look up the key. That's ok though, we
- * don't need to decrypt the frame, we just won't
- * be able to keep statistics accurate.
- * Except for key threshold notifications, should
- * we somehow allow the driver to tell us which key
- * the hardware used if this flag is set?
- */
- if ((status->flag & RX_FLAG_DECRYPTED) &&
- (status->flag & RX_FLAG_IV_STRIPPED))
- return RX_CONTINUE;
-
- hdrlen = ieee80211_hdrlen(fc);
-
- if (rx->skb->len < 8 + hdrlen)
- return RX_DROP_UNUSABLE; /* TODO: count this? */
-
- /*
- * no need to call ieee80211_wep_get_keyidx,
- * it verifies a bunch of things we've done already
- */
- skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
- keyidx = keyid >> 6;
-
- /* check per-station GTK first, if multicast packet */
- if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
- rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
-
- /* if not found, try default key */
- if (!rx->key) {
- rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
-
- /*
- * RSNA-protected unicast frames should always be
- * sent with pairwise or station-to-station keys,
- * but for WEP we allow using a key index as well.
- */
- if (rx->key &&
- rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
- rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
- !is_multicast_ether_addr(hdr->addr1))
- rx->key = NULL;
- }
- }
-
- if (rx->key) {
- if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
- return RX_DROP_MONITOR;
-
- rx->key->tx_rx_count++;
- /* TODO: add threshold stuff again */
- } else {
- return RX_DROP_MONITOR;
- }
-
- switch (rx->key->conf.cipher) {
- case WLAN_CIPHER_SUITE_WEP40:
- case WLAN_CIPHER_SUITE_WEP104:
- result = ieee80211_crypto_wep_decrypt(rx);
- break;
- case WLAN_CIPHER_SUITE_TKIP:
- result = ieee80211_crypto_tkip_decrypt(rx);
- break;
- case WLAN_CIPHER_SUITE_CCMP:
- result = ieee80211_crypto_ccmp_decrypt(rx);
- break;
- case WLAN_CIPHER_SUITE_AES_CMAC:
- result = ieee80211_crypto_aes_cmac_decrypt(rx);
- break;
- default:
- /*
- * We can reach here only with HW-only algorithms
- * but why didn't it decrypt the frame?!
- */
- return RX_DROP_UNUSABLE;
- }
-
- /* the hdr variable is invalid after the decrypt handlers */
-
- /* either the frame has been decrypted or will be dropped */
- status->flag |= RX_FLAG_DECRYPTED;
-
- return result;
-}
-
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_check_more_data(struct ieee80211_rx_data *rx)
{
return RX_CONTINUE;
} /* ieee80211_rx_h_sta_process */
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
+{
+ struct sk_buff *skb = rx->skb;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ int keyidx;
+ int hdrlen;
+ ieee80211_rx_result result = RX_DROP_UNUSABLE;
+ struct ieee80211_key *sta_ptk = NULL;
+ int mmie_keyidx = -1;
+ __le16 fc;
+
+ /*
+ * Key selection 101
+ *
+ * There are four types of keys:
+ * - GTK (group keys)
+ * - IGTK (group keys for management frames)
+ * - PTK (pairwise keys)
+ * - STK (station-to-station pairwise keys)
+ *
+ * When selecting a key, we have to distinguish between multicast
+ * (including broadcast) and unicast frames, the latter can only
+ * use PTKs and STKs while the former always use GTKs and IGTKs.
+ * Unless, of course, actual WEP keys ("pre-RSNA") are used, then
+ * unicast frames can also use key indices like GTKs. Hence, if we
+ * don't have a PTK/STK we check the key index for a WEP key.
+ *
+ * Note that in a regular BSS, multicast frames are sent by the
+ * AP only, associated stations unicast the frame to the AP first
+ * which then multicasts it on their behalf.
+ *
+ * There is also a slight problem in IBSS mode: GTKs are negotiated
+ * with each station, that is something we don't currently handle.
+ * The spec seems to expect that one negotiates the same key with
+ * every station but there's no such requirement; VLANs could be
+ * possible.
+ */
+
+ /*
+ * No point in finding a key and decrypting if the frame is neither
+ * addressed to us nor a multicast frame.
+ */
+ if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
+ return RX_CONTINUE;
+
+ /* start without a key */
+ rx->key = NULL;
+
+ if (rx->sta)
+ sta_ptk = rcu_dereference(rx->sta->ptk);
+
+ fc = hdr->frame_control;
+
+ if (!ieee80211_has_protected(fc))
+ mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
+
+ if (!is_multicast_ether_addr(hdr->addr1) && sta_ptk) {
+ rx->key = sta_ptk;
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
+ return RX_CONTINUE;
+ /* Skip decryption if the frame is not protected. */
+ if (!ieee80211_has_protected(fc))
+ return RX_CONTINUE;
+ } else if (mmie_keyidx >= 0) {
+ /* Broadcast/multicast robust management frame / BIP */
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
+ return RX_CONTINUE;
+
+ if (mmie_keyidx < NUM_DEFAULT_KEYS ||
+ mmie_keyidx >= NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS)
+ return RX_DROP_MONITOR; /* unexpected BIP keyidx */
+ if (rx->sta)
+ rx->key = rcu_dereference(rx->sta->gtk[mmie_keyidx]);
+ if (!rx->key)
+ rx->key = rcu_dereference(rx->sdata->keys[mmie_keyidx]);
+ } else if (!ieee80211_has_protected(fc)) {
+ /*
+ * The frame was not protected, so skip decryption. However, we
+ * need to set rx->key if there is a key that could have been
+ * used so that the frame may be dropped if encryption would
+ * have been expected.
+ */
+ struct ieee80211_key *key = NULL;
+ struct ieee80211_sub_if_data *sdata = rx->sdata;
+ int i;
+
+ if (ieee80211_is_mgmt(fc) &&
+ is_multicast_ether_addr(hdr->addr1) &&
+ (key = rcu_dereference(rx->sdata->default_mgmt_key)))
+ rx->key = key;
+ else {
+ if (rx->sta) {
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ key = rcu_dereference(rx->sta->gtk[i]);
+ if (key)
+ break;
+ }
+ }
+ if (!key) {
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ key = rcu_dereference(sdata->keys[i]);
+ if (key)
+ break;
+ }
+ }
+ if (key)
+ rx->key = key;
+ }
+ return RX_CONTINUE;
+ } else {
+ u8 keyid;
+ /*
+ * The device doesn't give us the IV so we won't be
+ * able to look up the key. That's ok though, we
+ * don't need to decrypt the frame, we just won't
+ * be able to keep statistics accurate.
+ * Except for key threshold notifications, should
+ * we somehow allow the driver to tell us which key
+ * the hardware used if this flag is set?
+ */
+ if ((status->flag & RX_FLAG_DECRYPTED) &&
+ (status->flag & RX_FLAG_IV_STRIPPED))
+ return RX_CONTINUE;
+
+ hdrlen = ieee80211_hdrlen(fc);
+
+ if (rx->skb->len < 8 + hdrlen)
+ return RX_DROP_UNUSABLE; /* TODO: count this? */
+
+ /*
+ * no need to call ieee80211_wep_get_keyidx,
+ * it verifies a bunch of things we've done already
+ */
+ skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
+ keyidx = keyid >> 6;
+
+ /* check per-station GTK first, if multicast packet */
+ if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
+ rx->key = rcu_dereference(rx->sta->gtk[keyidx]);
+
+ /* if not found, try default key */
+ if (!rx->key) {
+ rx->key = rcu_dereference(rx->sdata->keys[keyidx]);
+
+ /*
+ * RSNA-protected unicast frames should always be
+ * sent with pairwise or station-to-station keys,
+ * but for WEP we allow using a key index as well.
+ */
+ if (rx->key &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP40 &&
+ rx->key->conf.cipher != WLAN_CIPHER_SUITE_WEP104 &&
+ !is_multicast_ether_addr(hdr->addr1))
+ rx->key = NULL;
+ }
+ }
+
+ if (rx->key) {
+ if (unlikely(rx->key->flags & KEY_FLAG_TAINTED))
+ return RX_DROP_MONITOR;
+
+ rx->key->tx_rx_count++;
+ /* TODO: add threshold stuff again */
+ } else {
+ return RX_DROP_MONITOR;
+ }
+
+ switch (rx->key->conf.cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ result = ieee80211_crypto_wep_decrypt(rx);
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ result = ieee80211_crypto_tkip_decrypt(rx);
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ result = ieee80211_crypto_ccmp_decrypt(rx);
+ break;
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ result = ieee80211_crypto_aes_cmac_decrypt(rx);
+ break;
+ default:
+ /*
+ * We can reach here only with HW-only algorithms
+ * but why didn't it decrypt the frame?!
+ */
+ return RX_DROP_UNUSABLE;
+ }
+
+ /* the hdr variable is invalid after the decrypt handlers */
+
+ /* either the frame has been decrypted or will be dropped */
+ status->flag |= RX_FLAG_DECRYPTED;
+
+ return result;
+}
+
static inline struct ieee80211_fragment_entry *
ieee80211_reassemble_add(struct ieee80211_sub_if_data *sdata,
unsigned int frag, unsigned int seq, int rx_queue,
*/
rx->skb = skb;
- CALL_RXH(ieee80211_rx_h_decrypt)
CALL_RXH(ieee80211_rx_h_check_more_data)
CALL_RXH(ieee80211_rx_h_uapsd_and_pspoll)
CALL_RXH(ieee80211_rx_h_sta_process)
+ CALL_RXH(ieee80211_rx_h_decrypt)
CALL_RXH(ieee80211_rx_h_defragment)
CALL_RXH(ieee80211_rx_h_michael_mic_verify)
/* must be after MMIC verify so header is counted in MPDU mic */
struct cfg80211_bss *cbss;
struct ieee80211_bss *bss;
int clen, srlen;
+ enum nl80211_bss_scan_width scan_width;
s32 signal = 0;
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
signal = (rx_status->signal * 100) / local->hw.max_signal;
- cbss = cfg80211_inform_bss_frame(local->hw.wiphy, channel,
- mgmt, len, signal, GFP_ATOMIC);
+ scan_width = NL80211_BSS_CHAN_WIDTH_20;
+ if (rx_status->flag & RX_FLAG_5MHZ)
+ scan_width = NL80211_BSS_CHAN_WIDTH_5;
+ if (rx_status->flag & RX_FLAG_10MHZ)
+ scan_width = NL80211_BSS_CHAN_WIDTH_10;
+
+ cbss = cfg80211_inform_bss_width_frame(local->hw.wiphy, channel,
+ scan_width, mgmt, len, signal,
+ GFP_ATOMIC);
if (!cbss)
return NULL;
ieee80211_rx_bss_put(local, bss);
}
+static void
+ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
+ enum nl80211_bss_scan_width scan_width)
+{
+ memset(chandef, 0, sizeof(*chandef));
+ switch (scan_width) {
+ case NL80211_BSS_CHAN_WIDTH_5:
+ chandef->width = NL80211_CHAN_WIDTH_5;
+ break;
+ case NL80211_BSS_CHAN_WIDTH_10:
+ chandef->width = NL80211_CHAN_WIDTH_10;
+ break;
+ default:
+ chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+ break;
+ }
+}
+
/* return false if no more work */
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{
struct cfg80211_scan_request *req = local->scan_req;
+ struct cfg80211_chan_def chandef;
enum ieee80211_band band;
int i, ielen, n_chans;
} while (!n_chans);
local->hw_scan_req->n_channels = n_chans;
+ ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
local->hw_scan_ies_bufsize,
req->ie, req->ie_len, band,
- req->rates[band], 0);
+ req->rates[band], &chandef);
local->hw_scan_req->ie_len = ielen;
local->hw_scan_req->no_cck = req->no_cck;
rcu_assign_pointer(local->scan_sdata, NULL);
local->scanning = 0;
- local->scan_channel = NULL;
+ local->scan_chandef.chan = NULL;
/* Set power back to normal operating levels. */
ieee80211_hw_config(local, 0);
{
int skip;
struct ieee80211_channel *chan;
+ enum nl80211_bss_scan_width oper_scan_width;
skip = 0;
chan = local->scan_req->channels[local->scan_channel_idx];
- local->scan_channel = chan;
+ local->scan_chandef.chan = chan;
+ local->scan_chandef.center_freq1 = chan->center_freq;
+ local->scan_chandef.center_freq2 = 0;
+ switch (local->scan_req->scan_width) {
+ case NL80211_BSS_CHAN_WIDTH_5:
+ local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
+ break;
+ case NL80211_BSS_CHAN_WIDTH_10:
+ local->scan_chandef.width = NL80211_CHAN_WIDTH_10;
+ break;
+ case NL80211_BSS_CHAN_WIDTH_20:
+ /* If scanning on oper channel, use whatever channel-type
+ * is currently in use.
+ */
+ oper_scan_width = cfg80211_chandef_to_scan_width(
+ &local->_oper_chandef);
+ if (chan == local->_oper_chandef.chan &&
+ oper_scan_width == local->scan_req->scan_width)
+ local->scan_chandef = local->_oper_chandef;
+ else
+ local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
+ break;
+ }
if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
skip = 1;
unsigned long *next_delay)
{
/* switch back to the operating channel */
- local->scan_channel = NULL;
+ local->scan_chandef.chan = NULL;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
/* disable PS */
int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len,
- struct ieee80211_channel *chan)
+ struct ieee80211_channel *chan,
+ enum nl80211_bss_scan_width scan_width)
{
struct ieee80211_local *local = sdata->local;
int ret = -EBUSY;
local->int_scan_req->ssids = &local->scan_ssid;
local->int_scan_req->n_ssids = 1;
+ local->int_scan_req->scan_width = scan_width;
memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
local->int_scan_req->ssids[0].ssid_len = ssid_len;
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_sched_scan_ies sched_scan_ies = {};
+ struct cfg80211_chan_def chandef;
int ret, i, iebufsz;
iebufsz = 2 + IEEE80211_MAX_SSID_LEN +
goto out_free;
}
+ ieee80211_prepare_scan_chandef(&chandef, req->scan_width);
+
sched_scan_ies.len[i] =
ieee80211_build_preq_ies(local, sched_scan_ies.ie[i],
iebufsz, req->ie, req->ie_len,
- i, (u32) -1, 0);
+ i, (u32) -1, &chandef);
}
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
/* IEEE80211_RADIOTAP_RATE rate */
if (info->status.rates[0].idx >= 0 &&
- !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS))
+ !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+ IEEE80211_TX_RC_VHT_MCS)))
len += 2;
/* IEEE80211_RADIOTAP_TX_FLAGS */
/* IEEE80211_RADIOTAP_DATA_RETRIES */
len += 1;
- /* IEEE80211_TX_RC_MCS */
- if (info->status.rates[0].idx >= 0 &&
- info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
- len += 3;
+ /* IEEE80211_RADIOTAP_MCS
+ * IEEE80211_RADIOTAP_VHT */
+ if (info->status.rates[0].idx >= 0) {
+ if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS)
+ len += 3;
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS)
+ len = ALIGN(len, 2) + 12;
+ }
return len;
}
-static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band
- *sband, struct sk_buff *skb,
- int retry_count, int rtap_len)
+static void
+ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
+ struct ieee80211_supported_band *sband,
+ struct sk_buff *skb, int retry_count,
+ int rtap_len, int shift)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
/* IEEE80211_RADIOTAP_RATE */
if (info->status.rates[0].idx >= 0 &&
- !(info->status.rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ !(info->status.rates[0].flags & (IEEE80211_TX_RC_MCS |
+ IEEE80211_TX_RC_VHT_MCS))) {
+ u16 rate;
+
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE);
- *pos = sband->bitrates[info->status.rates[0].idx].bitrate / 5;
+ rate = sband->bitrates[info->status.rates[0].idx].bitrate;
+ *pos = DIV_ROUND_UP(rate, 5 * (1 << shift));
/* padding for tx flags */
pos += 2;
}
*pos = retry_count;
pos++;
- /* IEEE80211_TX_RC_MCS */
- if (info->status.rates[0].idx >= 0 &&
- info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
+ if (info->status.rates[0].idx < 0)
+ return;
+
+ /* IEEE80211_RADIOTAP_MCS
+ * IEEE80211_RADIOTAP_VHT */
+ if (info->status.rates[0].flags & IEEE80211_TX_RC_MCS) {
rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS);
pos[0] = IEEE80211_RADIOTAP_MCS_HAVE_MCS |
IEEE80211_RADIOTAP_MCS_HAVE_GI |
pos[1] |= IEEE80211_RADIOTAP_MCS_FMT_GF;
pos[2] = info->status.rates[0].idx;
pos += 3;
- }
+ } else if (info->status.rates[0].flags & IEEE80211_TX_RC_VHT_MCS) {
+ u16 known = local->hw.radiotap_vht_details &
+ (IEEE80211_RADIOTAP_VHT_KNOWN_GI |
+ IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH);
+
+ rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT);
+
+ /* required alignment from rthdr */
+ pos = (u8 *)rthdr + ALIGN(pos - (u8 *)rthdr, 2);
+ /* u16 known - IEEE80211_RADIOTAP_VHT_KNOWN_* */
+ put_unaligned_le16(known, pos);
+ pos += 2;
+
+ /* u8 flags - IEEE80211_RADIOTAP_VHT_FLAG_* */
+ if (info->status.rates[0].flags & IEEE80211_TX_RC_SHORT_GI)
+ *pos |= IEEE80211_RADIOTAP_VHT_FLAG_SGI;
+ pos++;
+
+ /* u8 bandwidth */
+ if (info->status.rates[0].flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ *pos = 1;
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ *pos = 4;
+ else if (info->status.rates[0].flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
+ *pos = 11;
+ else /* IEEE80211_TX_RC_{20_MHZ_WIDTH,FIXME:DUP_DATA} */
+ *pos = 0;
+ pos++;
+
+ /* u8 mcs_nss[4] */
+ *pos = (ieee80211_rate_get_vht_mcs(&info->status.rates[0]) << 4) |
+ ieee80211_rate_get_vht_nss(&info->status.rates[0]);
+ pos += 4;
+
+ /* u8 coding */
+ pos++;
+ /* u8 group_id */
+ pos++;
+ /* u16 partial_aid */
+ pos += 2;
+ }
}
static void ieee80211_report_used_skb(struct ieee80211_local *local,
bool acked;
struct ieee80211_bar *bar;
int rtap_len;
+ int shift = 0;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
continue;
+ shift = ieee80211_vif_get_shift(&sta->sdata->vif);
+
if (info->flags & IEEE80211_TX_STATUS_EOSP)
clear_sta_flag(sta, WLAN_STA_SP);
rcu_read_unlock();
- ieee80211_led_tx(local, 0);
+ ieee80211_led_tx(local);
/* SNMP counters
* Fragments are passed to low-level drivers as separate skbs, so these
dev_kfree_skb(skb);
return;
}
- ieee80211_add_tx_radiotap_header(sband, skb, retry_count, rtap_len);
+ ieee80211_add_tx_radiotap_header(local, sband, skb, retry_count,
+ rtap_len, shift);
/* XXX: is this sufficient for BPF? */
skb_set_mac_header(skb, 0);
)
);
+TRACE_EVENT(drv_channel_switch_beacon,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_chan_def *chandef),
+
+ TP_ARGS(local, sdata, chandef),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ CHANDEF_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ CHANDEF_ASSIGN(chandef);
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " channel switch to " CHANDEF_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG
+ )
+);
+
+
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mac80211_msg
struct sk_buff *skb, int group_addr,
int next_frag_len)
{
- int rate, mrate, erp, dur, i;
+ int rate, mrate, erp, dur, i, shift = 0;
struct ieee80211_rate *txrate;
struct ieee80211_local *local = tx->local;
struct ieee80211_supported_band *sband;
struct ieee80211_hdr *hdr;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ u32 rate_flags = 0;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(tx->sdata->vif.chanctx_conf);
+ if (chanctx_conf) {
+ shift = ieee80211_chandef_get_shift(&chanctx_conf->def);
+ rate_flags = ieee80211_chandef_rate_flags(&chanctx_conf->def);
+ }
+ rcu_read_unlock();
/* assume HW handles this */
if (tx->rate.flags & IEEE80211_TX_RC_MCS)
if (r->bitrate > txrate->bitrate)
break;
+ if ((rate_flags & r->flags) != rate_flags)
+ continue;
+
if (tx->sdata->vif.bss_conf.basic_rates & BIT(i))
- rate = r->bitrate;
+ rate = DIV_ROUND_UP(r->bitrate, 1 << shift);
switch (sband->band) {
case IEEE80211_BAND_2GHZ: {
if (rate == -1) {
/* No matching basic rate found; use highest suitable mandatory
* PHY rate */
- rate = mrate;
+ rate = DIV_ROUND_UP(mrate, 1 << shift);
}
/* Don't calculate ACKs for QoS Frames with NoAck Policy set */
* (10 bytes + 4-byte FCS = 112 bits) plus SIFS; rounded up
* to closest integer */
dur = ieee80211_frame_duration(sband->band, 10, rate, erp,
- tx->sdata->vif.bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble,
+ shift);
if (next_frag_len) {
/* Frame is fragmented: duration increases with time needed to
/* next fragment */
dur += ieee80211_frame_duration(sband->band, next_frag_len,
txrate->bitrate, erp,
- tx->sdata->vif.bss_conf.use_short_preamble);
+ tx->sdata->vif.bss_conf.use_short_preamble,
+ shift);
}
return cpu_to_le16(dur);
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
- if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol &&
- tx->sdata->control_port_no_encrypt))
- info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ if (unlikely(tx->sdata->control_port_protocol == tx->skb->protocol)) {
+ if (tx->sdata->control_port_no_encrypt)
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
+ }
return TX_CONTINUE;
}
switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR:
+ if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
+ vif = &sdata->vif;
+ break;
+ }
sdata = rcu_dereference(local->monitor_sdata);
if (sdata) {
vif = &sdata->vif;
txpending);
ieee80211_tpt_led_trig_tx(local, fc, led_len);
- ieee80211_led_tx(local, 1);
WARN_ON_ONCE(!skb_queue_empty(skbs));
return 0;
}
+void ieee80211_csa_finish(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ ieee80211_queue_work(&sdata->local->hw,
+ &sdata->csa_finalize_work);
+}
+EXPORT_SYMBOL(ieee80211_csa_finish);
+
+static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
+ struct beacon_data *beacon)
+{
+ struct probe_resp *resp;
+ int counter_offset_beacon = sdata->csa_counter_offset_beacon;
+ int counter_offset_presp = sdata->csa_counter_offset_presp;
+
+ /* warn if the driver did not check for/react to csa completeness */
+ if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
+ return;
+
+ ((u8 *)beacon->tail)[counter_offset_beacon]--;
+
+ if (sdata->vif.type == NL80211_IFTYPE_AP &&
+ counter_offset_presp) {
+ rcu_read_lock();
+ resp = rcu_dereference(sdata->u.ap.probe_resp);
+
+ /* if nl80211 accepted the offset, this should not happen. */
+ if (WARN_ON(!resp)) {
+ rcu_read_unlock();
+ return;
+ }
+ resp->data[counter_offset_presp]--;
+ rcu_read_unlock();
+ }
+}
+
+bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct beacon_data *beacon = NULL;
+ u8 *beacon_data;
+ size_t beacon_data_len;
+ int counter_beacon = sdata->csa_counter_offset_beacon;
+ int ret = false;
+
+ if (!ieee80211_sdata_running(sdata))
+ return false;
+
+ rcu_read_lock();
+ if (vif->type == NL80211_IFTYPE_AP) {
+ struct ieee80211_if_ap *ap = &sdata->u.ap;
+
+ beacon = rcu_dereference(ap->beacon);
+ if (WARN_ON(!beacon || !beacon->tail))
+ goto out;
+ beacon_data = beacon->tail;
+ beacon_data_len = beacon->tail_len;
+ } else {
+ WARN_ON(1);
+ goto out;
+ }
+
+ if (WARN_ON(counter_beacon > beacon_data_len))
+ goto out;
+
+ if (beacon_data[counter_beacon] == 0)
+ ret = true;
+ out:
+ rcu_read_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_csa_is_complete);
+
struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
u16 *tim_offset, u16 *tim_length)
struct beacon_data *beacon = rcu_dereference(ap->beacon);
if (beacon) {
+ if (sdata->vif.csa_active)
+ ieee80211_update_csa(sdata, beacon);
+
/*
* headroom, head length,
* tail length and maximum TIM length
}
int ieee80211_frame_duration(enum ieee80211_band band, size_t len,
- int rate, int erp, int short_preamble)
+ int rate, int erp, int short_preamble,
+ int shift)
{
int dur;
*
* rate is in 100 kbps, so divident is multiplied by 10 in the
* DIV_ROUND_UP() operations.
+ *
+ * shift may be 2 for 5 MHz channels or 1 for 10 MHz channels, and
+ * is assumed to be 0 otherwise.
*/
if (band == IEEE80211_BAND_5GHZ || erp) {
* TXTIME = T_PREAMBLE + T_SIGNAL + T_SYM x N_SYM + Signal Ext
*
* T_SYM = 4 usec
- * 802.11a - 17.5.2: aSIFSTime = 16 usec
+ * 802.11a - 18.5.2: aSIFSTime = 16 usec
* 802.11g - 19.8.4: aSIFSTime = 10 usec +
* signal ext = 6 usec
*/
dur = 16; /* SIFS + signal ext */
- dur += 16; /* 17.3.2.3: T_PREAMBLE = 16 usec */
- dur += 4; /* 17.3.2.3: T_SIGNAL = 4 usec */
+ dur += 16; /* IEEE 802.11-2012 18.3.2.4: T_PREAMBLE = 16 usec */
+ dur += 4; /* IEEE 802.11-2012 18.3.2.4: T_SIGNAL = 4 usec */
+
+ /* IEEE 802.11-2012 18.3.2.4: all values above are:
+ * * times 4 for 5 MHz
+ * * times 2 for 10 MHz
+ */
+ dur *= 1 << shift;
+
+ /* rates should already consider the channel bandwidth,
+ * don't apply divisor again.
+ */
dur += 4 * DIV_ROUND_UP((16 + 8 * (len + 4) + 6) * 10,
4 * rate); /* T_SYM x N_SYM */
} else {
{
struct ieee80211_sub_if_data *sdata;
u16 dur;
- int erp;
+ int erp, shift = 0;
bool short_preamble = false;
erp = 0;
short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
+ shift = ieee80211_vif_get_shift(vif);
}
dur = ieee80211_frame_duration(band, frame_len, rate->bitrate, erp,
- short_preamble);
+ short_preamble, shift);
return cpu_to_le16(dur);
}
struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata;
bool short_preamble;
- int erp;
+ int erp, shift = 0, bitrate;
u16 dur;
struct ieee80211_supported_band *sband;
short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
+ shift = ieee80211_vif_get_shift(vif);
}
+ bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
/* CTS duration */
- dur = ieee80211_frame_duration(sband->band, 10, rate->bitrate,
- erp, short_preamble);
+ dur = ieee80211_frame_duration(sband->band, 10, bitrate,
+ erp, short_preamble, shift);
/* Data frame duration */
- dur += ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
- erp, short_preamble);
+ dur += ieee80211_frame_duration(sband->band, frame_len, bitrate,
+ erp, short_preamble, shift);
/* ACK duration */
- dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
- erp, short_preamble);
+ dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+ erp, short_preamble, shift);
return cpu_to_le16(dur);
}
struct ieee80211_rate *rate;
struct ieee80211_sub_if_data *sdata;
bool short_preamble;
- int erp;
+ int erp, shift = 0, bitrate;
u16 dur;
struct ieee80211_supported_band *sband;
short_preamble = sdata->vif.bss_conf.use_short_preamble;
if (sdata->flags & IEEE80211_SDATA_OPERATING_GMODE)
erp = rate->flags & IEEE80211_RATE_ERP_G;
+ shift = ieee80211_vif_get_shift(vif);
}
+ bitrate = DIV_ROUND_UP(rate->bitrate, 1 << shift);
+
/* Data frame duration */
- dur = ieee80211_frame_duration(sband->band, frame_len, rate->bitrate,
- erp, short_preamble);
+ dur = ieee80211_frame_duration(sband->band, frame_len, bitrate,
+ erp, short_preamble, shift);
if (!(frame_txctl->flags & IEEE80211_TX_CTL_NO_ACK)) {
/* ACK duration */
- dur += ieee80211_frame_duration(sband->band, 10, rate->bitrate,
- erp, short_preamble);
+ dur += ieee80211_frame_duration(sband->band, 10, bitrate,
+ erp, short_preamble, shift);
}
return cpu_to_le16(dur);
}
}
-void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
- const size_t supp_rates_len,
- const u8 *supp_rates)
-{
- struct ieee80211_chanctx_conf *chanctx_conf;
- int i, have_higher_than_11mbit = 0;
-
- /* cf. IEEE 802.11 9.2.12 */
- for (i = 0; i < supp_rates_len; i++)
- if ((supp_rates[i] & 0x7f) * 5 > 110)
- have_higher_than_11mbit = 1;
-
- rcu_read_lock();
- chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
-
- if (chanctx_conf &&
- chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ &&
- have_higher_than_11mbit)
- sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
- else
- sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- rcu_read_unlock();
-
- ieee80211_set_wmm_default(sdata, true);
-}
-
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
const u8 *extra, size_t extra_len, const u8 *da,
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
size_t buffer_len, const u8 *ie, size_t ie_len,
enum ieee80211_band band, u32 rate_mask,
- u8 channel)
+ struct cfg80211_chan_def *chandef)
{
struct ieee80211_supported_band *sband;
u8 *pos = buffer, *end = buffer + buffer_len;
u8 rates[32];
int num_rates;
int ext_rates_len;
+ int shift;
+ u32 rate_flags;
sband = local->hw.wiphy->bands[band];
if (WARN_ON_ONCE(!sband))
return 0;
+ rate_flags = ieee80211_chandef_rate_flags(chandef);
+ shift = ieee80211_chandef_get_shift(chandef);
+
num_rates = 0;
for (i = 0; i < sband->n_bitrates; i++) {
if ((BIT(i) & rate_mask) == 0)
continue; /* skip rate */
- rates[num_rates++] = (u8) (sband->bitrates[i].bitrate / 5);
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+
+ rates[num_rates++] =
+ (u8) DIV_ROUND_UP(sband->bitrates[i].bitrate,
+ (1 << shift) * 5);
}
supp_rates_len = min_t(int, num_rates, 8);
pos += ext_rates_len;
}
- if (channel && sband->band == IEEE80211_BAND_2GHZ) {
+ if (chandef->chan && sband->band == IEEE80211_BAND_2GHZ) {
if (end - pos < 3)
goto out_err;
*pos++ = WLAN_EID_DS_PARAMS;
*pos++ = 1;
- *pos++ = channel;
+ *pos++ = ieee80211_frequency_to_channel(
+ chandef->chan->center_freq);
}
/* insert custom IEs that go before HT */
bool directed)
{
struct ieee80211_local *local = sdata->local;
+ struct cfg80211_chan_def chandef;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 chan_no;
int ies_len;
/*
* in order to maximize the chance that we get a response. Some
* badly-behaved APs don't respond when this parameter is included.
*/
+ chandef.width = sdata->vif.bss_conf.chandef.width;
if (directed)
- chan_no = 0;
+ chandef.chan = NULL;
else
- chan_no = ieee80211_frequency_to_channel(chan->center_freq);
+ chandef.chan = chan;
skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
ssid, ssid_len, 100 + ie_len);
ies_len = ieee80211_build_preq_ies(local, skb_tail_pointer(skb),
skb_tailroom(skb),
ie, ie_len, chan->band,
- ratemask, chan_no);
+ ratemask, &chandef);
skb_put(skb, ies_len);
if (dst) {
}
}
-u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
+u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
enum ieee80211_band band, u32 *basic_rates)
{
struct ieee80211_supported_band *sband;
struct ieee80211_rate *bitrates;
size_t num_rates;
- u32 supp_rates;
- int i, j;
- sband = local->hw.wiphy->bands[band];
+ u32 supp_rates, rate_flags;
+ int i, j, shift;
+ sband = sdata->local->hw.wiphy->bands[band];
+
+ rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+ shift = ieee80211_vif_get_shift(&sdata->vif);
if (WARN_ON(!sband))
return 1;
continue;
for (j = 0; j < num_rates; j++) {
- if (bitrates[j].bitrate == own_rate) {
+ int brate;
+ if ((rate_flags & sband->bitrates[j].flags)
+ != rate_flags)
+ continue;
+
+ brate = DIV_ROUND_UP(sband->bitrates[j].bitrate,
+ 1 << shift);
+
+ if (brate == own_rate) {
supp_rates |= BIT(j);
if (basic_rates && is_basic)
*basic_rates |= BIT(j);
local->resuming = true;
if (local->wowlan) {
- local->wowlan = false;
res = drv_resume(local);
+ local->wowlan = false;
if (res < 0) {
local->resuming = false;
return res;
cfg80211_chandef_create(chandef, control_chan, channel_type);
}
+int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef,
+ const struct ieee80211_supported_band *sband,
+ const u8 *srates, int srates_len, u32 *rates)
+{
+ u32 rate_flags = ieee80211_chandef_rate_flags(chandef);
+ int shift = ieee80211_chandef_get_shift(chandef);
+ struct ieee80211_rate *br;
+ int brate, rate, i, j, count = 0;
+
+ *rates = 0;
+
+ for (i = 0; i < srates_len; i++) {
+ rate = srates[i] & 0x7f;
+
+ for (j = 0; j < sband->n_bitrates; j++) {
+ br = &sband->bitrates[j];
+ if ((rate_flags & br->flags) != rate_flags)
+ continue;
+
+ brate = DIV_ROUND_UP(br->bitrate, (1 << shift) * 5);
+ if (brate == rate) {
+ *rates |= BIT(j);
+ count++;
+ break;
+ }
+ }
+ }
+ return count;
+}
+
int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, bool need_basic,
enum ieee80211_band band)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- int rate;
+ int rate, shift;
u8 i, rates, *pos;
u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+ u32 rate_flags;
+ shift = ieee80211_vif_get_shift(&sdata->vif);
+ rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
sband = local->hw.wiphy->bands[band];
- rates = sband->n_bitrates;
+ rates = 0;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+ rates++;
+ }
if (rates > 8)
rates = 8;
*pos++ = rates;
for (i = 0; i < rates; i++) {
u8 basic = 0;
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+
if (need_basic && basic_rates & BIT(i))
basic = 0x80;
rate = sband->bitrates[i].bitrate;
- *pos++ = basic | (u8) (rate / 5);
+ rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+ 5 * (1 << shift));
+ *pos++ = basic | (u8) rate;
}
return 0;
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- int rate;
+ int rate, skip, shift;
u8 i, exrates, *pos;
u32 basic_rates = sdata->vif.bss_conf.basic_rates;
+ u32 rate_flags;
+
+ rate_flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
+ shift = ieee80211_vif_get_shift(&sdata->vif);
sband = local->hw.wiphy->bands[band];
- exrates = sband->n_bitrates;
+ exrates = 0;
+ for (i = 0; i < sband->n_bitrates; i++) {
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
+ continue;
+ exrates++;
+ }
+
if (exrates > 8)
exrates -= 8;
else
pos = skb_put(skb, exrates + 2);
*pos++ = WLAN_EID_EXT_SUPP_RATES;
*pos++ = exrates;
+ skip = 0;
for (i = 8; i < sband->n_bitrates; i++) {
u8 basic = 0;
+ if ((rate_flags & sband->bitrates[i].flags)
+ != rate_flags)
+ continue;
+ if (skip++ < 8)
+ continue;
if (need_basic && basic_rates & BIT(i))
basic = 0x80;
- rate = sband->bitrates[i].bitrate;
- *pos++ = basic | (u8) (rate / 5);
+ rate = DIV_ROUND_UP(sband->bitrates[i].bitrate,
+ 5 * (1 << shift));
+ *pos++ = basic | (u8) rate;
}
}
return 0;
ri.flags |= RATE_INFO_FLAGS_SHORT_GI;
} else {
struct ieee80211_supported_band *sband;
+ int shift = 0;
+ int bitrate;
+
+ if (status->flag & RX_FLAG_10MHZ)
+ shift = 1;
+ if (status->flag & RX_FLAG_5MHZ)
+ shift = 2;
sband = local->hw.wiphy->bands[status->band];
- ri.legacy = sband->bitrates[status->rate_idx].bitrate;
+ bitrate = sband->bitrates[status->rate_idx].bitrate;
+ ri.legacy = DIV_ROUND_UP(bitrate, (1 << shift));
}
rate = cfg80211_calculate_bitrate(&ri);
return rc;
}
-int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
+/**
+ * nfc_fw_download_done - inform that a firmware download was completed
+ *
+ * @dev: The nfc device to which firmware was downloaded
+ * @firmware_name: The firmware filename
+ * @result: The positive value of a standard errno value
+ */
+int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+ u32 result)
{
dev->fw_download_in_progress = false;
- return nfc_genl_fw_download_done(dev, firmware_name);
+ return nfc_genl_fw_download_done(dev, firmware_name, result);
}
EXPORT_SYMBOL(nfc_fw_download_done);
/* We have to enable the device before discovering SEs */
if (dev->ops->discover_se) {
rc = dev->ops->discover_se(dev);
- if (!rc)
+ if (rc)
pr_warn("SE discovery failed\n");
}
goto error;
}
- if (se->type == NFC_SE_ENABLED) {
+ if (se->state == NFC_SE_ENABLED) {
rc = -EALREADY;
goto error;
}
rc = dev->ops->enable_se(dev, se_idx);
+ if (rc >= 0)
+ se->state = NFC_SE_ENABLED;
error:
device_unlock(&dev->dev);
goto error;
}
- if (se->type == NFC_SE_DISABLED) {
+ if (se->state == NFC_SE_DISABLED) {
rc = -EALREADY;
goto error;
}
rc = dev->ops->disable_se(dev, se_idx);
+ if (rc >= 0)
+ se->state = NFC_SE_DISABLED;
error:
device_unlock(&dev->dev);
struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
if (hdev->ops->disable_se)
- return hdev->ops->enable_se(hdev, se_idx);
+ return hdev->ops->disable_se(hdev, se_idx);
return 0;
}
return rc;
}
-int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name)
+int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+ u32 result)
{
struct sk_buff *msg;
void *hdr;
goto free_msg;
if (nla_put_string(msg, NFC_ATTR_FIRMWARE_NAME, firmware_name) ||
+ nla_put_u32(msg, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, result) ||
nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx))
goto nla_put_failure;
return rc;
}
+static int nfc_genl_send_se(struct sk_buff *msg, struct nfc_dev *dev,
+ u32 portid, u32 seq,
+ struct netlink_callback *cb,
+ int flags)
+{
+ void *hdr;
+ struct nfc_se *se, *n;
+
+ list_for_each_entry_safe(se, n, &dev->secure_elements, list) {
+ hdr = genlmsg_put(msg, portid, seq, &nfc_genl_family, flags,
+ NFC_CMD_GET_SE);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (cb)
+ genl_dump_check_consistent(cb, hdr, &nfc_genl_family);
+
+ if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, dev->idx) ||
+ nla_put_u32(msg, NFC_ATTR_SE_INDEX, se->idx) ||
+ nla_put_u8(msg, NFC_ATTR_SE_TYPE, se->type))
+ goto nla_put_failure;
+
+ if (genlmsg_end(msg, hdr) < 0)
+ goto nla_put_failure;
+ }
+
+ return 0;
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int nfc_genl_dump_ses(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+ struct nfc_dev *dev = (struct nfc_dev *) cb->args[1];
+ bool first_call = false;
+
+ if (!iter) {
+ first_call = true;
+ iter = kmalloc(sizeof(struct class_dev_iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+ cb->args[0] = (long) iter;
+ }
+
+ mutex_lock(&nfc_devlist_mutex);
+
+ cb->seq = nfc_devlist_generation;
+
+ if (first_call) {
+ nfc_device_iter_init(iter);
+ dev = nfc_device_iter_next(iter);
+ }
+
+ while (dev) {
+ int rc;
+
+ rc = nfc_genl_send_se(skb, dev, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, cb, NLM_F_MULTI);
+ if (rc < 0)
+ break;
+
+ dev = nfc_device_iter_next(iter);
+ }
+
+ mutex_unlock(&nfc_devlist_mutex);
+
+ cb->args[1] = (long) dev;
+
+ return skb->len;
+}
+
+static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
+{
+ struct class_dev_iter *iter = (struct class_dev_iter *) cb->args[0];
+
+ nfc_device_iter_exit(iter);
+ kfree(iter);
+
+ return 0;
+}
+
static struct genl_ops nfc_genl_ops[] = {
{
.cmd = NFC_CMD_GET_DEVICE,
.doit = nfc_genl_disable_se,
.policy = nfc_genl_policy,
},
+ {
+ .cmd = NFC_CMD_GET_SE,
+ .dumpit = nfc_genl_dump_ses,
+ .done = nfc_genl_dump_ses_done,
+ .policy = nfc_genl_policy,
+ },
};
}
int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name);
-int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name);
-
-int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name);
+int nfc_genl_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
+ u32 result);
int nfc_dev_up(struct nfc_dev *dev);
static int rfkill_regulator_set_block(void *data, bool blocked)
{
struct rfkill_regulator_data *rfkill_data = data;
+ int ret = 0;
pr_debug("%s: blocked: %d\n", __func__, blocked);
}
} else {
if (!rfkill_data->reg_enabled) {
- regulator_enable(rfkill_data->vcc);
- rfkill_data->reg_enabled = true;
+ ret = regulator_enable(rfkill_data->vcc);
+ if (!ret)
+ rfkill_data->reg_enabled = true;
}
}
pr_debug("%s: regulator_is_enabled after set_block: %d\n", __func__,
regulator_is_enabled(rfkill_data->vcc));
- return 0;
+ return ret;
}
static struct rfkill_ops rfkill_regulator_ops = {
return -EINVAL;
#endif
+ if (WARN_ON(wiphy->coalesce &&
+ (!wiphy->coalesce->n_rules ||
+ !wiphy->coalesce->n_patterns) &&
+ (!wiphy->coalesce->pattern_min_len ||
+ wiphy->coalesce->pattern_min_len >
+ wiphy->coalesce->pattern_max_len)))
+ return -EINVAL;
+
if (WARN_ON(wiphy->ap_sme_capa &&
!(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME)))
return -EINVAL;
rdev_set_wakeup(rdev, false);
#endif
cfg80211_rdev_free_wowlan(rdev);
+ cfg80211_rdev_free_coalesce(rdev);
}
EXPORT_SYMBOL(wiphy_unregister);
/* netlink port which started critical protocol (0 means not started) */
u32 crit_proto_nlportid;
+ struct cfg80211_coalesce *coalesce;
+
/* must be last because of the way we do wiphy_priv(),
* and it should at least be aligned to NETDEV_ALIGN */
struct wiphy wiphy __aligned(NETDEV_ALIGN);
* basic rates
*/
if (!setup->basic_rates) {
+ enum nl80211_bss_scan_width scan_width;
struct ieee80211_supported_band *sband =
rdev->wiphy.bands[setup->chandef.chan->band];
- setup->basic_rates = ieee80211_mandatory_rates(sband);
+ scan_width = cfg80211_chandef_to_scan_width(&setup->chandef);
+ setup->basic_rates = ieee80211_mandatory_rates(sband,
+ scan_width);
}
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef))
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
+ [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG },
+ [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
+ [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
+ [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
};
/* policy for the key attributes */
[NL80211_WOWLAN_TCP_WAKE_MASK] = { .len = 1 },
};
+/* policy for coalesce rule attributes */
+static const struct nla_policy
+nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = {
+ [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 },
+ [NL80211_ATTR_COALESCE_RULE_CONDITION] = { .type = NLA_U32 },
+ [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED },
+};
+
/* policy for GTK rekey offload attributes */
static const struct nla_policy
nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
return -ENOBUFS;
if (dev->wiphy.wowlan->n_patterns) {
- struct nl80211_wowlan_pattern_support pat = {
+ struct nl80211_pattern_support pat = {
.max_patterns = dev->wiphy.wowlan->n_patterns,
.min_pattern_len = dev->wiphy.wowlan->pattern_min_len,
.max_pattern_len = dev->wiphy.wowlan->pattern_max_len,
}
#endif
+static int nl80211_send_coalesce(struct sk_buff *msg,
+ struct cfg80211_registered_device *dev)
+{
+ struct nl80211_coalesce_rule_support rule;
+
+ if (!dev->wiphy.coalesce)
+ return 0;
+
+ rule.max_rules = dev->wiphy.coalesce->n_rules;
+ rule.max_delay = dev->wiphy.coalesce->max_delay;
+ rule.pat.max_patterns = dev->wiphy.coalesce->n_patterns;
+ rule.pat.min_pattern_len = dev->wiphy.coalesce->pattern_min_len;
+ rule.pat.max_pattern_len = dev->wiphy.coalesce->pattern_max_len;
+ rule.pat.max_pkt_offset = dev->wiphy.coalesce->max_pkt_offset;
+
+ if (nla_put(msg, NL80211_ATTR_COALESCE_RULE, sizeof(rule), &rule))
+ return -ENOBUFS;
+
+ return 0;
+}
+
static int nl80211_send_band_rateinfo(struct sk_buff *msg,
struct ieee80211_supported_band *sband)
{
if (state->split) {
CMD(crit_proto_start, CRIT_PROTOCOL_START);
CMD(crit_proto_stop, CRIT_PROTOCOL_STOP);
+ if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
+ CMD(channel_switch, CHANNEL_SWITCH);
}
#ifdef CONFIG_NL80211_TESTMODE
dev->wiphy.vht_capa_mod_mask))
goto nla_put_failure;
+ state->split_start++;
+ break;
+ case 10:
+ if (nl80211_send_coalesce(msg, dev))
+ goto nla_put_failure;
+
/* done */
state->split_start = 0;
break;
return err;
}
+static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_csa_settings params;
+ /* csa_attrs is defined static to avoid waste of stack size - this
+ * function is called under RTNL lock, so this should not be a problem.
+ */
+ static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
+ u8 radar_detect_width = 0;
+ int err;
+
+ if (!rdev->ops->channel_switch ||
+ !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ /* may add IBSS support later */
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
+ dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+ return -EOPNOTSUPP;
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
+ !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT])
+ return -EINVAL;
+
+ /* only important for AP, IBSS and mesh create IEs internally */
+ if (!info->attrs[NL80211_ATTR_CSA_IES])
+ return -EINVAL;
+
+ /* useless if AP is not running */
+ if (!wdev->beacon_interval)
+ return -EINVAL;
+
+ params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
+
+ err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
+ if (err)
+ return err;
+
+ err = nla_parse_nested(csa_attrs, NL80211_ATTR_MAX,
+ info->attrs[NL80211_ATTR_CSA_IES],
+ nl80211_policy);
+ if (err)
+ return err;
+
+ err = nl80211_parse_beacon(csa_attrs, ¶ms.beacon_csa);
+ if (err)
+ return err;
+
+ if (!csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON])
+ return -EINVAL;
+
+ params.counter_offset_beacon =
+ nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_BEACON]);
+ if (params.counter_offset_beacon >= params.beacon_csa.tail_len)
+ return -EINVAL;
+
+ /* sanity check - counters should be the same */
+ if (params.beacon_csa.tail[params.counter_offset_beacon] !=
+ params.count)
+ return -EINVAL;
+
+ if (csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]) {
+ params.counter_offset_presp =
+ nla_get_u16(csa_attrs[NL80211_ATTR_CSA_C_OFF_PRESP]);
+ if (params.counter_offset_presp >=
+ params.beacon_csa.probe_resp_len)
+ return -EINVAL;
+
+ if (params.beacon_csa.probe_resp[params.counter_offset_presp] !=
+ params.count)
+ return -EINVAL;
+ }
+
+ err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
+ if (err)
+ return err;
+
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
+ return -EINVAL;
+
+ err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
+ if (err < 0) {
+ return err;
+ } else if (err) {
+ radar_detect_width = BIT(params.chandef.width);
+ params.radar_required = true;
+ }
+
+ err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+ params.chandef.chan,
+ CHAN_MODE_SHARED,
+ radar_detect_width);
+ if (err)
+ return err;
+
+ if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
+ params.block_tx = true;
+
+ return rdev_channel_switch(rdev, dev, ¶ms);
+}
+
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
u32 seq, int flags,
struct cfg80211_registered_device *rdev,
goto nla_put_failure;
if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
+ nla_put_u32(msg, NL80211_BSS_CHAN_WIDTH, res->scan_width) ||
nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
jiffies_to_msecs(jiffies - intbss->ts)))
goto nla_put_failure;
return -EINVAL;
switch (ibss.chandef.width) {
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
break;
case NL80211_CHAN_WIDTH_20:
return err;
}
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ memcpy(&ibss.ht_capa_mask,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
+ sizeof(ibss.ht_capa_mask));
+
+ if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
+ if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
+ return -EINVAL;
+ memcpy(&ibss.ht_capa,
+ nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
+ sizeof(ibss.ht_capa));
+ }
+
if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
!nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct wireless_dev *wdev =
+ __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
int err;
+ if (!rdev->ops->testmode_cmd)
+ return -EOPNOTSUPP;
+
+ if (IS_ERR(wdev)) {
+ err = PTR_ERR(wdev);
+ if (err != -EINVAL)
+ return err;
+ wdev = NULL;
+ } else if (wdev->wiphy != &rdev->wiphy) {
+ return -EINVAL;
+ }
+
if (!info->attrs[NL80211_ATTR_TESTDATA])
return -EINVAL;
- err = -EOPNOTSUPP;
- if (rdev->ops->testmode_cmd) {
- rdev->testmode_info = info;
- err = rdev_testmode_cmd(rdev,
+ rdev->testmode_info = info;
+ err = rdev_testmode_cmd(rdev, wdev,
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
- rdev->testmode_info = NULL;
- }
+ rdev->testmode_info = NULL;
return err;
}
u32 rate, u32 pkts, u32 intvl)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
return -EINVAL;
- wdev = dev->ieee80211_ptr;
-
if (!rdev->ops->set_cqm_txe_config)
return -EOPNOTSUPP;
s32 threshold, u32 hysteresis)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct wireless_dev *wdev;
struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
if (threshold > 0)
return -EINVAL;
- wdev = dev->ieee80211_ptr;
+ /* disabling - hysteresis should also be zero then */
+ if (threshold == 0)
+ hysteresis = 0;
if (!rdev->ops->set_cqm_rssi_config)
return -EOPNOTSUPP;
int err;
cqm = info->attrs[NL80211_ATTR_CQM];
- if (!cqm) {
- err = -EINVAL;
- goto out;
- }
+ if (!cqm)
+ return -EINVAL;
err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
nl80211_attr_cqm_policy);
if (err)
- goto out;
+ return err;
if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
- s32 threshold;
- u32 hysteresis;
- threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
- hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
- err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
- } else if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
- attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
- attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
- u32 rate, pkts, intvl;
- rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
- pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
- intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
- err = nl80211_set_cqm_txe(info, rate, pkts, intvl);
- } else
- err = -EINVAL;
+ s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
-out:
- return err;
+ return nl80211_set_cqm_rssi(info, threshold, hysteresis);
+ }
+
+ if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
+ attrs[NL80211_ATTR_CQM_TXE_PKTS] &&
+ attrs[NL80211_ATTR_CQM_TXE_INTVL]) {
+ u32 rate = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_RATE]);
+ u32 pkts = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_PKTS]);
+ u32 intvl = nla_get_u32(attrs[NL80211_ATTR_CQM_TXE_INTVL]);
+
+ return nl80211_set_cqm_txe(info, rate, pkts, intvl);
+ }
+
+ return -EINVAL;
}
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
if (!nl_pat)
return -ENOBUFS;
pat_len = wowlan->patterns[i].pattern_len;
- if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
- DIV_ROUND_UP(pat_len, 8),
+ if (nla_put(msg, NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8),
wowlan->patterns[i].mask) ||
- nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
- pat_len, wowlan->patterns[i].pattern) ||
- nla_put_u32(msg, NL80211_WOWLAN_PKTPAT_OFFSET,
+ nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+ wowlan->patterns[i].pattern) ||
+ nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
wowlan->patterns[i].pkt_offset))
return -ENOBUFS;
nla_nest_end(msg, nl_pat);
struct nlattr *pat;
int n_patterns = 0;
int rem, pat_len, mask_len, pkt_offset;
- struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
+ struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem)
nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
rem) {
- nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
- nla_data(pat), nla_len(pat), NULL);
+ nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+ nla_len(pat), NULL);
err = -EINVAL;
- if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
- !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
+ if (!pat_tb[NL80211_PKTPAT_MASK] ||
+ !pat_tb[NL80211_PKTPAT_PATTERN])
goto error;
- pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
+ pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
mask_len = DIV_ROUND_UP(pat_len, 8);
- if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
- mask_len)
+ if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
goto error;
if (pat_len > wowlan->pattern_max_len ||
pat_len < wowlan->pattern_min_len)
goto error;
- if (!pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET])
+ if (!pat_tb[NL80211_PKTPAT_OFFSET])
pkt_offset = 0;
else
pkt_offset = nla_get_u32(
- pat_tb[NL80211_WOWLAN_PKTPAT_OFFSET]);
+ pat_tb[NL80211_PKTPAT_OFFSET]);
if (pkt_offset > wowlan->max_pkt_offset)
goto error;
new_triggers.patterns[i].pkt_offset = pkt_offset;
new_triggers.patterns[i].pattern =
new_triggers.patterns[i].mask + mask_len;
memcpy(new_triggers.patterns[i].mask,
- nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
+ nla_data(pat_tb[NL80211_PKTPAT_MASK]),
mask_len);
new_triggers.patterns[i].pattern_len = pat_len;
memcpy(new_triggers.patterns[i].pattern,
- nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
+ nla_data(pat_tb[NL80211_PKTPAT_PATTERN]),
pat_len);
i++;
}
}
#endif
+static int nl80211_send_coalesce_rules(struct sk_buff *msg,
+ struct cfg80211_registered_device *rdev)
+{
+ struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules;
+ int i, j, pat_len;
+ struct cfg80211_coalesce_rules *rule;
+
+ if (!rdev->coalesce->n_rules)
+ return 0;
+
+ nl_rules = nla_nest_start(msg, NL80211_ATTR_COALESCE_RULE);
+ if (!nl_rules)
+ return -ENOBUFS;
+
+ for (i = 0; i < rdev->coalesce->n_rules; i++) {
+ nl_rule = nla_nest_start(msg, i + 1);
+ if (!nl_rule)
+ return -ENOBUFS;
+
+ rule = &rdev->coalesce->rules[i];
+ if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_DELAY,
+ rule->delay))
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL80211_ATTR_COALESCE_RULE_CONDITION,
+ rule->condition))
+ return -ENOBUFS;
+
+ nl_pats = nla_nest_start(msg,
+ NL80211_ATTR_COALESCE_RULE_PKT_PATTERN);
+ if (!nl_pats)
+ return -ENOBUFS;
+
+ for (j = 0; j < rule->n_patterns; j++) {
+ nl_pat = nla_nest_start(msg, j + 1);
+ if (!nl_pat)
+ return -ENOBUFS;
+ pat_len = rule->patterns[j].pattern_len;
+ if (nla_put(msg, NL80211_PKTPAT_MASK,
+ DIV_ROUND_UP(pat_len, 8),
+ rule->patterns[j].mask) ||
+ nla_put(msg, NL80211_PKTPAT_PATTERN, pat_len,
+ rule->patterns[j].pattern) ||
+ nla_put_u32(msg, NL80211_PKTPAT_OFFSET,
+ rule->patterns[j].pkt_offset))
+ return -ENOBUFS;
+ nla_nest_end(msg, nl_pat);
+ }
+ nla_nest_end(msg, nl_pats);
+ nla_nest_end(msg, nl_rule);
+ }
+ nla_nest_end(msg, nl_rules);
+
+ return 0;
+}
+
+static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct sk_buff *msg;
+ void *hdr;
+
+ if (!rdev->wiphy.coalesce)
+ return -EOPNOTSUPP;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
+ NL80211_CMD_GET_COALESCE);
+ if (!hdr)
+ goto nla_put_failure;
+
+ if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev))
+ goto nla_put_failure;
+
+ genlmsg_end(msg, hdr);
+ return genlmsg_reply(msg, info);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev)
+{
+ struct cfg80211_coalesce *coalesce = rdev->coalesce;
+ int i, j;
+ struct cfg80211_coalesce_rules *rule;
+
+ if (!coalesce)
+ return;
+
+ for (i = 0; i < coalesce->n_rules; i++) {
+ rule = &coalesce->rules[i];
+ for (j = 0; j < rule->n_patterns; j++)
+ kfree(rule->patterns[j].mask);
+ kfree(rule->patterns);
+ }
+ kfree(coalesce->rules);
+ kfree(coalesce);
+ rdev->coalesce = NULL;
+}
+
+static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev,
+ struct nlattr *rule,
+ struct cfg80211_coalesce_rules *new_rule)
+{
+ int err, i;
+ const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+ struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat;
+ int rem, pat_len, mask_len, pkt_offset, n_patterns = 0;
+ struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
+
+ err = nla_parse(tb, NL80211_ATTR_COALESCE_RULE_MAX, nla_data(rule),
+ nla_len(rule), nl80211_coalesce_policy);
+ if (err)
+ return err;
+
+ if (tb[NL80211_ATTR_COALESCE_RULE_DELAY])
+ new_rule->delay =
+ nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_DELAY]);
+ if (new_rule->delay > coalesce->max_delay)
+ return -EINVAL;
+
+ if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION])
+ new_rule->condition =
+ nla_get_u32(tb[NL80211_ATTR_COALESCE_RULE_CONDITION]);
+ if (new_rule->condition != NL80211_COALESCE_CONDITION_MATCH &&
+ new_rule->condition != NL80211_COALESCE_CONDITION_NO_MATCH)
+ return -EINVAL;
+
+ if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN])
+ return -EINVAL;
+
+ nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+ rem)
+ n_patterns++;
+ if (n_patterns > coalesce->n_patterns)
+ return -EINVAL;
+
+ new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]),
+ GFP_KERNEL);
+ if (!new_rule->patterns)
+ return -ENOMEM;
+
+ new_rule->n_patterns = n_patterns;
+ i = 0;
+
+ nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN],
+ rem) {
+ nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
+ nla_len(pat), NULL);
+ if (!pat_tb[NL80211_PKTPAT_MASK] ||
+ !pat_tb[NL80211_PKTPAT_PATTERN])
+ return -EINVAL;
+ pat_len = nla_len(pat_tb[NL80211_PKTPAT_PATTERN]);
+ mask_len = DIV_ROUND_UP(pat_len, 8);
+ if (nla_len(pat_tb[NL80211_PKTPAT_MASK]) != mask_len)
+ return -EINVAL;
+ if (pat_len > coalesce->pattern_max_len ||
+ pat_len < coalesce->pattern_min_len)
+ return -EINVAL;
+
+ if (!pat_tb[NL80211_PKTPAT_OFFSET])
+ pkt_offset = 0;
+ else
+ pkt_offset = nla_get_u32(pat_tb[NL80211_PKTPAT_OFFSET]);
+ if (pkt_offset > coalesce->max_pkt_offset)
+ return -EINVAL;
+ new_rule->patterns[i].pkt_offset = pkt_offset;
+
+ new_rule->patterns[i].mask =
+ kmalloc(mask_len + pat_len, GFP_KERNEL);
+ if (!new_rule->patterns[i].mask)
+ return -ENOMEM;
+ new_rule->patterns[i].pattern =
+ new_rule->patterns[i].mask + mask_len;
+ memcpy(new_rule->patterns[i].mask,
+ nla_data(pat_tb[NL80211_PKTPAT_MASK]), mask_len);
+ new_rule->patterns[i].pattern_len = pat_len;
+ memcpy(new_rule->patterns[i].pattern,
+ nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), pat_len);
+ i++;
+ }
+
+ return 0;
+}
+
+static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce;
+ struct cfg80211_coalesce new_coalesce = {};
+ struct cfg80211_coalesce *n_coalesce;
+ int err, rem_rule, n_rules = 0, i, j;
+ struct nlattr *rule;
+ struct cfg80211_coalesce_rules *tmp_rule;
+
+ if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce)
+ return -EOPNOTSUPP;
+
+ if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) {
+ cfg80211_rdev_free_coalesce(rdev);
+ rdev->ops->set_coalesce(&rdev->wiphy, NULL);
+ return 0;
+ }
+
+ nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+ rem_rule)
+ n_rules++;
+ if (n_rules > coalesce->n_rules)
+ return -EINVAL;
+
+ new_coalesce.rules = kcalloc(n_rules, sizeof(new_coalesce.rules[0]),
+ GFP_KERNEL);
+ if (!new_coalesce.rules)
+ return -ENOMEM;
+
+ new_coalesce.n_rules = n_rules;
+ i = 0;
+
+ nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE],
+ rem_rule) {
+ err = nl80211_parse_coalesce_rule(rdev, rule,
+ &new_coalesce.rules[i]);
+ if (err)
+ goto error;
+
+ i++;
+ }
+
+ err = rdev->ops->set_coalesce(&rdev->wiphy, &new_coalesce);
+ if (err)
+ goto error;
+
+ n_coalesce = kmemdup(&new_coalesce, sizeof(new_coalesce), GFP_KERNEL);
+ if (!n_coalesce) {
+ err = -ENOMEM;
+ goto error;
+ }
+ cfg80211_rdev_free_coalesce(rdev);
+ rdev->coalesce = n_coalesce;
+
+ return 0;
+error:
+ for (i = 0; i < new_coalesce.n_rules; i++) {
+ tmp_rule = &new_coalesce.rules[i];
+ for (j = 0; j < tmp_rule->n_patterns; j++)
+ kfree(tmp_rule->patterns[j].mask);
+ kfree(tmp_rule->patterns);
+ }
+ kfree(new_coalesce.rules);
+
+ return err;
+}
+
static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
- }
+ },
+ {
+ .cmd = NL80211_CMD_GET_COALESCE,
+ .doit = nl80211_get_coalesce,
+ .policy = nl80211_policy,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_SET_COALESCE,
+ .doit = nl80211_set_coalesce,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_WIPHY |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_CHANNEL_SWITCH,
+ .doit = nl80211_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
+void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
+
#endif /* __NET_WIRELESS_NL80211_H */
#ifdef CONFIG_NL80211_TESTMODE
static inline int rdev_testmode_cmd(struct cfg80211_registered_device *rdev,
+ struct wireless_dev *wdev,
void *data, int len)
{
int ret;
- trace_rdev_testmode_cmd(&rdev->wiphy);
- ret = rdev->ops->testmode_cmd(&rdev->wiphy, data, len);
+ trace_rdev_testmode_cmd(&rdev->wiphy, wdev);
+ ret = rdev->ops->testmode_cmd(&rdev->wiphy, wdev, data, len);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
trace_rdev_return_void(&rdev->wiphy);
}
+static inline int rdev_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct cfg80211_csa_settings *params)
+{
+ int ret;
+
+ trace_rdev_channel_switch(&rdev->wiphy, dev, params);
+ ret = rdev->ops->channel_switch(&rdev->wiphy, dev, params);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
#endif /* __CFG80211_RDEV_OPS */
continue;
if (bss->pub.channel != new->pub.channel)
continue;
+ if (bss->pub.scan_width != new->pub.scan_width)
+ continue;
if (rcu_access_pointer(bss->pub.beacon_ies))
continue;
ies = rcu_access_pointer(bss->pub.ies);
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss*
-cfg80211_inform_bss(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
- const u8 *bssid, u64 tsf, u16 capability,
- u16 beacon_interval, const u8 *ie, size_t ielen,
- s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ enum nl80211_bss_scan_width scan_width,
+ const u8 *bssid, u64 tsf, u16 capability,
+ u16 beacon_interval, const u8 *ie, size_t ielen,
+ s32 signal, gfp_t gfp)
{
struct cfg80211_bss_ies *ies;
struct cfg80211_internal_bss tmp = {}, *res;
memcpy(tmp.pub.bssid, bssid, ETH_ALEN);
tmp.pub.channel = channel;
+ tmp.pub.scan_width = scan_width;
tmp.pub.signal = signal;
tmp.pub.beacon_interval = beacon_interval;
tmp.pub.capability = capability;
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
-EXPORT_SYMBOL(cfg80211_inform_bss);
+EXPORT_SYMBOL(cfg80211_inform_bss_width);
/* Returned bss is reference counted and must be cleaned up appropriately. */
struct cfg80211_bss *
-cfg80211_inform_bss_frame(struct wiphy *wiphy,
- struct ieee80211_channel *channel,
- struct ieee80211_mgmt *mgmt, size_t len,
- s32 signal, gfp_t gfp)
+cfg80211_inform_bss_width_frame(struct wiphy *wiphy,
+ struct ieee80211_channel *channel,
+ enum nl80211_bss_scan_width scan_width,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ s32 signal, gfp_t gfp)
{
struct cfg80211_internal_bss tmp = {}, *res;
struct cfg80211_bss_ies *ies;
BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) !=
offsetof(struct ieee80211_mgmt, u.beacon.variable));
- trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal);
+ trace_cfg80211_inform_bss_width_frame(wiphy, channel, scan_width, mgmt,
+ len, signal);
if (WARN_ON(!mgmt))
return NULL;
memcpy(tmp.pub.bssid, mgmt->bssid, ETH_ALEN);
tmp.pub.channel = channel;
+ tmp.pub.scan_width = scan_width;
tmp.pub.signal = signal;
tmp.pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
tmp.pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
/* cfg80211_bss_update gives us a referenced result */
return &res->pub;
}
-EXPORT_SYMBOL(cfg80211_inform_bss_frame);
+EXPORT_SYMBOL(cfg80211_inform_bss_width_frame);
void cfg80211_ref_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
{
#ifdef CONFIG_NL80211_TESTMODE
TRACE_EVENT(rdev_testmode_cmd,
- TP_PROTO(struct wiphy *wiphy),
- TP_ARGS(wiphy),
+ TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
+ TP_ARGS(wiphy, wdev),
TP_STRUCT__entry(
WIPHY_ENTRY
+ WDEV_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
+ WDEV_ASSIGN;
),
- TP_printk(WIPHY_PR_FMT, WIPHY_PR_ARG)
+ TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
);
TRACE_EVENT(rdev_testmode_dump,
WIPHY_PR_ARG, WDEV_PR_ARG)
);
+TRACE_EVENT(rdev_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct cfg80211_csa_settings *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ __field(u16, counter_offset_beacon)
+ __field(u16, counter_offset_presp)
+ __field(bool, radar_required)
+ __field(bool, block_tx)
+ __field(u8, count)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(¶ms->chandef);
+ __entry->counter_offset_beacon = params->counter_offset_beacon;
+ __entry->counter_offset_presp = params->counter_offset_presp;
+ __entry->radar_required = params->radar_required;
+ __entry->block_tx = params->block_tx;
+ __entry->count = params->count;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT
+ ", block_tx: %d, count: %u, radar_required: %d"
+ ", counter offsets (beacon/presp): %u/%u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_DEF_PR_ARG,
+ __entry->block_tx, __entry->count, __entry->radar_required,
+ __entry->counter_offset_beacon,
+ __entry->counter_offset_presp)
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
__entry->capa_mask, __entry->capa_val)
);
-TRACE_EVENT(cfg80211_inform_bss_frame,
+TRACE_EVENT(cfg80211_inform_bss_width_frame,
TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel,
+ enum nl80211_bss_scan_width scan_width,
struct ieee80211_mgmt *mgmt, size_t len,
s32 signal),
- TP_ARGS(wiphy, channel, mgmt, len, signal),
+ TP_ARGS(wiphy, channel, scan_width, mgmt, len, signal),
TP_STRUCT__entry(
WIPHY_ENTRY
CHAN_ENTRY
+ __field(enum nl80211_bss_scan_width, scan_width)
__dynamic_array(u8, mgmt, len)
__field(s32, signal)
),
TP_fast_assign(
WIPHY_ASSIGN;
CHAN_ASSIGN(channel);
+ __entry->scan_width = scan_width;
if (mgmt)
memcpy(__get_dynamic_array(mgmt), mgmt, len);
__entry->signal = signal;
),
- TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d",
- WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal)
+ TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "(scan_width: %d) signal: %d",
+ WIPHY_PR_ARG, CHAN_PR_ARG, __entry->scan_width,
+ __entry->signal)
);
DECLARE_EVENT_CLASS(cfg80211_bss_evt,
}
EXPORT_SYMBOL(ieee80211_get_response_rate);
-u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband)
+u32 ieee80211_mandatory_rates(struct ieee80211_supported_band *sband,
+ enum nl80211_bss_scan_width scan_width)
{
struct ieee80211_rate *bitrates;
u32 mandatory_rates = 0;
if (WARN_ON(!sband))
return 1;
- if (sband->band == IEEE80211_BAND_2GHZ)
- mandatory_flag = IEEE80211_RATE_MANDATORY_B;
- else
+ if (sband->band == IEEE80211_BAND_2GHZ) {
+ if (scan_width == NL80211_BSS_CHAN_WIDTH_5 ||
+ scan_width == NL80211_BSS_CHAN_WIDTH_10)
+ mandatory_flag = IEEE80211_RATE_MANDATORY_G;
+ else
+ mandatory_flag = IEEE80211_RATE_MANDATORY_B;
+ } else {
mandatory_flag = IEEE80211_RATE_MANDATORY_A;
+ }
bitrates = sband->bitrates;
for (i = 0; i < sband->n_bitrates; i++)