Merge branch 'for-john' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac802...
authorJohn W. Linville <linville@tuxdriver.com>
Mon, 13 Jan 2014 19:40:59 +0000 (14:40 -0500)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 13 Jan 2014 19:40:59 +0000 (14:40 -0500)
312 files changed:
MAINTAINERS
drivers/bcma/driver_chipcommon_sflash.c
drivers/bcma/main.c
drivers/bluetooth/ath3k.c
drivers/bluetooth/btsdio.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_vhci.c
drivers/net/wireless/adm8211.c
drivers/net/wireless/airo_cs.c
drivers/net/wireless/at76c50x-usb.c
drivers/net/wireless/ath/ar5523/ar5523.c
drivers/net/wireless/ath/ath10k/Kconfig
drivers/net/wireless/ath/ath10k/core.h
drivers/net/wireless/ath/ath10k/debug.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_rx.c
drivers/net/wireless/ath/ath10k/hw.h
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/trace.h
drivers/net/wireless/ath/ath10k/txrx.c
drivers/net/wireless/ath/ath10k/wmi.c
drivers/net/wireless/ath/ath10k/wmi.h
drivers/net/wireless/ath/ath5k/base.c
drivers/net/wireless/ath/ath9k/Makefile
drivers/net/wireless/ath/ath9k/antenna.c
drivers/net/wireless/ath/ath9k/ar9002_hw.c
drivers/net/wireless/ath/ath9k/ar9002_mac.c
drivers/net/wireless/ath/ath9k/ar9002_phy.c
drivers/net/wireless/ath/ath9k/ar9003_calib.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_eeprom.h
drivers/net/wireless/ath/ath9k/ar9003_hw.c
drivers/net/wireless/ath/ath9k/ar9003_mac.c
drivers/net/wireless/ath/ath9k/ar9003_phy.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/ar953x_initvals.h [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/beacon.c
drivers/net/wireless/ath/ath9k/btcoex.c
drivers/net/wireless/ath/ath9k/common.c
drivers/net/wireless/ath/ath9k/debug.c
drivers/net/wireless/ath/ath9k/debug.h
drivers/net/wireless/ath/ath9k/dfs.c
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/eeprom_9287.c
drivers/net/wireless/ath/ath9k/eeprom_def.c
drivers/net/wireless/ath/ath9k/gpio.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_beacon.c
drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
drivers/net/wireless/ath/ath9k/htc_drv_init.c
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/hw-ops.h
drivers/net/wireless/ath/ath9k/hw.c
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/init.c
drivers/net/wireless/ath/ath9k/link.c
drivers/net/wireless/ath/ath9k/mac.c
drivers/net/wireless/ath/ath9k/mac.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/mci.c
drivers/net/wireless/ath/ath9k/pci.c
drivers/net/wireless/ath/ath9k/recv.c
drivers/net/wireless/ath/ath9k/reg.h
drivers/net/wireless/ath/ath9k/spectral.c [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/spectral.h [new file with mode: 0644]
drivers/net/wireless/ath/ath9k/wow.c
drivers/net/wireless/ath/ath9k/xmit.c
drivers/net/wireless/ath/carl9170/debug.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/ath/carl9170/rx.c
drivers/net/wireless/ath/carl9170/tx.c
drivers/net/wireless/ath/wcn36xx/smd.c
drivers/net/wireless/ath/wil6210/interrupt.c
drivers/net/wireless/ath/wil6210/txrx.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/atmel.c
drivers/net/wireless/atmel_cs.c
drivers/net/wireless/atmel_pci.c
drivers/net/wireless/brcm80211/brcmfmac/Makefile
drivers/net/wireless/brcm80211/brcmfmac/bcdc.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c [deleted file]
drivers/net/wireless/brcm80211/brcmfmac/dhd.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
drivers/net/wireless/brcm80211/brcmfmac/fwil.c
drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c
drivers/net/wireless/brcm80211/brcmfmac/p2p.c
drivers/net/wireless/brcm80211/brcmfmac/proto.c
drivers/net/wireless/brcm80211/brcmfmac/proto.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c
drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h
drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
drivers/net/wireless/brcm80211/brcmfmac/usb.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
drivers/net/wireless/brcm80211/include/brcmu_wifi.h
drivers/net/wireless/cw1200/fwio.c
drivers/net/wireless/cw1200/main.c
drivers/net/wireless/cw1200/pm.c
drivers/net/wireless/hostap/hostap_cs.c
drivers/net/wireless/hostap/hostap_ioctl.c
drivers/net/wireless/hostap/hostap_pci.c
drivers/net/wireless/hostap/hostap_plx.c
drivers/net/wireless/ipw2x00/ipw2200.h
drivers/net/wireless/ipw2x00/libipw_rx.c
drivers/net/wireless/iwlegacy/3945-debug.c
drivers/net/wireless/iwlegacy/3945-rs.c
drivers/net/wireless/iwlegacy/3945.c
drivers/net/wireless/iwlegacy/4965-debug.c
drivers/net/wireless/iwlegacy/4965-rs.c
drivers/net/wireless/iwlegacy/4965.c
drivers/net/wireless/iwlegacy/common.c
drivers/net/wireless/iwlegacy/debug.c
drivers/net/wireless/iwlwifi/dvm/agn.h
drivers/net/wireless/iwlwifi/dvm/calib.c
drivers/net/wireless/iwlwifi/dvm/calib.h
drivers/net/wireless/iwlwifi/dvm/commands.h
drivers/net/wireless/iwlwifi/dvm/debugfs.c
drivers/net/wireless/iwlwifi/dvm/dev.h
drivers/net/wireless/iwlwifi/dvm/devices.c
drivers/net/wireless/iwlwifi/dvm/led.c
drivers/net/wireless/iwlwifi/dvm/led.h
drivers/net/wireless/iwlwifi/dvm/lib.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/dvm/main.c
drivers/net/wireless/iwlwifi/dvm/power.c
drivers/net/wireless/iwlwifi/dvm/power.h
drivers/net/wireless/iwlwifi/dvm/rs.c
drivers/net/wireless/iwlwifi/dvm/rs.h
drivers/net/wireless/iwlwifi/dvm/rx.c
drivers/net/wireless/iwlwifi/dvm/rxon.c
drivers/net/wireless/iwlwifi/dvm/scan.c
drivers/net/wireless/iwlwifi/dvm/sta.c
drivers/net/wireless/iwlwifi/dvm/tt.c
drivers/net/wireless/iwlwifi/dvm/tt.h
drivers/net/wireless/iwlwifi/dvm/tx.c
drivers/net/wireless/iwlwifi/dvm/ucode.c
drivers/net/wireless/iwlwifi/iwl-1000.c
drivers/net/wireless/iwlwifi/iwl-2000.c
drivers/net/wireless/iwlwifi/iwl-5000.c
drivers/net/wireless/iwlwifi/iwl-6000.c
drivers/net/wireless/iwlwifi/iwl-7000.c
drivers/net/wireless/iwlwifi/iwl-agn-hw.h
drivers/net/wireless/iwlwifi/iwl-config.h
drivers/net/wireless/iwlwifi/iwl-csr.h
drivers/net/wireless/iwlwifi/iwl-debug.h
drivers/net/wireless/iwlwifi/iwl-devtrace.c
drivers/net/wireless/iwlwifi/iwl-devtrace.h
drivers/net/wireless/iwlwifi/iwl-drv.c
drivers/net/wireless/iwlwifi/iwl-drv.h
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
drivers/net/wireless/iwlwifi/iwl-eeprom-parse.h
drivers/net/wireless/iwlwifi/iwl-eeprom-read.c
drivers/net/wireless/iwlwifi/iwl-eeprom-read.h
drivers/net/wireless/iwlwifi/iwl-fh.h
drivers/net/wireless/iwlwifi/iwl-fw-file.h
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/iwl-io.c
drivers/net/wireless/iwlwifi/iwl-io.h
drivers/net/wireless/iwlwifi/iwl-modparams.h
drivers/net/wireless/iwlwifi/iwl-notif-wait.c
drivers/net/wireless/iwlwifi/iwl-notif-wait.h
drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
drivers/net/wireless/iwlwifi/iwl-nvm-parse.h
drivers/net/wireless/iwlwifi/iwl-op-mode.h
drivers/net/wireless/iwlwifi/iwl-phy-db.c
drivers/net/wireless/iwlwifi/iwl-phy-db.h
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/iwl-trans.h
drivers/net/wireless/iwlwifi/mvm/Makefile
drivers/net/wireless/iwlwifi/mvm/binding.c
drivers/net/wireless/iwlwifi/mvm/bt-coex.c
drivers/net/wireless/iwlwifi/mvm/constants.h
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c
drivers/net/wireless/iwlwifi/mvm/debugfs.c
drivers/net/wireless/iwlwifi/mvm/debugfs.h
drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h
drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h
drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/led.c
drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/nvm.c
drivers/net/wireless/iwlwifi/mvm/ops.c
drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
drivers/net/wireless/iwlwifi/mvm/power.c
drivers/net/wireless/iwlwifi/mvm/power_legacy.c
drivers/net/wireless/iwlwifi/mvm/quota.c
drivers/net/wireless/iwlwifi/mvm/rs.c
drivers/net/wireless/iwlwifi/mvm/rs.h
drivers/net/wireless/iwlwifi/mvm/rx.c
drivers/net/wireless/iwlwifi/mvm/scan.c
drivers/net/wireless/iwlwifi/mvm/sf.c [new file with mode: 0644]
drivers/net/wireless/iwlwifi/mvm/sta.c
drivers/net/wireless/iwlwifi/mvm/sta.h
drivers/net/wireless/iwlwifi/mvm/testmode.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/mvm/time-event.h
drivers/net/wireless/iwlwifi/mvm/tt.c
drivers/net/wireless/iwlwifi/mvm/tx.c
drivers/net/wireless/iwlwifi/mvm/utils.c
drivers/net/wireless/iwlwifi/pcie/drv.c
drivers/net/wireless/iwlwifi/pcie/internal.h
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/iwlwifi/pcie/tx.c
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_cmd.c
drivers/net/wireless/mwifiex/sta_ioctl.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/orinoco/hermes.c
drivers/net/wireless/orinoco/orinoco_cs.c
drivers/net/wireless/orinoco/orinoco_usb.c
drivers/net/wireless/orinoco/spectrum_cs.c
drivers/net/wireless/p54/eeprom.c
drivers/net/wireless/p54/fwio.c
drivers/net/wireless/p54/led.c
drivers/net/wireless/p54/main.c
drivers/net/wireless/p54/p54pci.c
drivers/net/wireless/p54/p54usb.c
drivers/net/wireless/p54/txrx.c
drivers/net/wireless/rndis_wlan.c
drivers/net/wireless/rt2x00/rt2400pci.c
drivers/net/wireless/rt2x00/rt2500pci.c
drivers/net/wireless/rt2x00/rt2500usb.c
drivers/net/wireless/rt2x00/rt2800usb.c
drivers/net/wireless/rt2x00/rt2x00dev.c
drivers/net/wireless/rt2x00/rt61pci.c
drivers/net/wireless/rt2x00/rt73usb.c
drivers/net/wireless/rtl818x/rtl8180/dev.c
drivers/net/wireless/rtl818x/rtl8180/grf5101.c
drivers/net/wireless/rtl818x/rtl8180/max2820.c
drivers/net/wireless/rtl818x/rtl8180/rtl8225.c
drivers/net/wireless/rtl818x/rtl8180/sa2400.c
drivers/net/wireless/rtl818x/rtl8187/dev.c
drivers/net/wireless/rtl818x/rtl8187/rtl8225.c
drivers/net/wireless/rtlwifi/base.c
drivers/net/wireless/rtlwifi/pci.c
drivers/net/wireless/rtlwifi/ps.c
drivers/net/wireless/ti/wl1251/acx.c
drivers/net/wireless/ti/wl1251/acx.h
drivers/net/wireless/ti/wl1251/boot.c
drivers/net/wireless/ti/wl1251/cmd.c
drivers/net/wireless/ti/wl1251/cmd.h
drivers/net/wireless/ti/wl1251/event.c
drivers/net/wireless/ti/wl1251/event.h
drivers/net/wireless/ti/wl1251/init.c
drivers/net/wireless/ti/wl1251/main.c
drivers/net/wireless/ti/wl1251/rx.c
drivers/net/wireless/ti/wl1251/tx.c
drivers/net/wireless/ti/wl1251/wl1251.h
drivers/net/wireless/wl3501_cs.c
drivers/nfc/Kconfig
drivers/nfc/Makefile
drivers/nfc/mei_phy.c
drivers/nfc/nfcmrvl/Kconfig [new file with mode: 0644]
drivers/nfc/nfcmrvl/Makefile [new file with mode: 0644]
drivers/nfc/nfcmrvl/main.c [new file with mode: 0644]
drivers/nfc/nfcmrvl/nfcmrvl.h [new file with mode: 0644]
drivers/nfc/nfcmrvl/usb.c [new file with mode: 0644]
drivers/nfc/pn533.c
drivers/nfc/pn544/pn544.c
drivers/nfc/port100.c
drivers/ssb/driver_chipcommon_sflash.c
include/linux/mmc/sdio_ids.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/nfc/digital.h
include/net/nfc/nci_core.h
include/uapi/linux/if_arp.h
net/bluetooth/6lowpan.c [new file with mode: 0644]
net/bluetooth/6lowpan.h [new file with mode: 0644]
net/bluetooth/Makefile
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/hci_sock.c
net/bluetooth/l2cap_core.c
net/bluetooth/l2cap_sock.c
net/bluetooth/rfcomm/tty.c
net/ieee802154/6lowpan.c
net/ieee802154/6lowpan.h
net/ieee802154/6lowpan_iphc.c [new file with mode: 0644]
net/ieee802154/Makefile
net/ipv6/addrconf.c
net/nfc/core.c
net/nfc/digital_core.c
net/nfc/digital_dep.c
net/nfc/hci/core.c
net/nfc/llcp_commands.c
net/nfc/llcp_core.c
net/nfc/llcp_sock.c
net/nfc/nci/core.c
net/wireless/radiotap.c
net/wireless/sme.c

index bdf13e4..f6e9052 100644 (file)
@@ -1435,7 +1435,7 @@ F:        Documentation/aoe/
 F:     drivers/block/aoe/
 
 ATHEROS ATH GENERIC UTILITIES
-M:     "Luis R. Rodriguez" <mcgrof@qca.qualcomm.com>
+M:     "Luis R. Rodriguez" <mcgrof@do-not-panic.com>
 L:     linux-wireless@vger.kernel.org
 S:     Supported
 F:     drivers/net/wireless/ath/*
@@ -1443,7 +1443,7 @@ F:        drivers/net/wireless/ath/*
 ATHEROS ATH5K WIRELESS DRIVER
 M:     Jiri Slaby <jirislaby@gmail.com>
 M:     Nick Kossifidis <mickflemm@gmail.com>
-M:     "Luis R. Rodriguez" <mcgrof@qca.qualcomm.com>
+M:     "Luis R. Rodriguez" <mcgrof@do-not-panic.com>
 L:     linux-wireless@vger.kernel.org
 L:     ath5k-devel@lists.ath5k.org
 W:     http://wireless.kernel.org/en/users/Drivers/ath5k
index 4d07cce..7e11ef4 100644 (file)
@@ -38,7 +38,7 @@ static const struct bcma_sflash_tbl_e bcma_sflash_st_tbl[] = {
        { "M25P32", 0x15, 0x10000, 64, },
        { "M25P64", 0x16, 0x10000, 128, },
        { "M25FL128", 0x17, 0x10000, 256, },
-       { 0 },
+       { NULL },
 };
 
 static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
@@ -56,7 +56,7 @@ static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
        { "SST25VF016", 0x41, 0x1000, 512, },
        { "SST25VF032", 0x4a, 0x1000, 1024, },
        { "SST25VF064", 0x4b, 0x1000, 2048, },
-       { 0 },
+       { NULL },
 };
 
 static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
@@ -67,7 +67,7 @@ static const struct bcma_sflash_tbl_e bcma_sflash_at_tbl[] = {
        { "AT45DB161", 0x2c, 512, 4096, },
        { "AT45DB321", 0x34, 512, 8192, },
        { "AT45DB642", 0x3c, 1024, 8192, },
-       { 0 },
+       { NULL },
 };
 
 static void bcma_sflash_cmd(struct bcma_drv_cc *cc, u32 opcode)
index e15430a..5a9f6bd 100644 (file)
@@ -176,6 +176,7 @@ static int bcma_register_cores(struct bcma_bus *bus)
                        bcma_err(bus,
                                 "Could not register dev for core 0x%03X\n",
                                 core->id.id);
+                       put_device(&core->dev);
                        continue;
                }
                core->dev_registered = true;
index d3fdc32..106d1d8 100644 (file)
@@ -88,6 +88,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x0CF3, 0xE004) },
        { USB_DEVICE(0x0CF3, 0xE005) },
        { USB_DEVICE(0x0930, 0x0219) },
+       { USB_DEVICE(0x0930, 0x0220) },
        { USB_DEVICE(0x0489, 0xe057) },
        { USB_DEVICE(0x13d3, 0x3393) },
        { USB_DEVICE(0x0489, 0xe04e) },
@@ -132,6 +133,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
index b61440a..83f6437 100644 (file)
@@ -73,6 +73,7 @@ struct btsdio_data {
 #define REG_CL_INTRD 0x13      /* Interrupt Clear */
 #define REG_EN_INTRD 0x14      /* Interrupt Enable */
 #define REG_MD_STAT  0x20      /* Bluetooth Mode Status */
+#define REG_MD_SET   0x20      /* Bluetooth Mode Set */
 
 static int btsdio_tx_packet(struct btsdio_data *data, struct sk_buff *skb)
 {
@@ -212,7 +213,7 @@ static int btsdio_open(struct hci_dev *hdev)
        }
 
        if (data->func->class == SDIO_CLASS_BT_B)
-               sdio_writeb(data->func, 0x00, REG_MD_STAT, NULL);
+               sdio_writeb(data->func, 0x00, REG_MD_SET, NULL);
 
        sdio_writeb(data->func, 0x01, REG_EN_INTRD, NULL);
 
@@ -333,6 +334,9 @@ static int btsdio_probe(struct sdio_func *func,
        hdev->flush    = btsdio_flush;
        hdev->send     = btsdio_send_frame;
 
+       if (func->vendor == 0x0104 && func->device == 0x00c5)
+               set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
        err = hci_register_dev(hdev);
        if (err < 0) {
                hci_free_dev(hdev);
index bfbcc5a..baeaaed 100644 (file)
@@ -155,6 +155,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0cf3, 0xe005), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe04e), .driver_info = BTUSB_ATH3012 },
@@ -964,6 +965,45 @@ static int btusb_setup_bcm92035(struct hci_dev *hdev)
        return 0;
 }
 
+static int btusb_setup_csr(struct hci_dev *hdev)
+{
+       struct hci_rp_read_local_version *rp;
+       struct sk_buff *skb;
+       int ret;
+
+       BT_DBG("%s", hdev->name);
+
+       skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
+                            HCI_INIT_TIMEOUT);
+       if (IS_ERR(skb)) {
+               BT_ERR("Reading local version failed (%ld)", -PTR_ERR(skb));
+               return -PTR_ERR(skb);
+       }
+
+       rp = (struct hci_rp_read_local_version *) skb->data;
+
+       if (!rp->status) {
+               if (le16_to_cpu(rp->manufacturer) != 10) {
+                       /* Clear the reset quirk since this is not an actual
+                        * early Bluetooth 1.1 device from CSR.
+                        */
+                       clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+                       /* These fake CSR controllers have all a broken
+                        * stored link key handling and so just disable it.
+                        */
+                       set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY,
+                               &hdev->quirks);
+               }
+       }
+
+       ret = -bt_to_errno(rp->status);
+
+       kfree_skb(skb);
+
+       return ret;
+}
+
 struct intel_version {
        u8 status;
        u8 hw_platform;
@@ -1464,10 +1504,15 @@ static int btusb_probe(struct usb_interface *intf,
 
        if (id->driver_info & BTUSB_CSR) {
                struct usb_device *udev = data->udev;
+               u16 bcdDevice = le16_to_cpu(udev->descriptor.bcdDevice);
 
                /* Old firmware would otherwise execute USB reset */
-               if (le16_to_cpu(udev->descriptor.bcdDevice) < 0x117)
+               if (bcdDevice < 0x117)
                        set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
+
+               /* Fake CSR devices with broken commands */
+               if (bcdDevice <= 0x100)
+                       hdev->setup = btusb_setup_csr;
        }
 
        if (id->driver_info & BTUSB_SNIFFER) {
index 7b16738..1ef6990 100644 (file)
@@ -141,22 +141,28 @@ static int vhci_create_device(struct vhci_data *data, __u8 dev_type)
 }
 
 static inline ssize_t vhci_get_user(struct vhci_data *data,
-                                   const char __user *buf, size_t count)
+                                   const struct iovec *iov,
+                                   unsigned long count)
 {
+       size_t len = iov_length(iov, count);
        struct sk_buff *skb;
        __u8 pkt_type, dev_type;
+       unsigned long i;
        int ret;
 
-       if (count < 2 || count > HCI_MAX_FRAME_SIZE)
+       if (len < 2 || len > HCI_MAX_FRAME_SIZE)
                return -EINVAL;
 
-       skb = bt_skb_alloc(count, GFP_KERNEL);
+       skb = bt_skb_alloc(len, GFP_KERNEL);
        if (!skb)
                return -ENOMEM;
 
-       if (copy_from_user(skb_put(skb, count), buf, count)) {
-               kfree_skb(skb);
-               return -EFAULT;
+       for (i = 0; i < count; i++) {
+               if (copy_from_user(skb_put(skb, iov[i].iov_len),
+                                  iov[i].iov_base, iov[i].iov_len)) {
+                       kfree_skb(skb);
+                       return -EFAULT;
+               }
        }
 
        pkt_type = *((__u8 *) skb->data);
@@ -205,7 +211,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data,
                return -EINVAL;
        }
 
-       return (ret < 0) ? ret : count;
+       return (ret < 0) ? ret : len;
 }
 
 static inline ssize_t vhci_put_user(struct vhci_data *data,
@@ -272,12 +278,13 @@ static ssize_t vhci_read(struct file *file,
        return ret;
 }
 
-static ssize_t vhci_write(struct file *file,
-                         const char __user *buf, size_t count, loff_t *pos)
+static ssize_t vhci_write(struct kiocb *iocb, const struct iovec *iov,
+                         unsigned long count, loff_t pos)
 {
+       struct file *file = iocb->ki_filp;
        struct vhci_data *data = file->private_data;
 
-       return vhci_get_user(data, buf, count);
+       return vhci_get_user(data, iov, count);
 }
 
 static unsigned int vhci_poll(struct file *file, poll_table *wait)
@@ -342,7 +349,7 @@ static int vhci_release(struct inode *inode, struct file *file)
 static const struct file_operations vhci_fops = {
        .owner          = THIS_MODULE,
        .read           = vhci_read,
-       .write          = vhci_write,
+       .aio_write      = vhci_write,
        .poll           = vhci_poll,
        .open           = vhci_open,
        .release        = vhci_release,
index 54afde0..84e5723 100644 (file)
@@ -15,7 +15,6 @@
  * more details.
  */
 
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/if.h>
 #include <linux/skbuff.h>
index 14128fd..7e9ede6 100644 (file)
@@ -23,7 +23,6 @@
 #ifdef __IN_PCMCIA_PACKAGE__
 #include <pcmcia/k_compat.h>
 #endif
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/ptrace.h>
index 8cfd1d6..99b3bfa 100644 (file)
@@ -1721,7 +1721,7 @@ static void at76_mac80211_tx(struct ieee80211_hw *hw,
         * following workaround is necessary. If the TX frame is an
         * authentication frame extract the bssid and send the CMD_JOIN. */
        if (mgmt->frame_control & cpu_to_le16(IEEE80211_STYPE_AUTH)) {
-               if (!ether_addr_equal(priv->bssid, mgmt->bssid)) {
+               if (!ether_addr_equal_64bits(priv->bssid, mgmt->bssid)) {
                        memcpy(priv->bssid, mgmt->bssid, ETH_ALEN);
                        ieee80211_queue_work(hw, &priv->work_join_bssid);
                        dev_kfree_skb_any(skb);
index 280fc3d..8aa20df 100644 (file)
@@ -25,7 +25,6 @@
  * that and only has minimal functionality.
  */
 #include <linux/compiler.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/list.h>
index 82e8088..a6f5285 100644 (file)
@@ -37,3 +37,10 @@ config ATH10K_TRACING
        ---help---
          Select this to ath10k use tracing infrastructure.
 
+config ATH10K_DFS_CERTIFIED
+       bool "Atheros DFS support for certified platforms"
+       depends on ATH10K && CFG80211_CERTIFICATION_ONUS
+       default n
+       ---help---
+       This option enables DFS support for initiating radiation on
+       ath10k.
index 79726e0..ade1781 100644 (file)
@@ -253,6 +253,9 @@ struct ath10k_vif {
                        u8 bssid[ETH_ALEN];
                } ibss;
        } u;
+
+       u8 fixed_rate;
+       u8 fixed_nss;
 };
 
 struct ath10k_vif_iter {
@@ -272,6 +275,8 @@ struct ath10k_debug {
        struct delayed_work htt_stats_dwork;
        struct ath10k_dfs_stats dfs_stats;
        struct ath_dfs_pool_stats dfs_pool_stats;
+
+       u32 fw_dbglog_mask;
 };
 
 enum ath10k_state {
@@ -306,6 +311,9 @@ enum ath10k_fw_features {
        /* firmware support tx frame management over WMI, otherwise it's HTT */
        ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2,
 
+       /* Firmware does not support P2P */
+       ATH10K_FW_FEATURE_NO_P2P = 3,
+
        /* keep last */
        ATH10K_FW_FEATURE_COUNT,
 };
@@ -429,6 +437,9 @@ struct ath10k {
        struct list_head peers;
        wait_queue_head_t peer_mapping_wq;
 
+       /* number of created peers; protected by data_lock */
+       int num_peers;
+
        struct work_struct offchan_tx_work;
        struct sk_buff_head offchan_tx_queue;
        struct completion offchan_tx_completed;
index 6bdfad3..6debd28 100644 (file)
@@ -614,6 +614,61 @@ static const struct file_operations fops_htt_stats_mask = {
        .llseek = default_llseek,
 };
 
+static ssize_t ath10k_read_fw_dbglog(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       unsigned int len;
+       char buf[32];
+
+       len = scnprintf(buf, sizeof(buf), "0x%08x\n",
+                       ar->debug.fw_dbglog_mask);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t ath10k_write_fw_dbglog(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct ath10k *ar = file->private_data;
+       unsigned long mask;
+       int ret;
+
+       ret = kstrtoul_from_user(user_buf, count, 0, &mask);
+       if (ret)
+               return ret;
+
+       mutex_lock(&ar->conf_mutex);
+
+       ar->debug.fw_dbglog_mask = mask;
+
+       if (ar->state == ATH10K_STATE_ON) {
+               ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
+               if (ret) {
+                       ath10k_warn("dbglog cfg failed from debugfs: %d\n",
+                                   ret);
+                       goto exit;
+               }
+       }
+
+       ret = count;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+
+       return ret;
+}
+
+static const struct file_operations fops_fw_dbglog = {
+       .read = ath10k_read_fw_dbglog,
+       .write = ath10k_write_fw_dbglog,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
 int ath10k_debug_start(struct ath10k *ar)
 {
        int ret;
@@ -625,6 +680,14 @@ int ath10k_debug_start(struct ath10k *ar)
                /* continue normally anyway, this isn't serious */
                ath10k_warn("failed to start htt stats workqueue: %d\n", ret);
 
+       if (ar->debug.fw_dbglog_mask) {
+               ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask);
+               if (ret)
+                       /* not serious */
+                       ath10k_warn("failed to enable dbglog during start: %d",
+                                   ret);
+       }
+
        return 0;
 }
 
@@ -747,6 +810,9 @@ int ath10k_debug_create(struct ath10k *ar)
        debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy,
                            ar, &fops_htt_stats_mask);
 
+       debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy,
+                           ar, &fops_fw_dbglog);
+
        if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) {
                debugfs_create_file("dfs_simulate_radar", S_IWUSR,
                                    ar->debug.debugfs_phy, ar,
index 7fc7919..b93ae35 100644 (file)
@@ -1183,6 +1183,7 @@ struct htt_rx_info {
        } rate;
        bool fcs_err;
        bool amsdu_more;
+       bool mic_err;
 };
 
 struct ath10k_htt {
index fcb534f..fe8bd1b 100644 (file)
@@ -838,6 +838,20 @@ static bool ath10k_htt_rx_has_fcs_err(struct sk_buff *skb)
        return false;
 }
 
+static bool ath10k_htt_rx_has_mic_err(struct sk_buff *skb)
+{
+       struct htt_rx_desc *rxd;
+       u32 flags;
+
+       rxd = (void *)skb->data - sizeof(*rxd);
+       flags = __le32_to_cpu(rxd->attention.flags);
+
+       if (flags & RX_ATTENTION_FLAGS_TKIP_MIC_ERR)
+               return true;
+
+       return false;
+}
+
 static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb)
 {
        struct htt_rx_desc *rxd;
@@ -960,6 +974,7 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt,
 
                        info.skb     = msdu_head;
                        info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head);
+                       info.mic_err = ath10k_htt_rx_has_mic_err(msdu_head);
                        info.signal  = ATH10K_DEFAULT_NOISE_FLOOR;
                        info.signal += rx->ppdu.combined_rssi;
 
index 9535eaa..f1505a2 100644 (file)
@@ -115,6 +115,7 @@ enum ath10k_mcast2ucast_mode {
 #define TARGET_10X_MAC_AGGR_DELIM              0
 #define TARGET_10X_AST_SKID_LIMIT              16
 #define TARGET_10X_NUM_PEERS                   (128 + (TARGET_10X_NUM_VDEVS))
+#define TARGET_10X_NUM_PEERS_MAX               128
 #define TARGET_10X_NUM_OFFLOAD_PEERS           0
 #define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS    0
 #define TARGET_10X_NUM_PEER_KEYS               2
index 747c8bd..776e364 100644 (file)
@@ -332,6 +332,9 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
                ath10k_warn("Failed to wait for created wmi peer: %i\n", ret);
                return ret;
        }
+       spin_lock_bh(&ar->data_lock);
+       ar->num_peers++;
+       spin_unlock_bh(&ar->data_lock);
 
        return 0;
 }
@@ -377,6 +380,10 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
        if (ret)
                return ret;
 
+       spin_lock_bh(&ar->data_lock);
+       ar->num_peers--;
+       spin_unlock_bh(&ar->data_lock);
+
        return 0;
 }
 
@@ -396,6 +403,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 
                list_del(&peer->list);
                kfree(peer);
+               ar->num_peers--;
        }
        spin_unlock_bh(&ar->data_lock);
 }
@@ -411,6 +419,7 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar)
                list_del(&peer->list);
                kfree(peer);
        }
+       ar->num_peers = 0;
        spin_unlock_bh(&ar->data_lock);
 }
 
@@ -2205,7 +2214,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
        enum wmi_sta_powersave_param param;
        int ret = 0;
-       u32 value;
+       u32 value, param_id;
        int bit;
        u32 vdev_param;
 
@@ -2297,6 +2306,13 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to create peer for AP: %d\n", ret);
                        goto err_vdev_delete;
                }
+
+               param_id = ar->wmi.pdev_param->sta_kickout_th;
+
+               /* Disable STA KICKOUT functionality in FW */
+               ret = ath10k_wmi_pdev_set_param(ar, param_id, 0);
+               if (ret)
+                       ath10k_warn("Failed to disable STA KICKOUT\n");
        }
 
        if (arvif->vdev_type == WMI_VDEV_TYPE_STA) {
@@ -2842,6 +2858,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
 {
        struct ath10k *ar = hw->priv;
        struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       int max_num_peers;
        int ret = 0;
 
        mutex_lock(&ar->conf_mutex);
@@ -2852,9 +2869,21 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                /*
                 * New station addition.
                 */
+               if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features))
+                       max_num_peers = TARGET_10X_NUM_PEERS_MAX - 1;
+               else
+                       max_num_peers = TARGET_NUM_PEERS;
+
+               if (ar->num_peers >= max_num_peers) {
+                       ath10k_warn("Number of peers exceeded: peers number %d (max peers %d)\n",
+                                   ar->num_peers, max_num_peers);
+                       ret = -ENOBUFS;
+                       goto exit;
+               }
+
                ath10k_dbg(ATH10K_DBG_MAC,
-                          "mac vdev %d peer create %pM (new sta)\n",
-                          arvif->vdev_id, sta->addr);
+                          "mac vdev %d peer create %pM (new sta) num_peers %d\n",
+                          arvif->vdev_id, sta->addr, ar->num_peers);
 
                ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr);
                if (ret)
@@ -2904,7 +2933,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
                        ath10k_warn("Failed to disassociate station: %pM\n",
                                    sta->addr);
        }
-
+exit:
        mutex_unlock(&ar->conf_mutex);
        return ret;
 }
@@ -3310,6 +3339,307 @@ exit:
        return ret;
 }
 
+/* Helper table for legacy fixed_rate/bitrate_mask */
+static const u8 cck_ofdm_rate[] = {
+       /* CCK */
+       3, /* 1Mbps */
+       2, /* 2Mbps */
+       1, /* 5.5Mbps */
+       0, /* 11Mbps */
+       /* OFDM */
+       3, /* 6Mbps */
+       7, /* 9Mbps */
+       2, /* 12Mbps */
+       6, /* 18Mbps */
+       1, /* 24Mbps */
+       5, /* 36Mbps */
+       0, /* 48Mbps */
+       4, /* 54Mbps */
+};
+
+/* Check if only one bit set */
+static int ath10k_check_single_mask(u32 mask)
+{
+       int bit;
+
+       bit = ffs(mask);
+       if (!bit)
+               return 0;
+
+       mask &= ~BIT(bit - 1);
+       if (mask)
+               return 2;
+
+       return 1;
+}
+
+static bool
+ath10k_default_bitrate_mask(struct ath10k *ar,
+                           enum ieee80211_band band,
+                           const struct cfg80211_bitrate_mask *mask)
+{
+       u32 legacy = 0x00ff;
+       u8 ht = 0xff, i;
+       u16 vht = 0x3ff;
+
+       switch (band) {
+       case IEEE80211_BAND_2GHZ:
+               legacy = 0x00fff;
+               vht = 0;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               break;
+       default:
+               return false;
+       }
+
+       if (mask->control[band].legacy != legacy)
+               return false;
+
+       for (i = 0; i < ar->num_rf_chains; i++)
+               if (mask->control[band].ht_mcs[i] != ht)
+                       return false;
+
+       for (i = 0; i < ar->num_rf_chains; i++)
+               if (mask->control[band].vht_mcs[i] != vht)
+                       return false;
+
+       return true;
+}
+
+static bool
+ath10k_bitrate_mask_nss(const struct cfg80211_bitrate_mask *mask,
+                       enum ieee80211_band band,
+                       u8 *fixed_nss)
+{
+       int ht_nss = 0, vht_nss = 0, i;
+
+       /* check legacy */
+       if (ath10k_check_single_mask(mask->control[band].legacy))
+               return false;
+
+       /* check HT */
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+               if (mask->control[band].ht_mcs[i] == 0xff)
+                       continue;
+               else if (mask->control[band].ht_mcs[i] == 0x00)
+                       break;
+               else
+                       return false;
+       }
+
+       ht_nss = i;
+
+       /* check VHT */
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++) {
+               if (mask->control[band].vht_mcs[i] == 0x03ff)
+                       continue;
+               else if (mask->control[band].vht_mcs[i] == 0x0000)
+                       break;
+               else
+                       return false;
+       }
+
+       vht_nss = i;
+
+       if (ht_nss > 0 && vht_nss > 0)
+               return false;
+
+       if (ht_nss)
+               *fixed_nss = ht_nss;
+       else if (vht_nss)
+               *fixed_nss = vht_nss;
+       else
+               return false;
+
+       return true;
+}
+
+static bool
+ath10k_bitrate_mask_correct(const struct cfg80211_bitrate_mask *mask,
+                           enum ieee80211_band band,
+                           enum wmi_rate_preamble *preamble)
+{
+       int legacy = 0, ht = 0, vht = 0, i;
+
+       *preamble = WMI_RATE_PREAMBLE_OFDM;
+
+       /* check legacy */
+       legacy = ath10k_check_single_mask(mask->control[band].legacy);
+       if (legacy > 1)
+               return false;
+
+       /* check HT */
+       for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+               ht += ath10k_check_single_mask(mask->control[band].ht_mcs[i]);
+       if (ht > 1)
+               return false;
+
+       /* check VHT */
+       for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+               vht += ath10k_check_single_mask(mask->control[band].vht_mcs[i]);
+       if (vht > 1)
+               return false;
+
+       /* Currently we support only one fixed_rate */
+       if ((legacy + ht + vht) != 1)
+               return false;
+
+       if (ht)
+               *preamble = WMI_RATE_PREAMBLE_HT;
+       else if (vht)
+               *preamble = WMI_RATE_PREAMBLE_VHT;
+
+       return true;
+}
+
+static bool
+ath10k_bitrate_mask_rate(const struct cfg80211_bitrate_mask *mask,
+                        enum ieee80211_band band,
+                        u8 *fixed_rate,
+                        u8 *fixed_nss)
+{
+       u8 rate = 0, pream = 0, nss = 0, i;
+       enum wmi_rate_preamble preamble;
+
+       /* Check if single rate correct */
+       if (!ath10k_bitrate_mask_correct(mask, band, &preamble))
+               return false;
+
+       pream = preamble;
+
+       switch (preamble) {
+       case WMI_RATE_PREAMBLE_CCK:
+       case WMI_RATE_PREAMBLE_OFDM:
+               i = ffs(mask->control[band].legacy) - 1;
+
+               if (band == IEEE80211_BAND_2GHZ && i < 4)
+                       pream = WMI_RATE_PREAMBLE_CCK;
+
+               if (band == IEEE80211_BAND_5GHZ)
+                       i += 4;
+
+               if (i >= ARRAY_SIZE(cck_ofdm_rate))
+                       return false;
+
+               rate = cck_ofdm_rate[i];
+               break;
+       case WMI_RATE_PREAMBLE_HT:
+               for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
+                       if (mask->control[band].ht_mcs[i])
+                               break;
+
+               if (i == IEEE80211_HT_MCS_MASK_LEN)
+                       return false;
+
+               rate = ffs(mask->control[band].ht_mcs[i]) - 1;
+               nss = i;
+               break;
+       case WMI_RATE_PREAMBLE_VHT:
+               for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
+                       if (mask->control[band].vht_mcs[i])
+                               break;
+
+               if (i == NL80211_VHT_NSS_MAX)
+                       return false;
+
+               rate = ffs(mask->control[band].vht_mcs[i]) - 1;
+               nss = i;
+               break;
+       }
+
+       *fixed_nss = nss + 1;
+       nss <<= 4;
+       pream <<= 6;
+
+       ath10k_dbg(ATH10K_DBG_MAC, "mac fixed rate pream 0x%02x nss 0x%02x rate 0x%02x\n",
+                  pream, nss, rate);
+
+       *fixed_rate = pream | nss | rate;
+
+       return true;
+}
+
+static bool ath10k_get_fixed_rate_nss(const struct cfg80211_bitrate_mask *mask,
+                                     enum ieee80211_band band,
+                                     u8 *fixed_rate,
+                                     u8 *fixed_nss)
+{
+       /* First check full NSS mask, if we can simply limit NSS */
+       if (ath10k_bitrate_mask_nss(mask, band, fixed_nss))
+               return true;
+
+       /* Next Check single rate is set */
+       return ath10k_bitrate_mask_rate(mask, band, fixed_rate, fixed_nss);
+}
+
+static int ath10k_set_fixed_rate_param(struct ath10k_vif *arvif,
+                                      u8 fixed_rate,
+                                      u8 fixed_nss)
+{
+       struct ath10k *ar = arvif->ar;
+       u32 vdev_param;
+       int ret = 0;
+
+       mutex_lock(&ar->conf_mutex);
+
+       if (arvif->fixed_rate == fixed_rate &&
+           arvif->fixed_nss == fixed_nss)
+               goto exit;
+
+       if (fixed_rate == WMI_FIXED_RATE_NONE)
+               ath10k_dbg(ATH10K_DBG_MAC, "mac disable fixed bitrate mask\n");
+
+       vdev_param = ar->wmi.vdev_param->fixed_rate;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       vdev_param, fixed_rate);
+       if (ret) {
+               ath10k_warn("Could not set fixed_rate param 0x%02x: %d\n",
+                           fixed_rate, ret);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       arvif->fixed_rate = fixed_rate;
+
+       vdev_param = ar->wmi.vdev_param->nss;
+       ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id,
+                                       vdev_param, fixed_nss);
+
+       if (ret) {
+               ath10k_warn("Could not set fixed_nss param %d: %d\n",
+                           fixed_nss, ret);
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       arvif->fixed_nss = fixed_nss;
+
+exit:
+       mutex_unlock(&ar->conf_mutex);
+       return ret;
+}
+
+static int ath10k_set_bitrate_mask(struct ieee80211_hw *hw,
+                                  struct ieee80211_vif *vif,
+                                  const struct cfg80211_bitrate_mask *mask)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k *ar = arvif->ar;
+       enum ieee80211_band band = ar->hw->conf.chandef.chan->band;
+       u8 fixed_rate = WMI_FIXED_RATE_NONE;
+       u8 fixed_nss = ar->num_rf_chains;
+
+       if (!ath10k_default_bitrate_mask(ar, band, mask)) {
+               if (!ath10k_get_fixed_rate_nss(mask, band,
+                                              &fixed_rate,
+                                              &fixed_nss))
+                       return -EINVAL;
+       }
+
+       return ath10k_set_fixed_rate_param(arvif, fixed_rate, fixed_nss);
+}
+
 static const struct ieee80211_ops ath10k_ops = {
        .tx                             = ath10k_tx,
        .start                          = ath10k_start,
@@ -3332,6 +3662,7 @@ static const struct ieee80211_ops ath10k_ops = {
        .tx_last_beacon                 = ath10k_tx_last_beacon,
        .restart_complete               = ath10k_restart_complete,
        .get_survey                     = ath10k_get_survey,
+       .set_bitrate_mask               = ath10k_set_bitrate_mask,
 #ifdef CONFIG_PM
        .suspend                        = ath10k_suspend,
        .resume                         = ath10k_resume,
@@ -3464,14 +3795,12 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = {
        },
 };
 
-#ifdef CONFIG_ATH10K_DFS_CERTIFIED
-static const struct ieee80211_iface_limit ath10k_if_dfs_limits[] = {
+static const struct ieee80211_iface_limit ath10k_10x_if_limits[] = {
        {
        .max    = 8,
        .types  = BIT(NL80211_IFTYPE_AP)
        },
 };
-#endif
 
 static const struct ieee80211_iface_combination ath10k_if_comb[] = {
        {
@@ -3481,19 +3810,22 @@ static const struct ieee80211_iface_combination ath10k_if_comb[] = {
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
        },
-#ifdef CONFIG_ATH10K_DFS_CERTIFIED
+};
+
+static const struct ieee80211_iface_combination ath10k_10x_if_comb[] = {
        {
-               .limits = ath10k_if_dfs_limits,
-               .n_limits = ARRAY_SIZE(ath10k_if_dfs_limits),
+               .limits = ath10k_10x_if_limits,
+               .n_limits = ARRAY_SIZE(ath10k_10x_if_limits),
                .max_interfaces = 8,
                .num_different_channels = 1,
                .beacon_int_infra_match = true,
+#ifdef CONFIG_ATH10K_DFS_CERTIFIED
                .radar_detect_widths =  BIT(NL80211_CHAN_WIDTH_20_NOHT) |
                                        BIT(NL80211_CHAN_WIDTH_20) |
                                        BIT(NL80211_CHAN_WIDTH_40) |
                                        BIT(NL80211_CHAN_WIDTH_80),
-       }
 #endif
+       },
 };
 
 static struct ieee80211_sta_vht_cap ath10k_create_vht_cap(struct ath10k *ar)
@@ -3672,9 +4004,12 @@ int ath10k_mac_register(struct ath10k *ar)
        ar->hw->wiphy->interface_modes =
                BIT(NL80211_IFTYPE_STATION) |
                BIT(NL80211_IFTYPE_ADHOC) |
-               BIT(NL80211_IFTYPE_AP) |
-               BIT(NL80211_IFTYPE_P2P_CLIENT) |
-               BIT(NL80211_IFTYPE_P2P_GO);
+               BIT(NL80211_IFTYPE_AP);
+
+       if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features))
+               ar->hw->wiphy->interface_modes |=
+                       BIT(NL80211_IFTYPE_P2P_CLIENT) |
+                       BIT(NL80211_IFTYPE_P2P_GO);
 
        ar->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                        IEEE80211_HW_SUPPORTS_PS |
@@ -3716,8 +4051,15 @@ int ath10k_mac_register(struct ath10k *ar)
         */
        ar->hw->queues = 4;
 
-       ar->hw->wiphy->iface_combinations = ath10k_if_comb;
-       ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb);
+       if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) {
+               ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb;
+               ar->hw->wiphy->n_iface_combinations =
+                       ARRAY_SIZE(ath10k_10x_if_comb);
+       } else {
+               ar->hw->wiphy->iface_combinations = ath10k_if_comb;
+               ar->hw->wiphy->n_iface_combinations =
+                       ARRAY_SIZE(ath10k_if_comb);
+       }
 
        ar->hw->netdev_features = NETIF_F_HW_CSUM;
 
index 90817dd..4eb2ecb 100644 (file)
@@ -182,6 +182,27 @@ TRACE_EVENT(ath10k_htt_stats,
        )
 );
 
+TRACE_EVENT(ath10k_wmi_dbglog,
+       TP_PROTO(void *buf, size_t buf_len),
+
+       TP_ARGS(buf, buf_len),
+
+       TP_STRUCT__entry(
+               __field(size_t, buf_len)
+               __dynamic_array(u8, buf, buf_len)
+       ),
+
+       TP_fast_assign(
+               __entry->buf_len = buf_len;
+               memcpy(__get_dynamic_array(buf), buf, buf_len);
+       ),
+
+       TP_printk(
+               "len %zu",
+               __entry->buf_len
+       )
+);
+
 #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/
 
 /* we don't want to use include/trace/events */
index 2282980..74f45fa 100644 (file)
@@ -231,7 +231,7 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info)
                                ~IEEE80211_FCTL_PROTECTED);
        }
 
-       if (info->status == HTT_RX_IND_MPDU_STATUS_TKIP_MIC_ERR)
+       if (info->mic_err)
                status->flag |= RX_FLAG_MMIC_ERROR;
 
        if (info->fcs_err)
index 1260a8d..712a606 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/skbuff.h>
+#include <linux/ctype.h>
 
 #include "core.h"
 #include "htc.h"
@@ -875,6 +876,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        struct wmi_mgmt_rx_event_v2 *ev_v2;
        struct wmi_mgmt_rx_hdr_v1 *ev_hdr;
        struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+       struct ieee80211_channel *ch;
        struct ieee80211_hdr *hdr;
        u32 rx_status;
        u32 channel;
@@ -927,7 +929,25 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        if (rx_status & WMI_RX_STATUS_ERR_MIC)
                status->flag |= RX_FLAG_MMIC_ERROR;
 
-       status->band = phy_mode_to_band(phy_mode);
+       /* HW can Rx CCK rates on 5GHz. In that case phy_mode is set to
+        * MODE_11B. This means phy_mode is not a reliable source for the band
+        * of mgmt rx. */
+
+       ch = ar->scan_channel;
+       if (!ch)
+               ch = ar->rx_channel;
+
+       if (ch) {
+               status->band = ch->band;
+
+               if (phy_mode == MODE_11B &&
+                   status->band == IEEE80211_BAND_5GHZ)
+                       ath10k_dbg(ATH10K_DBG_MGMT, "wmi mgmt rx 11b (CCK) on 5GHz\n");
+       } else {
+               ath10k_warn("using (unreliable) phy_mode to extract band for mgmt rx\n");
+               status->band = phy_mode_to_band(phy_mode);
+       }
+
        status->freq = ieee80211_channel_to_frequency(channel, status->band);
        status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR;
        status->rate_idx = get_rate_idx(rate, status->band);
@@ -937,7 +957,11 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb)
        hdr = (struct ieee80211_hdr *)skb->data;
        fc = le16_to_cpu(hdr->frame_control);
 
-       if (fc & IEEE80211_FCTL_PROTECTED) {
+       /* FW delivers WEP Shared Auth frame with Protected Bit set and
+        * encrypted payload. However in case of PMF it delivers decrypted
+        * frames with Protected Bit set. */
+       if (ieee80211_has_protected(hdr->frame_control) &&
+           !ieee80211_is_auth(hdr->frame_control)) {
                status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED |
                                RX_FLAG_MMIC_STRIPPED;
                hdr->frame_control = __cpu_to_le16(fc &
@@ -1047,9 +1071,14 @@ static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb)
        ath10k_dbg(ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n");
 }
 
-static void ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
+static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb)
 {
-       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_MESG_EVENTID\n");
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug mesg len %d\n",
+                  skb->len);
+
+       trace_ath10k_wmi_dbglog(skb->data, skb->len);
+
+       return 0;
 }
 
 static void ath10k_wmi_event_update_stats(struct ath10k *ar,
@@ -1653,9 +1682,37 @@ static void ath10k_wmi_event_profile_match(struct ath10k *ar,
 }
 
 static void ath10k_wmi_event_debug_print(struct ath10k *ar,
-                                 struct sk_buff *skb)
+                                        struct sk_buff *skb)
 {
-       ath10k_dbg(ATH10K_DBG_WMI, "WMI_DEBUG_PRINT_EVENTID\n");
+       char buf[101], c;
+       int i;
+
+       for (i = 0; i < sizeof(buf) - 1; i++) {
+               if (i >= skb->len)
+                       break;
+
+               c = skb->data[i];
+
+               if (c == '\0')
+                       break;
+
+               if (isascii(c) && isprint(c))
+                       buf[i] = c;
+               else
+                       buf[i] = '.';
+       }
+
+       if (i == sizeof(buf) - 1)
+               ath10k_warn("wmi debug print truncated: %d\n", skb->len);
+
+       /* for some reason the debug prints end with \n, remove that */
+       if (skb->data[i - 1] == '\n')
+               i--;
+
+       /* the last byte is always reserved for the null character */
+       buf[i] = '\0';
+
+       ath10k_dbg(ATH10K_DBG_WMI, "wmi event debug print '%s'\n", buf);
 }
 
 static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb)
@@ -3445,3 +3502,40 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar,
                   type, delay_ms);
        return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid);
 }
+
+int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable)
+{
+       struct wmi_dbglog_cfg_cmd *cmd;
+       struct sk_buff *skb;
+       u32 cfg;
+
+       skb = ath10k_wmi_alloc_skb(sizeof(*cmd));
+       if (!skb)
+               return -ENOMEM;
+
+       cmd = (struct wmi_dbglog_cfg_cmd *)skb->data;
+
+       if (module_enable) {
+               cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE,
+                        ATH10K_DBGLOG_CFG_LOG_LVL);
+       } else {
+               /* set back defaults, all modules with WARN level */
+               cfg = SM(ATH10K_DBGLOG_LEVEL_WARN,
+                        ATH10K_DBGLOG_CFG_LOG_LVL);
+               module_enable = ~0;
+       }
+
+       cmd->module_enable = __cpu_to_le32(module_enable);
+       cmd->module_valid = __cpu_to_le32(~0);
+       cmd->config_enable = __cpu_to_le32(cfg);
+       cmd->config_valid = __cpu_to_le32(ATH10K_DBGLOG_CFG_LOG_LVL_MASK);
+
+       ath10k_dbg(ATH10K_DBG_WMI,
+                  "wmi dbglog cfg modules %08x %08x config %08x %08x\n",
+                  __le32_to_cpu(cmd->module_enable),
+                  __le32_to_cpu(cmd->module_valid),
+                  __le32_to_cpu(cmd->config_enable),
+                  __le32_to_cpu(cmd->config_valid));
+
+       return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid);
+}
index 0087d69..4b5e7d3 100644 (file)
@@ -3003,6 +3003,18 @@ struct wmi_vdev_install_key_arg {
        const void *key_data;
 };
 
+/*
+ * vdev fixed rate format:
+ * - preamble - b7:b6 - see WMI_RATE_PREMABLE_
+ * - nss      - b5:b4 - ss number (0 mean 1ss)
+ * - rate_mcs - b3:b0 - as below
+ *    CCK:  0 - 11Mbps, 1 - 5,5Mbps, 2 - 2Mbps, 3 - 1Mbps,
+ *          4 - 11Mbps (s), 5 - 5,5Mbps (s), 6 - 2Mbps (s)
+ *    OFDM: 0 - 48Mbps, 1 - 24Mbps, 2 - 12Mbps, 3 - 6Mbps,
+ *          4 - 54Mbps, 5 - 36Mbps, 6 - 18Mbps, 7 - 9Mbps
+ *    HT/VHT: MCS index
+ */
+
 /* Preamble types to be used with VDEV fixed rate configuration */
 enum wmi_rate_preamble {
        WMI_RATE_PREAMBLE_OFDM,
@@ -4090,6 +4102,54 @@ struct wmi_force_fw_hang_cmd {
        __le32 delay_ms;
 } __packed;
 
+enum ath10k_dbglog_level {
+       ATH10K_DBGLOG_LEVEL_VERBOSE = 0,
+       ATH10K_DBGLOG_LEVEL_INFO = 1,
+       ATH10K_DBGLOG_LEVEL_WARN = 2,
+       ATH10K_DBGLOG_LEVEL_ERR = 3,
+};
+
+/* VAP ids to enable dbglog */
+#define ATH10K_DBGLOG_CFG_VAP_LOG_LSB          0
+#define ATH10K_DBGLOG_CFG_VAP_LOG_MASK         0x0000ffff
+
+/* to enable dbglog in the firmware */
+#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_LSB 16
+#define ATH10K_DBGLOG_CFG_REPORTING_ENABLE_MASK        0x00010000
+
+/* timestamp resolution */
+#define ATH10K_DBGLOG_CFG_RESOLUTION_LSB       17
+#define ATH10K_DBGLOG_CFG_RESOLUTION_MASK      0x000E0000
+
+/* number of queued messages before sending them to the host */
+#define ATH10K_DBGLOG_CFG_REPORT_SIZE_LSB      20
+#define ATH10K_DBGLOG_CFG_REPORT_SIZE_MASK     0x0ff00000
+
+/*
+ * Log levels to enable. This defines the minimum level to enable, this is
+ * not a bitmask. See enum ath10k_dbglog_level for the values.
+ */
+#define ATH10K_DBGLOG_CFG_LOG_LVL_LSB          28
+#define ATH10K_DBGLOG_CFG_LOG_LVL_MASK         0x70000000
+
+/*
+ * Note: this is a cleaned up version of a struct firmware uses. For
+ * example, config_valid was hidden inside an array.
+ */
+struct wmi_dbglog_cfg_cmd {
+       /* bitmask to hold mod id config*/
+       __le32 module_enable;
+
+       /* see ATH10K_DBGLOG_CFG_ */
+       __le32 config_enable;
+
+       /* mask of module id bits to be changed */
+       __le32 module_valid;
+
+       /* mask of config bits to be changed, see ATH10K_DBGLOG_CFG_ */
+       __le32 config_valid;
+} __packed;
+
 #define ATH10K_RTS_MAX         2347
 #define ATH10K_FRAGMT_THRESHOLD_MIN    540
 #define ATH10K_FRAGMT_THRESHOLD_MAX    2346
@@ -4167,5 +4227,6 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id);
 int ath10k_wmi_force_fw_hang(struct ath10k *ar,
                             enum wmi_force_fw_hang_type type, u32 delay_ms);
 int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable);
 
 #endif /* _WMI_H_ */
index fe6878b..d85c312 100644 (file)
@@ -1245,7 +1245,7 @@ ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb,
 
        if (ieee80211_is_beacon(mgmt->frame_control) &&
            le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS &&
-           ether_addr_equal(mgmt->bssid, common->curbssid)) {
+           ether_addr_equal_64bits(mgmt->bssid, common->curbssid)) {
                /*
                 * Received an IBSS beacon with the same BSSID. Hardware *must*
                 * have updated the local TSF. We have to work around various
@@ -1309,7 +1309,7 @@ ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi)
 
        /* only beacons from our BSSID */
        if (!ieee80211_is_beacon(mgmt->frame_control) ||
-           !ether_addr_equal(mgmt->bssid, common->curbssid))
+           !ether_addr_equal_64bits(mgmt->bssid, common->curbssid))
                return;
 
        ewma_add(&ah->ah_beacon_rssi_avg, rssi);
index 337c459..e9904e5 100644 (file)
@@ -11,12 +11,14 @@ ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
 ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o
 ath9k-$(CONFIG_ATH9K_PCI) += pci.o
 ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
-ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
 ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
 ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
 ath9k-$(CONFIG_ATH9K_TX99) += tx99.o
 ath9k-$(CONFIG_ATH9K_WOW) += wow.o
 
+ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o \
+                                spectral.o
+
 obj-$(CONFIG_ATH9K) += ath9k.o
 
 ath9k_hw-y:=   \
index bd048cc..a366843 100644 (file)
@@ -724,14 +724,14 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
        struct ath_ant_comb *antcomb = &sc->ant_comb;
        int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
        int curr_main_set;
-       int main_rssi = rs->rs_rssi_ctl0;
-       int alt_rssi = rs->rs_rssi_ctl1;
+       int main_rssi = rs->rs_rssi_ctl[0];
+       int alt_rssi = rs->rs_rssi_ctl[1];
        int rx_ant_conf,  main_ant_conf;
        bool short_scan = false, ret;
 
-       rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
+       rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
                       ATH_ANT_RX_MASK;
-       main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
+       main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
                         ATH_ANT_RX_MASK;
 
        if (alt_rssi >= antcomb->low_rssi_thresh) {
index 5c95fd9..d480d2f 100644 (file)
@@ -32,12 +32,8 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
                return 0;
        }
 
-       if (ah->config.pcie_clock_req)
-               INIT_INI_ARRAY(&ah->iniPcieSerdes,
-                          ar9280PciePhy_clkreq_off_L1_9280);
-       else
-               INIT_INI_ARRAY(&ah->iniPcieSerdes,
-                          ar9280PciePhy_clkreq_always_on_L1_9280);
+       INIT_INI_ARRAY(&ah->iniPcieSerdes,
+                      ar9280PciePhy_clkreq_always_on_L1_9280);
 
        if (AR_SREV_9287_11_OR_LATER(ah)) {
                INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1);
@@ -387,6 +383,20 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah)
        }
 }
 
+static void ar9002_hw_init_hang_checks(struct ath_hw *ah)
+{
+       if (AR_SREV_9100(ah) || AR_SREV_9160(ah)) {
+               ah->config.hw_hang_checks |= HW_BB_RIFS_HANG;
+               ah->config.hw_hang_checks |= HW_BB_DFS_HANG;
+       }
+
+       if (AR_SREV_9280(ah))
+               ah->config.hw_hang_checks |= HW_BB_RX_CLEAR_STUCK_HANG;
+
+       if (AR_SREV_5416(ah) || AR_SREV_9100(ah) || AR_SREV_9160(ah))
+               ah->config.hw_hang_checks |= HW_MAC_HANG;
+}
+
 /* Sets up the AR5008/AR9001/AR9002 hardware familiy callbacks */
 int ar9002_hw_attach_ops(struct ath_hw *ah)
 {
@@ -399,6 +409,7 @@ int ar9002_hw_attach_ops(struct ath_hw *ah)
                return ret;
 
        priv_ops->init_mode_gain_regs = ar9002_hw_init_mode_gain_regs;
+       priv_ops->init_hang_checks = ar9002_hw_init_hang_checks;
 
        ops->config_pci_powersave = ar9002_hw_configpcipowersave;
 
index 8d78253..741b38d 100644 (file)
@@ -29,7 +29,8 @@ static void ar9002_hw_set_desc_link(void *ds, u32 ds_link)
        ((struct ath_desc*) ds)->ds_link = ds_link;
 }
 
-static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
+static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked,
+                             u32 *sync_cause_p)
 {
        u32 isr = 0;
        u32 mask2 = 0;
@@ -76,9 +77,16 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
                                mask2 |= ATH9K_INT_CST;
                        if (isr2 & AR_ISR_S2_TSFOOR)
                                mask2 |= ATH9K_INT_TSFOOR;
+
+                       if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
+                               REG_WRITE(ah, AR_ISR_S2, isr2);
+                               isr &= ~AR_ISR_BCNMISC;
+                       }
                }
 
-               isr = REG_READ(ah, AR_ISR_RAC);
+               if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)
+                       isr = REG_READ(ah, AR_ISR_RAC);
+
                if (isr == 0xffffffff) {
                        *masked = 0;
                        return false;
@@ -97,11 +105,23 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
 
                        *masked |= ATH9K_INT_TX;
 
-                       s0_s = REG_READ(ah, AR_ISR_S0_S);
+                       if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) {
+                               s0_s = REG_READ(ah, AR_ISR_S0_S);
+                               s1_s = REG_READ(ah, AR_ISR_S1_S);
+                       } else {
+                               s0_s = REG_READ(ah, AR_ISR_S0);
+                               REG_WRITE(ah, AR_ISR_S0, s0_s);
+                               s1_s = REG_READ(ah, AR_ISR_S1);
+                               REG_WRITE(ah, AR_ISR_S1, s1_s);
+
+                               isr &= ~(AR_ISR_TXOK |
+                                        AR_ISR_TXDESC |
+                                        AR_ISR_TXERR |
+                                        AR_ISR_TXEOL);
+                       }
+
                        ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXOK);
                        ah->intr_txqs |= MS(s0_s, AR_ISR_S0_QCU_TXDESC);
-
-                       s1_s = REG_READ(ah, AR_ISR_S1_S);
                        ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXERR);
                        ah->intr_txqs |= MS(s1_s, AR_ISR_S1_QCU_TXEOL);
                }
@@ -114,13 +134,15 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
                *masked |= mask2;
        }
 
-       if (AR_SREV_9100(ah))
-               return true;
-
-       if (isr & AR_ISR_GENTMR) {
+       if (!AR_SREV_9100(ah) && (isr & AR_ISR_GENTMR)) {
                u32 s5_s;
 
-               s5_s = REG_READ(ah, AR_ISR_S5_S);
+               if (pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED) {
+                       s5_s = REG_READ(ah, AR_ISR_S5_S);
+               } else {
+                       s5_s = REG_READ(ah, AR_ISR_S5);
+               }
+
                ah->intr_gen_timer_trigger =
                                MS(s5_s, AR_ISR_S5_GENTIMER_TRIG);
 
@@ -133,10 +155,24 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
                if ((s5_s & AR_ISR_S5_TIM_TIMER) &&
                    !(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP))
                        *masked |= ATH9K_INT_TIM_TIMER;
+
+               if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
+                       REG_WRITE(ah, AR_ISR_S5, s5_s);
+                       isr &= ~AR_ISR_GENTMR;
+               }
        }
 
+       if (!(pCap->hw_caps & ATH9K_HW_CAP_RAC_SUPPORTED)) {
+               REG_WRITE(ah, AR_ISR, isr);
+               REG_READ(ah, AR_ISR);
+       }
+
+       if (AR_SREV_9100(ah))
+               return true;
+
        if (sync_cause) {
-               ath9k_debug_sync_cause(common, sync_cause);
+               if (sync_cause_p)
+                       *sync_cause_p = sync_cause;
                fatal_int =
                        (sync_cause &
                         (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR))
index f087117..9a2afa2 100644 (file)
@@ -201,7 +201,6 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah,
        ath9k_hw_get_channel_centers(ah, chan, &centers);
        freq = centers.synth_center;
 
-       ah->config.spurmode = SPUR_ENABLE_EEPROM;
        for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
                cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz);
 
index 97e09d5..8c145cd 100644 (file)
@@ -326,6 +326,224 @@ static void ar9003_hw_init_cal_settings(struct ath_hw *ah)
        ah->supp_cals = IQ_MISMATCH_CAL;
 }
 
+#define OFF_UPPER_LT 24
+#define OFF_LOWER_LT 7
+
+static bool ar9003_hw_dynamic_osdac_selection(struct ath_hw *ah,
+                                             bool txiqcal_done)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       int ch0_done, osdac_ch0, dc_off_ch0_i1, dc_off_ch0_q1, dc_off_ch0_i2,
+               dc_off_ch0_q2, dc_off_ch0_i3, dc_off_ch0_q3;
+       int ch1_done, osdac_ch1, dc_off_ch1_i1, dc_off_ch1_q1, dc_off_ch1_i2,
+               dc_off_ch1_q2, dc_off_ch1_i3, dc_off_ch1_q3;
+       int ch2_done, osdac_ch2, dc_off_ch2_i1, dc_off_ch2_q1, dc_off_ch2_i2,
+               dc_off_ch2_q2, dc_off_ch2_i3, dc_off_ch2_q3;
+       bool status;
+       u32 temp, val;
+
+       /*
+        * Clear offset and IQ calibration, run AGC cal.
+        */
+       REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_OFFSET_CAL);
+       REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+                   AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
+       REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+                 REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL);
+
+       status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
+                              AR_PHY_AGC_CONTROL_CAL,
+                              0, AH_WAIT_TIMEOUT);
+       if (!status) {
+               ath_dbg(common, CALIBRATE,
+                       "AGC cal without offset cal failed to complete in 1ms");
+               return false;
+       }
+
+       /*
+        * Allow only offset calibration and disable the others
+        * (Carrier Leak calibration, TX Filter calibration and
+        *  Peak Detector offset calibration).
+        */
+       REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_OFFSET_CAL);
+       REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL,
+                   AR_PHY_CL_CAL_ENABLE);
+       REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_FLTR_CAL);
+       REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_PKDET_CAL);
+
+       ch0_done = 0;
+       ch1_done = 0;
+       ch2_done = 0;
+
+       while ((ch0_done == 0) || (ch1_done == 0) || (ch2_done == 0)) {
+               osdac_ch0 = (REG_READ(ah, AR_PHY_65NM_CH0_BB1) >> 30) & 0x3;
+               osdac_ch1 = (REG_READ(ah, AR_PHY_65NM_CH1_BB1) >> 30) & 0x3;
+               osdac_ch2 = (REG_READ(ah, AR_PHY_65NM_CH2_BB1) >> 30) & 0x3;
+
+               REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+
+               REG_WRITE(ah, AR_PHY_AGC_CONTROL,
+                         REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL);
+
+               status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL,
+                                      AR_PHY_AGC_CONTROL_CAL,
+                                      0, AH_WAIT_TIMEOUT);
+               if (!status) {
+                       ath_dbg(common, CALIBRATE,
+                               "DC offset cal failed to complete in 1ms");
+                       return false;
+               }
+
+               REG_CLR_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+
+               /*
+                * High gain.
+                */
+               REG_WRITE(ah, AR_PHY_65NM_CH0_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (1 << 8)));
+               REG_WRITE(ah, AR_PHY_65NM_CH1_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (1 << 8)));
+               REG_WRITE(ah, AR_PHY_65NM_CH2_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (1 << 8)));
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3);
+               dc_off_ch0_i1 = (temp >> 26) & 0x1f;
+               dc_off_ch0_q1 = (temp >> 21) & 0x1f;
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3);
+               dc_off_ch1_i1 = (temp >> 26) & 0x1f;
+               dc_off_ch1_q1 = (temp >> 21) & 0x1f;
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3);
+               dc_off_ch2_i1 = (temp >> 26) & 0x1f;
+               dc_off_ch2_q1 = (temp >> 21) & 0x1f;
+
+               /*
+                * Low gain.
+                */
+               REG_WRITE(ah, AR_PHY_65NM_CH0_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (2 << 8)));
+               REG_WRITE(ah, AR_PHY_65NM_CH1_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (2 << 8)));
+               REG_WRITE(ah, AR_PHY_65NM_CH2_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (2 << 8)));
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3);
+               dc_off_ch0_i2 = (temp >> 26) & 0x1f;
+               dc_off_ch0_q2 = (temp >> 21) & 0x1f;
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3);
+               dc_off_ch1_i2 = (temp >> 26) & 0x1f;
+               dc_off_ch1_q2 = (temp >> 21) & 0x1f;
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3);
+               dc_off_ch2_i2 = (temp >> 26) & 0x1f;
+               dc_off_ch2_q2 = (temp >> 21) & 0x1f;
+
+               /*
+                * Loopback.
+                */
+               REG_WRITE(ah, AR_PHY_65NM_CH0_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (3 << 8)));
+               REG_WRITE(ah, AR_PHY_65NM_CH1_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (3 << 8)));
+               REG_WRITE(ah, AR_PHY_65NM_CH2_BB3,
+                         ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (3 << 8)));
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3);
+               dc_off_ch0_i3 = (temp >> 26) & 0x1f;
+               dc_off_ch0_q3 = (temp >> 21) & 0x1f;
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3);
+               dc_off_ch1_i3 = (temp >> 26) & 0x1f;
+               dc_off_ch1_q3 = (temp >> 21) & 0x1f;
+
+               temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3);
+               dc_off_ch2_i3 = (temp >> 26) & 0x1f;
+               dc_off_ch2_q3 = (temp >> 21) & 0x1f;
+
+               if ((dc_off_ch0_i1 > OFF_UPPER_LT) || (dc_off_ch0_i1 < OFF_LOWER_LT) ||
+                   (dc_off_ch0_i2 > OFF_UPPER_LT) || (dc_off_ch0_i2 < OFF_LOWER_LT) ||
+                   (dc_off_ch0_i3 > OFF_UPPER_LT) || (dc_off_ch0_i3 < OFF_LOWER_LT) ||
+                   (dc_off_ch0_q1 > OFF_UPPER_LT) || (dc_off_ch0_q1 < OFF_LOWER_LT) ||
+                   (dc_off_ch0_q2 > OFF_UPPER_LT) || (dc_off_ch0_q2 < OFF_LOWER_LT) ||
+                   (dc_off_ch0_q3 > OFF_UPPER_LT) || (dc_off_ch0_q3 < OFF_LOWER_LT)) {
+                       if (osdac_ch0 == 3) {
+                               ch0_done = 1;
+                       } else {
+                               osdac_ch0++;
+
+                               val = REG_READ(ah, AR_PHY_65NM_CH0_BB1) & 0x3fffffff;
+                               val |= (osdac_ch0 << 30);
+                               REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, val);
+
+                               ch0_done = 0;
+                       }
+               } else {
+                       ch0_done = 1;
+               }
+
+               if ((dc_off_ch1_i1 > OFF_UPPER_LT) || (dc_off_ch1_i1 < OFF_LOWER_LT) ||
+                   (dc_off_ch1_i2 > OFF_UPPER_LT) || (dc_off_ch1_i2 < OFF_LOWER_LT) ||
+                   (dc_off_ch1_i3 > OFF_UPPER_LT) || (dc_off_ch1_i3 < OFF_LOWER_LT) ||
+                   (dc_off_ch1_q1 > OFF_UPPER_LT) || (dc_off_ch1_q1 < OFF_LOWER_LT) ||
+                   (dc_off_ch1_q2 > OFF_UPPER_LT) || (dc_off_ch1_q2 < OFF_LOWER_LT) ||
+                   (dc_off_ch1_q3 > OFF_UPPER_LT) || (dc_off_ch1_q3 < OFF_LOWER_LT)) {
+                       if (osdac_ch1 == 3) {
+                               ch1_done = 1;
+                       } else {
+                               osdac_ch1++;
+
+                               val = REG_READ(ah, AR_PHY_65NM_CH1_BB1) & 0x3fffffff;
+                               val |= (osdac_ch1 << 30);
+                               REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, val);
+
+                               ch1_done = 0;
+                       }
+               } else {
+                       ch1_done = 1;
+               }
+
+               if ((dc_off_ch2_i1 > OFF_UPPER_LT) || (dc_off_ch2_i1 < OFF_LOWER_LT) ||
+                   (dc_off_ch2_i2 > OFF_UPPER_LT) || (dc_off_ch2_i2 < OFF_LOWER_LT) ||
+                   (dc_off_ch2_i3 > OFF_UPPER_LT) || (dc_off_ch2_i3 < OFF_LOWER_LT) ||
+                   (dc_off_ch2_q1 > OFF_UPPER_LT) || (dc_off_ch2_q1 < OFF_LOWER_LT) ||
+                   (dc_off_ch2_q2 > OFF_UPPER_LT) || (dc_off_ch2_q2 < OFF_LOWER_LT) ||
+                   (dc_off_ch2_q3 > OFF_UPPER_LT) || (dc_off_ch2_q3 < OFF_LOWER_LT)) {
+                       if (osdac_ch2 == 3) {
+                               ch2_done = 1;
+                       } else {
+                               osdac_ch2++;
+
+                               val = REG_READ(ah, AR_PHY_65NM_CH2_BB1) & 0x3fffffff;
+                               val |= (osdac_ch2 << 30);
+                               REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, val);
+
+                               ch2_done = 0;
+                       }
+               } else {
+                       ch2_done = 1;
+               }
+       }
+
+       REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+                   AR_PHY_AGC_CONTROL_OFFSET_CAL);
+       REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
+
+       /*
+        * We don't need to check txiqcal_done here since it is always
+        * set for AR9550.
+        */
+       REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0,
+                   AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL);
+
+       return true;
+}
+
 /*
  * solve 4x4 linear equation used in loopback iq cal.
  */
@@ -1271,6 +1489,11 @@ static bool ar9003_hw_init_cal_soc(struct ath_hw *ah,
                REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
        }
 
+       if (AR_SREV_9550(ah) && IS_CHAN_2GHZ(chan)) {
+               if (!ar9003_hw_dynamic_osdac_selection(ah, txiqcal_done))
+                       return false;
+       }
+
 skip_tx_iqcal:
        if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) {
                if (AR_SREV_9330_11(ah))
index ec317d6..25243cb 100644 (file)
@@ -131,6 +131,7 @@ static const struct ar9300_eeprom ar9300_default = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0cf0e0e0),
                .papdRateMaskHt40 = LE32(0x6cf0e0e0),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -138,7 +139,7 @@ static const struct ar9300_eeprom ar9300_default = {
         },
        .base_ext1 = {
                .ant_div_control = 0,
-               .future = {0, 0, 0},
+               .future = {0, 0},
                .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0}
        },
        .calFreqPier2G = {
@@ -333,6 +334,7 @@ static const struct ar9300_eeprom ar9300_default = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0c80c080),
                .papdRateMaskHt40 = LE32(0x0080c080),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -707,6 +709,7 @@ static const struct ar9300_eeprom ar9300_x113 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0c80c080),
                .papdRateMaskHt40 = LE32(0x0080c080),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -714,7 +717,7 @@ static const struct ar9300_eeprom ar9300_x113 = {
         },
         .base_ext1 = {
                .ant_div_control = 0,
-               .future = {0, 0, 0},
+               .future = {0, 0},
                .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0}
         },
        .calFreqPier2G = {
@@ -909,6 +912,7 @@ static const struct ar9300_eeprom ar9300_x113 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0cf0e0e0),
                .papdRateMaskHt40 = LE32(0x6cf0e0e0),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -1284,6 +1288,7 @@ static const struct ar9300_eeprom ar9300_h112 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0c80c080),
                .papdRateMaskHt40 = LE32(0x0080c080),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -1291,7 +1296,7 @@ static const struct ar9300_eeprom ar9300_h112 = {
        },
        .base_ext1 = {
                .ant_div_control = 0,
-               .future = {0, 0, 0},
+               .future = {0, 0},
                .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0}
        },
        .calFreqPier2G = {
@@ -1486,6 +1491,7 @@ static const struct ar9300_eeprom ar9300_h112 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0cf0e0e0),
                .papdRateMaskHt40 = LE32(0x6cf0e0e0),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -1861,6 +1867,7 @@ static const struct ar9300_eeprom ar9300_x112 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0c80c080),
                .papdRateMaskHt40 = LE32(0x0080c080),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -1868,7 +1875,7 @@ static const struct ar9300_eeprom ar9300_x112 = {
        },
        .base_ext1 = {
                .ant_div_control = 0,
-               .future = {0, 0, 0},
+               .future = {0, 0},
                .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0}
        },
        .calFreqPier2G = {
@@ -2063,6 +2070,7 @@ static const struct ar9300_eeprom ar9300_x112 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0cf0e0e0),
                .papdRateMaskHt40 = LE32(0x6cf0e0e0),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -2437,6 +2445,7 @@ static const struct ar9300_eeprom ar9300_h116 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0c80C080),
                .papdRateMaskHt40 = LE32(0x0080C080),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -2444,7 +2453,7 @@ static const struct ar9300_eeprom ar9300_h116 = {
         },
         .base_ext1 = {
                .ant_div_control = 0,
-               .future = {0, 0, 0},
+               .future = {0, 0},
                .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0}
         },
        .calFreqPier2G = {
@@ -2639,6 +2648,7 @@ static const struct ar9300_eeprom ar9300_h116 = {
                .thresh62 = 28,
                .papdRateMaskHt20 = LE32(0x0cf0e0e0),
                .papdRateMaskHt40 = LE32(0x6cf0e0e0),
+               .switchcomspdt = 0,
                .xlna_bias_strength = 0,
                .futureModal = {
                        0, 0, 0, 0, 0, 0, 0,
@@ -3588,7 +3598,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz)
        if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) {
                REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
                                AR_SWITCH_TABLE_COM_AR9462_ALL, value);
-       } else if (AR_SREV_9550(ah)) {
+       } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
                REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM,
                                AR_SWITCH_TABLE_COM_AR9550_ALL, value);
        } else
@@ -3965,7 +3975,7 @@ static void ar9003_hw_apply_tuning_caps(struct ath_hw *ah)
        struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
        u8 tuning_caps_param = eep->baseEepHeader.params_for_tuning_caps[0];
 
-       if (AR_SREV_9340(ah))
+       if (AR_SREV_9340(ah) || AR_SREV_9531(ah))
                return;
 
        if (eep->baseEepHeader.featureEnable & 0x40) {
@@ -4020,7 +4030,10 @@ static void ar9003_hw_xpa_timing_control_apply(struct ath_hw *ah, bool is2ghz)
        if (!(eep->baseEepHeader.featureEnable & 0x80))
                return;
 
-       if (!AR_SREV_9300(ah) && !AR_SREV_9340(ah) && !AR_SREV_9580(ah))
+       if (!AR_SREV_9300(ah) &&
+           !AR_SREV_9340(ah) &&
+           !AR_SREV_9580(ah) &&
+           !AR_SREV_9531(ah))
                return;
 
        xpa_ctl = ar9003_modal_header(ah, is2ghz)->txFrameToXpaOn;
@@ -4111,6 +4124,37 @@ static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah)
        }
 }
 
+static void ar9003_hw_apply_minccapwr_thresh(struct ath_hw *ah,
+                                            bool is2ghz)
+{
+       struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+       const u_int32_t cca_ctrl[AR9300_MAX_CHAINS] = {
+               AR_PHY_CCA_CTRL_0,
+               AR_PHY_CCA_CTRL_1,
+               AR_PHY_CCA_CTRL_2,
+       };
+       int chain;
+       u32 val;
+
+       if (is2ghz) {
+               if (!(eep->base_ext1.misc_enable & BIT(2)))
+                       return;
+       } else {
+               if (!(eep->base_ext1.misc_enable & BIT(3)))
+                       return;
+       }
+
+       for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
+               if (!(ah->caps.tx_chainmask & BIT(chain)))
+                       continue;
+
+               val = ar9003_modal_header(ah, is2ghz)->noiseFloorThreshCh[chain];
+               REG_RMW_FIELD(ah, cca_ctrl[chain],
+                             AR_PHY_EXT_CCA0_THRESH62_1, val);
+       }
+
+}
+
 static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah,
                                             struct ath9k_channel *chan)
 {
@@ -4122,9 +4166,10 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah,
        ar9003_hw_xlna_bias_strength_apply(ah, is2ghz);
        ar9003_hw_atten_apply(ah, chan);
        ar9003_hw_quick_drop_apply(ah, chan->channel);
-       if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah))
+       if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah) && !AR_SREV_9531(ah))
                ar9003_hw_internal_regulator_apply(ah);
        ar9003_hw_apply_tuning_caps(ah);
+       ar9003_hw_apply_minccapwr_thresh(ah, chan);
        ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz);
        ar9003_hw_thermometer_apply(ah);
        ar9003_hw_thermo_cal_apply(ah);
@@ -4746,7 +4791,7 @@ static void ar9003_hw_power_control_override(struct ath_hw *ah,
        }
 
 tempslope:
-       if (AR_SREV_9550(ah)) {
+       if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
                /*
                 * AR955x has tempSlope register for each chain.
                 * Check whether temp_compensation feature is enabled or not.
index 0e5daa5..694ca2e 100644 (file)
@@ -270,10 +270,20 @@ struct cal_ctl_data_5g {
        u8 ctlEdges[AR9300_NUM_BAND_EDGES_5G];
 } __packed;
 
+#define MAX_BASE_EXTENSION_FUTURE 2
+
 struct ar9300_BaseExtension_1 {
        u8 ant_div_control;
-       u8 future[3];
-       u8 tempslopextension[8];
+       u8 future[MAX_BASE_EXTENSION_FUTURE];
+       /*
+        * misc_enable:
+        *
+        * BIT 0   - TX Gain Cap enable.
+        * BIT 1   - Uncompressed Checksum enable.
+        * BIT 2/3 - MinCCApwr enable 2g/5g.
+        */
+       u8 misc_enable;
+       int8_t tempslopextension[8];
        int8_t quick_drop_low;
        int8_t quick_drop_high;
 } __packed;
index 29613eb..ec1da0c 100644 (file)
@@ -28,6 +28,7 @@
 #include "ar9462_2p1_initvals.h"
 #include "ar9565_1p0_initvals.h"
 #include "ar9565_1p1_initvals.h"
+#include "ar953x_initvals.h"
 
 /* General hardware code for the AR9003 hadware family */
 
@@ -308,6 +309,31 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
                /* Fast clock modal settings */
                INIT_INI_ARRAY(&ah->iniModesFastClock,
                                ar955x_1p0_modes_fast_clock);
+       } else if (AR_SREV_9531(ah)) {
+               INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
+                              qca953x_1p0_mac_core);
+               INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
+                              qca953x_1p0_mac_postamble);
+               INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+                              qca953x_1p0_baseband_core);
+               INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+                              qca953x_1p0_baseband_postamble);
+               INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
+                              qca953x_1p0_radio_core);
+               INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
+                              qca953x_1p0_radio_postamble);
+               INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE],
+                              qca953x_1p0_soc_preamble);
+               INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST],
+                              qca953x_1p0_soc_postamble);
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              qca953x_1p0_common_wo_xlna_rx_gain_table);
+               INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+                              qca953x_1p0_common_wo_xlna_rx_gain_bounds);
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                              qca953x_1p0_modes_no_xpa_tx_gain_table);
+               INIT_INI_ARRAY(&ah->iniModesFastClock,
+                              qca953x_1p0_modes_fast_clock);
        } else if (AR_SREV_9580(ah)) {
                /* mac */
                INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE],
@@ -485,6 +511,9 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah)
        else if (AR_SREV_9550(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar955x_1p0_modes_xpa_tx_gain_table);
+       else if (AR_SREV_9531(ah))
+               INIT_INI_ARRAY(&ah->iniModesTxGain,
+                       qca953x_1p0_modes_xpa_tx_gain_table);
        else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9580_1p0_lowest_ob_db_tx_gain_table);
@@ -525,7 +554,14 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah)
        else if (AR_SREV_9550(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar955x_1p0_modes_no_xpa_tx_gain_table);
-       else if (AR_SREV_9462_21(ah))
+       else if (AR_SREV_9531(ah)) {
+               if (AR_SREV_9531_11(ah))
+                       INIT_INI_ARRAY(&ah->iniModesTxGain,
+                                      qca953x_1p1_modes_no_xpa_tx_gain_table);
+               else
+                       INIT_INI_ARRAY(&ah->iniModesTxGain,
+                                      qca953x_1p0_modes_no_xpa_tx_gain_table);
+       } else if (AR_SREV_9462_21(ah))
                INIT_INI_ARRAY(&ah->iniModesTxGain,
                        ar9462_2p1_modes_high_ob_db_tx_gain);
        else if (AR_SREV_9462_20(ah))
@@ -699,6 +735,11 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)
                                ar955x_1p0_common_rx_gain_table);
                INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
                                ar955x_1p0_common_rx_gain_bounds);
+       } else if (AR_SREV_9531(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              qca953x_1p0_common_rx_gain_table);
+               INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+                              qca953x_1p0_common_rx_gain_bounds);
        } else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                                ar9580_1p0_rx_gain_table);
@@ -744,6 +785,11 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah)
                        ar955x_1p0_common_wo_xlna_rx_gain_table);
                INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
                        ar955x_1p0_common_wo_xlna_rx_gain_bounds);
+       } else if (AR_SREV_9531(ah)) {
+               INIT_INI_ARRAY(&ah->iniModesRxGain,
+                              qca953x_1p0_common_wo_xlna_rx_gain_table);
+               INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds,
+                              qca953x_1p0_common_wo_xlna_rx_gain_bounds);
        } else if (AR_SREV_9580(ah))
                INIT_INI_ARRAY(&ah->iniModesRxGain,
                        ar9580_1p0_wo_xlna_rx_gain_table);
@@ -872,6 +918,117 @@ static void ar9003_hw_configpcipowersave(struct ath_hw *ah,
        }
 }
 
+static void ar9003_hw_init_hang_checks(struct ath_hw *ah)
+{
+       /*
+        * All chips support detection of BB/MAC hangs.
+        */
+       ah->config.hw_hang_checks |= HW_BB_WATCHDOG;
+       ah->config.hw_hang_checks |= HW_MAC_HANG;
+
+       /*
+        * This is not required for AR9580 1.0
+        */
+       if (AR_SREV_9300_22(ah))
+               ah->config.hw_hang_checks |= HW_PHYRESTART_CLC_WAR;
+
+       if (AR_SREV_9330(ah))
+               ah->bb_watchdog_timeout_ms = 85;
+       else
+               ah->bb_watchdog_timeout_ms = 25;
+}
+
+/*
+ * MAC HW hang check
+ * =================
+ *
+ * Signature: dcu_chain_state is 0x6 and dcu_complete_state is 0x1.
+ *
+ * The state of each DCU chain (mapped to TX queues) is available from these
+ * DMA debug registers:
+ *
+ * Chain 0 state : Bits 4:0   of AR_DMADBG_4
+ * Chain 1 state : Bits 9:5   of AR_DMADBG_4
+ * Chain 2 state : Bits 14:10 of AR_DMADBG_4
+ * Chain 3 state : Bits 19:15 of AR_DMADBG_4
+ * Chain 4 state : Bits 24:20 of AR_DMADBG_4
+ * Chain 5 state : Bits 29:25 of AR_DMADBG_4
+ * Chain 6 state : Bits 4:0   of AR_DMADBG_5
+ * Chain 7 state : Bits 9:5   of AR_DMADBG_5
+ * Chain 8 state : Bits 14:10 of AR_DMADBG_5
+ * Chain 9 state : Bits 19:15 of AR_DMADBG_5
+ *
+ * The DCU chain state "0x6" means "WAIT_FRDONE" - wait for TX frame to be done.
+ */
+
+#define NUM_STATUS_READS 50
+
+static bool ath9k_hw_verify_hang(struct ath_hw *ah, unsigned int queue)
+{
+       u32 dma_dbg_chain, dma_dbg_complete;
+       u8 dcu_chain_state, dcu_complete_state;
+       int i;
+
+       for (i = 0; i < NUM_STATUS_READS; i++) {
+               if (queue < 6)
+                       dma_dbg_chain = REG_READ(ah, AR_DMADBG_4);
+               else
+                       dma_dbg_chain = REG_READ(ah, AR_DMADBG_5);
+
+               dma_dbg_complete = REG_READ(ah, AR_DMADBG_6);
+
+               dcu_chain_state = (dma_dbg_chain >> (5 * queue)) & 0x1f;
+               dcu_complete_state = dma_dbg_complete & 0x3;
+
+               if ((dcu_chain_state != 0x6) || (dcu_complete_state != 0x1))
+                       return false;
+       }
+
+       ath_dbg(ath9k_hw_common(ah), RESET,
+               "MAC Hang signature found for queue: %d\n", queue);
+
+       return true;
+}
+
+static bool ar9003_hw_detect_mac_hang(struct ath_hw *ah)
+{
+       u32 dma_dbg_4, dma_dbg_5, dma_dbg_6, chk_dbg;
+       u8 dcu_chain_state, dcu_complete_state;
+       bool dcu_wait_frdone = false;
+       unsigned long chk_dcu = 0;
+       unsigned int i = 0;
+
+       dma_dbg_4 = REG_READ(ah, AR_DMADBG_4);
+       dma_dbg_5 = REG_READ(ah, AR_DMADBG_5);
+       dma_dbg_6 = REG_READ(ah, AR_DMADBG_6);
+
+       dcu_complete_state = dma_dbg_6 & 0x3;
+       if (dcu_complete_state != 0x1)
+               goto exit;
+
+       for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
+               if (i < 6)
+                       chk_dbg = dma_dbg_4;
+               else
+                       chk_dbg = dma_dbg_5;
+
+               dcu_chain_state = (chk_dbg >> (5 * i)) & 0x1f;
+               if (dcu_chain_state == 0x6) {
+                       dcu_wait_frdone = true;
+                       chk_dcu |= BIT(i);
+               }
+       }
+
+       if ((dcu_complete_state == 0x1) && dcu_wait_frdone) {
+               for_each_set_bit(i, &chk_dcu, ATH9K_NUM_TX_QUEUES) {
+                       if (ath9k_hw_verify_hang(ah, i))
+                               return true;
+               }
+       }
+exit:
+       return false;
+}
+
 /* Sets up the AR9003 hardware familiy callbacks */
 void ar9003_hw_attach_ops(struct ath_hw *ah)
 {
@@ -880,6 +1037,8 @@ void ar9003_hw_attach_ops(struct ath_hw *ah)
 
        ar9003_hw_init_mode_regs(ah);
        priv_ops->init_mode_gain_regs = ar9003_hw_init_mode_gain_regs;
+       priv_ops->init_hang_checks = ar9003_hw_init_hang_checks;
+       priv_ops->detect_mac_hang = ar9003_hw_detect_mac_hang;
 
        ops->config_pci_powersave = ar9003_hw_configpcipowersave;
 
index f6c5c1b..729ffbf 100644 (file)
@@ -175,7 +175,8 @@ static void ar9003_hw_set_desc_link(void *ds, u32 ds_link)
        ads->ctl10 |= ar9003_calc_ptr_chksum(ads);
 }
 
-static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
+static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked,
+                             u32 *sync_cause_p)
 {
        u32 isr = 0;
        u32 mask2 = 0;
@@ -310,7 +311,8 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked)
                ar9003_mci_get_isr(ah, masked);
 
        if (sync_cause) {
-               ath9k_debug_sync_cause(common, sync_cause);
+               if (sync_cause_p)
+                       *sync_cause_p = sync_cause;
                fatal_int =
                        (sync_cause &
                         (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR))
@@ -476,12 +478,12 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs,
 
        /* XXX: Keycache */
        rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined);
-       rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00);
-       rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01);
-       rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02);
-       rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10);
-       rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11);
-       rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12);
+       rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00);
+       rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01);
+       rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02);
+       rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10);
+       rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11);
+       rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12);
 
        if (rxsp->status11 & AR_RxKeyIdxValid)
                rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx);
index 9f051a0..09facba 100644 (file)
@@ -103,7 +103,7 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
                        } else {
                                channelSel = CHANSEL_2G(freq) >> 1;
                        }
-               } else if (AR_SREV_9550(ah)) {
+               } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
                        if (ah->is_clk_25mhz)
                                div = 75;
                        else
@@ -118,7 +118,7 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan)
                /* Set to 2G mode */
                bMode = 1;
        } else {
-               if ((AR_SREV_9340(ah) || AR_SREV_9550(ah)) &&
+               if ((AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) &&
                    ah->is_clk_25mhz) {
                        channelSel = freq / 75;
                        chan_frac = ((freq % 75) * 0x20000) / 75;
@@ -810,10 +810,12 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
        /*
         * TXGAIN initvals.
         */
-       if (AR_SREV_9550(ah)) {
-               int modes_txgain_index;
+       if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
+               int modes_txgain_index = 1;
+
+               if (AR_SREV_9550(ah))
+                       modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan);
 
-               modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan);
                if (modes_txgain_index < 0)
                        return -EINVAL;
 
@@ -1814,6 +1816,68 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
        memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs));
 }
 
+/*
+ * Baseband Watchdog signatures:
+ *
+ * 0x04000539: BB hang when operating in HT40 DFS Channel.
+ *             Full chip reset is not required, but a recovery
+ *             mechanism is needed.
+ *
+ * 0x1300000a: Related to CAC deafness.
+ *             Chip reset is not required.
+ *
+ * 0x0400000a: Related to CAC deafness.
+ *             Full chip reset is required.
+ *
+ * 0x04000b09: RX state machine gets into an illegal state
+ *             when a packet with unsupported rate is received.
+ *             Full chip reset is required and PHY_RESTART has
+ *             to be disabled.
+ *
+ * 0x04000409: Packet stuck on receive.
+ *             Full chip reset is required for all chips except AR9340.
+ */
+
+/*
+ * ar9003_hw_bb_watchdog_check(): Returns true if a chip reset is required.
+ */
+bool ar9003_hw_bb_watchdog_check(struct ath_hw *ah)
+{
+       u32 val;
+
+       switch(ah->bb_watchdog_last_status) {
+       case 0x04000539:
+               val = REG_READ(ah, AR_PHY_RADAR_0);
+               val &= (~AR_PHY_RADAR_0_FIRPWR);
+               val |= SM(0x7f, AR_PHY_RADAR_0_FIRPWR);
+               REG_WRITE(ah, AR_PHY_RADAR_0, val);
+               udelay(1);
+               val = REG_READ(ah, AR_PHY_RADAR_0);
+               val &= ~AR_PHY_RADAR_0_FIRPWR;
+               val |= SM(AR9300_DFS_FIRPWR, AR_PHY_RADAR_0_FIRPWR);
+               REG_WRITE(ah, AR_PHY_RADAR_0, val);
+
+               return false;
+       case 0x1300000a:
+               return false;
+       case 0x0400000a:
+       case 0x04000b09:
+               return true;
+       case 0x04000409:
+               if (AR_SREV_9340(ah) || AR_SREV_9531(ah))
+                       return false;
+               else
+                       return true;
+       default:
+               /*
+                * For any other unknown signatures, do a
+                * full chip reset.
+                */
+               return true;
+       }
+}
+EXPORT_SYMBOL(ar9003_hw_bb_watchdog_check);
+
 void ar9003_hw_bb_watchdog_config(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -1930,6 +1994,7 @@ EXPORT_SYMBOL(ar9003_hw_bb_watchdog_dbg_info);
 
 void ar9003_hw_disable_phy_restart(struct ath_hw *ah)
 {
+       u8 result;
        u32 val;
 
        /* While receiving unsupported rate frame rx state machine
@@ -1937,15 +2002,13 @@ void ar9003_hw_disable_phy_restart(struct ath_hw *ah)
         * state, BB would go hang. If RXSM is in 0xb state after
         * first bb panic, ensure to disable the phy_restart.
         */
-       if (!((MS(ah->bb_watchdog_last_status,
-                 AR_PHY_WATCHDOG_RX_OFDM_SM) == 0xb) ||
-           ah->bb_hang_rx_ofdm))
-               return;
-
-       ah->bb_hang_rx_ofdm = true;
-       val = REG_READ(ah, AR_PHY_RESTART);
-       val &= ~AR_PHY_RESTART_ENA;
+       result = MS(ah->bb_watchdog_last_status, AR_PHY_WATCHDOG_RX_OFDM_SM);
 
-       REG_WRITE(ah, AR_PHY_RESTART, val);
+       if ((result == 0xb) || ah->bb_hang_rx_ofdm) {
+               ah->bb_hang_rx_ofdm = true;
+               val = REG_READ(ah, AR_PHY_RESTART);
+               val &= ~AR_PHY_RESTART_ENA;
+               REG_WRITE(ah, AR_PHY_RESTART, val);
+       }
 }
 EXPORT_SYMBOL(ar9003_hw_disable_phy_restart);
index bbbfc4d..fd090b1 100644 (file)
 #define AR_PHY_AGC              (AR_AGC_BASE + 0x14)
 #define AR_PHY_EXT_ATTEN_CTL_0  (AR_AGC_BASE + 0x18)
 #define AR_PHY_CCA_0            (AR_AGC_BASE + 0x1c)
-#define AR_PHY_EXT_CCA0         (AR_AGC_BASE + 0x20)
+#define AR_PHY_CCA_CTRL_0       (AR_AGC_BASE + 0x20)
 #define AR_PHY_RESTART          (AR_AGC_BASE + 0x24)
 
 /*
 #define AR_PHY_CCA_NOM_VAL_9300_5GHZ          -115
 #define AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ     -125
 #define AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ     -125
-#define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ     -95
-#define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ     -100
-
+#define AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ     -60
+#define AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ     -60
 #define AR_PHY_CCA_MAX_GOOD_VAL_9300_FCC_2GHZ -95
 #define AR_PHY_CCA_MAX_GOOD_VAL_9300_FCC_5GHZ -100
 
 #define AR9280_PHY_CCA_THRESH62_S   12
 #define AR_PHY_EXT_CCA0_THRESH62    0x000000FF
 #define AR_PHY_EXT_CCA0_THRESH62_S  0
+#define AR_PHY_EXT_CCA0_THRESH62_1    0x000001FF
+#define AR_PHY_EXT_CCA0_THRESH62_1_S  0
 #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK          0x0000003F
 #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S        0
 #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME           0x00001FC0
 #define AR_PHY_65NM_CH1_RXTX4       0x1650c
 #define AR_PHY_65NM_CH2_RXTX4       0x1690c
 
+#define AR_PHY_65NM_CH0_BB1         0x16140
+#define AR_PHY_65NM_CH0_BB2         0x16144
+#define AR_PHY_65NM_CH0_BB3         0x16148
+#define AR_PHY_65NM_CH1_BB1         0x16540
+#define AR_PHY_65NM_CH1_BB2         0x16544
+#define AR_PHY_65NM_CH1_BB3         0x16548
+#define AR_PHY_65NM_CH2_BB1         0x16940
+#define AR_PHY_65NM_CH2_BB2         0x16944
+#define AR_PHY_65NM_CH2_BB3         0x16948
+
 #define AR_PHY_65NM_CH0_SYNTH12_VREFMUL3           0x00780000
 #define AR_PHY_65NM_CH0_SYNTH12_VREFMUL3_S         19
 #define AR_PHY_65NM_CH0_RXTX2_SYNTHON_MASK         0x00000004
 #define AR_PHY_65NM_RXRF_AGC_AGC_OUT                   0x00000004
 #define AR_PHY_65NM_RXRF_AGC_AGC_OUT_S                 2
 
+#define AR9300_DFS_FIRPWR -28
+
 #endif  /* AR9003_PHY_H */
diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
new file mode 100644 (file)
index 0000000..3c9113d
--- /dev/null
@@ -0,0 +1,718 @@
+/*
+ * Copyright (c) 2010-2011 Atheros Communications Inc.
+ * Copyright (c) 2011-2012 Qualcomm Atheros Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef INITVALS_953X_H
+#define INITVALS_953X_H
+
+#define qca953x_1p0_mac_postamble ar9300_2p2_mac_postamble
+
+#define qca953x_1p0_soc_postamble ar9300_2p2_soc_postamble
+
+#define qca953x_1p0_common_rx_gain_table ar9300Common_rx_gain_table_2p2
+
+#define qca953x_1p0_common_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2
+
+#define qca953x_1p0_modes_fast_clock ar9300Modes_fast_clock_2p2
+
+static const u32 qca953x_1p0_mac_core[][2] = {
+       /* Addr      allmodes  */
+       {0x00000008, 0x00000000},
+       {0x00000030, 0x00020085},
+       {0x00000034, 0x00000005},
+       {0x00000040, 0x00000000},
+       {0x00000044, 0x00000000},
+       {0x00000048, 0x00000008},
+       {0x0000004c, 0x00000010},
+       {0x00000050, 0x00000000},
+       {0x00001040, 0x002ffc0f},
+       {0x00001044, 0x002ffc0f},
+       {0x00001048, 0x002ffc0f},
+       {0x0000104c, 0x002ffc0f},
+       {0x00001050, 0x002ffc0f},
+       {0x00001054, 0x002ffc0f},
+       {0x00001058, 0x002ffc0f},
+       {0x0000105c, 0x002ffc0f},
+       {0x00001060, 0x002ffc0f},
+       {0x00001064, 0x002ffc0f},
+       {0x000010f0, 0x00000100},
+       {0x00001270, 0x00000000},
+       {0x000012b0, 0x00000000},
+       {0x000012f0, 0x00000000},
+       {0x0000143c, 0x00000000},
+       {0x0000147c, 0x00000000},
+       {0x00008000, 0x00000000},
+       {0x00008004, 0x00000000},
+       {0x00008008, 0x00000000},
+       {0x0000800c, 0x00000000},
+       {0x00008018, 0x00000000},
+       {0x00008020, 0x00000000},
+       {0x00008038, 0x00000000},
+       {0x0000803c, 0x00000000},
+       {0x00008040, 0x00000000},
+       {0x00008044, 0x00000000},
+       {0x00008048, 0x00000000},
+       {0x0000804c, 0xffffffff},
+       {0x00008054, 0x00000000},
+       {0x00008058, 0x00000000},
+       {0x0000805c, 0x000fc78f},
+       {0x00008060, 0x0000000f},
+       {0x00008064, 0x00000000},
+       {0x00008070, 0x00000310},
+       {0x00008074, 0x00000020},
+       {0x00008078, 0x00000000},
+       {0x0000809c, 0x0000000f},
+       {0x000080a0, 0x00000000},
+       {0x000080a4, 0x02ff0000},
+       {0x000080a8, 0x0e070605},
+       {0x000080ac, 0x0000000d},
+       {0x000080b0, 0x00000000},
+       {0x000080b4, 0x00000000},
+       {0x000080b8, 0x00000000},
+       {0x000080bc, 0x00000000},
+       {0x000080c0, 0x2a800000},
+       {0x000080c4, 0x06900168},
+       {0x000080c8, 0x13881c22},
+       {0x000080cc, 0x01f40000},
+       {0x000080d0, 0x00252500},
+       {0x000080d4, 0x00a00000},
+       {0x000080d8, 0x00400000},
+       {0x000080dc, 0x00000000},
+       {0x000080e0, 0xffffffff},
+       {0x000080e4, 0x0000ffff},
+       {0x000080e8, 0x3f3f3f3f},
+       {0x000080ec, 0x00000000},
+       {0x000080f0, 0x00000000},
+       {0x000080f4, 0x00000000},
+       {0x000080fc, 0x00020000},
+       {0x00008100, 0x00000000},
+       {0x00008108, 0x00000052},
+       {0x0000810c, 0x00000000},
+       {0x00008110, 0x00000000},
+       {0x00008114, 0x000007ff},
+       {0x00008118, 0x000000aa},
+       {0x0000811c, 0x00003210},
+       {0x00008124, 0x00000000},
+       {0x00008128, 0x00000000},
+       {0x0000812c, 0x00000000},
+       {0x00008130, 0x00000000},
+       {0x00008134, 0x00000000},
+       {0x00008138, 0x00000000},
+       {0x0000813c, 0x0000ffff},
+       {0x00008140, 0x000000fe},
+       {0x00008144, 0xffffffff},
+       {0x00008168, 0x00000000},
+       {0x0000816c, 0x00000000},
+       {0x000081c0, 0x00000000},
+       {0x000081c4, 0x33332210},
+       {0x000081ec, 0x00000000},
+       {0x000081f0, 0x00000000},
+       {0x000081f4, 0x00000000},
+       {0x000081f8, 0x00000000},
+       {0x000081fc, 0x00000000},
+       {0x00008240, 0x00100000},
+       {0x00008244, 0x0010f3d7},
+       {0x00008248, 0x00000852},
+       {0x0000824c, 0x0001e7ae},
+       {0x00008250, 0x00000000},
+       {0x00008254, 0x00000000},
+       {0x00008258, 0x00000000},
+       {0x0000825c, 0x40000000},
+       {0x00008260, 0x00080922},
+       {0x00008264, 0x9d400010},
+       {0x00008268, 0xffffffff},
+       {0x0000826c, 0x0000ffff},
+       {0x00008270, 0x00000000},
+       {0x00008274, 0x40000000},
+       {0x00008278, 0x003e4180},
+       {0x0000827c, 0x00000004},
+       {0x00008284, 0x0000002c},
+       {0x00008288, 0x0000002c},
+       {0x0000828c, 0x000000ff},
+       {0x00008294, 0x00000000},
+       {0x00008298, 0x00000000},
+       {0x0000829c, 0x00000000},
+       {0x00008300, 0x00001d40},
+       {0x00008314, 0x00000000},
+       {0x0000831c, 0x0000010d},
+       {0x00008328, 0x00000000},
+       {0x0000832c, 0x0000001f},
+       {0x00008330, 0x00000302},
+       {0x00008334, 0x00000700},
+       {0x00008338, 0xffff0000},
+       {0x0000833c, 0x02400000},
+       {0x00008340, 0x000107ff},
+       {0x00008344, 0xaa48107b},
+       {0x00008348, 0x008f0000},
+       {0x0000835c, 0x00000000},
+       {0x00008360, 0xffffffff},
+       {0x00008364, 0xffffffff},
+       {0x00008368, 0x00000000},
+       {0x00008370, 0x00000000},
+       {0x00008374, 0x000000ff},
+       {0x00008378, 0x00000000},
+       {0x0000837c, 0x00000000},
+       {0x00008380, 0xffffffff},
+       {0x00008384, 0xffffffff},
+       {0x00008390, 0xffffffff},
+       {0x00008394, 0xffffffff},
+       {0x00008398, 0x00000000},
+       {0x0000839c, 0x00000000},
+       {0x000083a0, 0x00000000},
+       {0x000083a4, 0x0000fa14},
+       {0x000083a8, 0x000f0c00},
+       {0x000083ac, 0x33332210},
+       {0x000083b0, 0x33332210},
+       {0x000083b4, 0x33332210},
+       {0x000083b8, 0x33332210},
+       {0x000083bc, 0x00000000},
+       {0x000083c0, 0x00000000},
+       {0x000083c4, 0x00000000},
+       {0x000083c8, 0x00000000},
+       {0x000083cc, 0x00000200},
+       {0x000083d0, 0x8c7901ff},
+};
+
+static const u32 qca953x_1p0_baseband_core[][2] = {
+       /* Addr      allmodes  */
+       {0x00009800, 0xafe68e30},
+       {0x00009804, 0xfd14e000},
+       {0x00009808, 0x9c0a9f6b},
+       {0x0000980c, 0x04900000},
+       {0x00009814, 0x0280c00a},
+       {0x00009818, 0x00000000},
+       {0x0000981c, 0x00020028},
+       {0x00009834, 0x6400a190},
+       {0x00009838, 0x0108ecff},
+       {0x0000983c, 0x14000600},
+       {0x00009880, 0x201fff00},
+       {0x00009884, 0x00001042},
+       {0x000098a4, 0x00200400},
+       {0x000098b0, 0x32840bbe},
+       {0x000098bc, 0x00000002},
+       {0x000098d0, 0x004b6a8e},
+       {0x000098d4, 0x00000820},
+       {0x000098dc, 0x00000000},
+       {0x000098f0, 0x00000000},
+       {0x000098f4, 0x00000000},
+       {0x00009c04, 0xff55ff55},
+       {0x00009c08, 0x0320ff55},
+       {0x00009c0c, 0x00000000},
+       {0x00009c10, 0x00000000},
+       {0x00009c14, 0x00046384},
+       {0x00009c18, 0x05b6b440},
+       {0x00009c1c, 0x00b6b440},
+       {0x00009d00, 0xc080a333},
+       {0x00009d04, 0x40206c10},
+       {0x00009d08, 0x009c4060},
+       {0x00009d0c, 0x9883800a},
+       {0x00009d10, 0x01884061},
+       {0x00009d14, 0x00c0040b},
+       {0x00009d18, 0x00000000},
+       {0x00009e08, 0x0038230c},
+       {0x00009e24, 0x990bb515},
+       {0x00009e28, 0x0c6f0000},
+       {0x00009e30, 0x06336f77},
+       {0x00009e34, 0x6af6532f},
+       {0x00009e38, 0x0cc80c00},
+       {0x00009e40, 0x0d261820},
+       {0x00009e4c, 0x00001004},
+       {0x00009e50, 0x00ff03f1},
+       {0x00009fc0, 0x813e4788},
+       {0x00009fc4, 0x0001efb5},
+       {0x00009fcc, 0x40000014},
+       {0x00009fd0, 0x01193b91},
+       {0x0000a20c, 0x00000000},
+       {0x0000a220, 0x00000000},
+       {0x0000a224, 0x00000000},
+       {0x0000a228, 0x10002310},
+       {0x0000a23c, 0x00000000},
+       {0x0000a244, 0x0c000000},
+       {0x0000a248, 0x00000140},
+       {0x0000a2a0, 0x00000007},
+       {0x0000a2c0, 0x00000007},
+       {0x0000a2c8, 0x00000000},
+       {0x0000a2d4, 0x00000000},
+       {0x0000a2ec, 0x00000000},
+       {0x0000a2f0, 0x00000000},
+       {0x0000a2f4, 0x00000000},
+       {0x0000a2f8, 0x00000000},
+       {0x0000a344, 0x00000000},
+       {0x0000a34c, 0x00000000},
+       {0x0000a350, 0x0000a000},
+       {0x0000a364, 0x00000000},
+       {0x0000a370, 0x00000000},
+       {0x0000a390, 0x00000001},
+       {0x0000a394, 0x00000444},
+       {0x0000a398, 0x1f020503},
+       {0x0000a39c, 0x29180c03},
+       {0x0000a3a0, 0x9a8b6844},
+       {0x0000a3a4, 0x000000ff},
+       {0x0000a3a8, 0x6a6a6a6a},
+       {0x0000a3ac, 0x6a6a6a6a},
+       {0x0000a3b0, 0x00c8641a},
+       {0x0000a3b4, 0x0000001a},
+       {0x0000a3b8, 0x0088642a},
+       {0x0000a3bc, 0x000001fa},
+       {0x0000a3c0, 0x20202020},
+       {0x0000a3c4, 0x22222220},
+       {0x0000a3c8, 0x20200020},
+       {0x0000a3cc, 0x20202020},
+       {0x0000a3d0, 0x20202020},
+       {0x0000a3d4, 0x20202020},
+       {0x0000a3d8, 0x20202020},
+       {0x0000a3dc, 0x20202020},
+       {0x0000a3e0, 0x20202020},
+       {0x0000a3e4, 0x20202020},
+       {0x0000a3e8, 0x20202020},
+       {0x0000a3ec, 0x20202020},
+       {0x0000a3f0, 0x00000000},
+       {0x0000a3f4, 0x00000000},
+       {0x0000a3f8, 0x0c9bd380},
+       {0x0000a3fc, 0x000f0f01},
+       {0x0000a400, 0x8fa91f01},
+       {0x0000a404, 0x00000000},
+       {0x0000a408, 0x0e79e5c6},
+       {0x0000a40c, 0x00820820},
+       {0x0000a414, 0x1ce42108},
+       {0x0000a418, 0x2d001dce},
+       {0x0000a41c, 0x1ce73908},
+       {0x0000a420, 0x000001ce},
+       {0x0000a424, 0x1ce738e7},
+       {0x0000a428, 0x000001ce},
+       {0x0000a42c, 0x1ce739ce},
+       {0x0000a430, 0x1ce739ce},
+       {0x0000a434, 0x00000000},
+       {0x0000a438, 0x00001801},
+       {0x0000a43c, 0x00100000},
+       {0x0000a444, 0x00000000},
+       {0x0000a448, 0x05000080},
+       {0x0000a44c, 0x00000001},
+       {0x0000a450, 0x00010000},
+       {0x0000a458, 0x00000000},
+       {0x0000a644, 0xbfad9d74},
+       {0x0000a648, 0x0048060a},
+       {0x0000a64c, 0x00003c37},
+       {0x0000a670, 0x03020100},
+       {0x0000a674, 0x09080504},
+       {0x0000a678, 0x0d0c0b0a},
+       {0x0000a67c, 0x13121110},
+       {0x0000a680, 0x31301514},
+       {0x0000a684, 0x35343332},
+       {0x0000a688, 0x00000036},
+       {0x0000a690, 0x08000838},
+       {0x0000a7cc, 0x00000000},
+       {0x0000a7d0, 0x00000000},
+       {0x0000a7d4, 0x00000004},
+       {0x0000a7dc, 0x00000000},
+       {0x0000a8d0, 0x004b6a8e},
+       {0x0000a8d4, 0x00000820},
+       {0x0000a8dc, 0x00000000},
+       {0x0000a8f0, 0x00000000},
+       {0x0000a8f4, 0x00000000},
+       {0x0000b2d0, 0x00000080},
+       {0x0000b2d4, 0x00000000},
+       {0x0000b2ec, 0x00000000},
+       {0x0000b2f0, 0x00000000},
+       {0x0000b2f4, 0x00000000},
+       {0x0000b2f8, 0x00000000},
+       {0x0000b408, 0x0e79e5c0},
+       {0x0000b40c, 0x00820820},
+       {0x0000b420, 0x00000000},
+};
+
+static const u32 qca953x_1p0_baseband_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+       {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+       {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+       {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+       {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+       {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+       {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+       {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+       {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+       {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+       {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+       {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
+       {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+       {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+       {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+       {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822},
+       {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+       {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+       {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+       {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0},
+       {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+       {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
+       {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+       {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+       {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
+       {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+       {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+       {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+       {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e},
+       {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+       {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e},
+       {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+       {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+       {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+       {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+       {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+       {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
+       {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
+       {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+       {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+       {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+       {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+       {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+       {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+       {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+};
+
+static const u32 qca953x_1p0_radio_core[][2] = {
+       /* Addr      allmodes  */
+       {0x00016000, 0x36db6db6},
+       {0x00016004, 0x6db6db40},
+       {0x00016008, 0x73f00000},
+       {0x0001600c, 0x00000000},
+       {0x00016040, 0x3f80fff8},
+       {0x0001604c, 0x000f0278},
+       {0x00016050, 0x8036db6c},
+       {0x00016054, 0x6db60000},
+       {0x00016080, 0x00080000},
+       {0x00016084, 0x0e48048c},
+       {0x00016088, 0x14214514},
+       {0x0001608c, 0x119f080a},
+       {0x00016090, 0x24926490},
+       {0x00016094, 0x00000000},
+       {0x000160a0, 0xc2108ffe},
+       {0x000160a4, 0x812fc370},
+       {0x000160a8, 0x423c8000},
+       {0x000160b4, 0x92480080},
+       {0x000160c0, 0x006db6d8},
+       {0x000160c4, 0x24b6db6c},
+       {0x000160c8, 0x6db6db6c},
+       {0x000160cc, 0x6db6fb7c},
+       {0x000160d0, 0x6db6da44},
+       {0x00016100, 0x07ff8001},
+       {0x00016108, 0x00080010},
+       {0x00016144, 0x01884080},
+       {0x00016148, 0x000080d8},
+       {0x00016280, 0x01000901},
+       {0x00016284, 0x15d30000},
+       {0x00016288, 0x00318000},
+       {0x0001628c, 0x50000000},
+       {0x00016380, 0x00000000},
+       {0x00016384, 0x00000000},
+       {0x00016388, 0x00800700},
+       {0x0001638c, 0x00800700},
+       {0x00016390, 0x00800700},
+       {0x00016394, 0x00000000},
+       {0x00016398, 0x00000000},
+       {0x0001639c, 0x00000000},
+       {0x000163a0, 0x00000001},
+       {0x000163a4, 0x00000001},
+       {0x000163a8, 0x00000000},
+       {0x000163ac, 0x00000000},
+       {0x000163b0, 0x00000000},
+       {0x000163b4, 0x00000000},
+       {0x000163b8, 0x00000000},
+       {0x000163bc, 0x00000000},
+       {0x000163c0, 0x000000a0},
+       {0x000163c4, 0x000c0000},
+       {0x000163c8, 0x14021402},
+       {0x000163cc, 0x00001402},
+       {0x000163d0, 0x00000000},
+       {0x000163d4, 0x00000000},
+       {0x00016400, 0x36db6db6},
+       {0x00016404, 0x6db6db40},
+       {0x00016408, 0x73f00000},
+       {0x0001640c, 0x00000000},
+       {0x00016440, 0x3f80fff8},
+       {0x0001644c, 0x000f0278},
+       {0x00016450, 0x8036db6c},
+       {0x00016454, 0x6db60000},
+       {0x00016500, 0x07ff8001},
+       {0x00016508, 0x00080010},
+       {0x00016544, 0x01884080},
+       {0x00016548, 0x000080d8},
+       {0x00016780, 0x00000000},
+       {0x00016784, 0x00000000},
+       {0x00016788, 0x00800700},
+       {0x0001678c, 0x00800700},
+       {0x00016790, 0x00800700},
+       {0x00016794, 0x00000000},
+       {0x00016798, 0x00000000},
+       {0x0001679c, 0x00000000},
+       {0x000167a0, 0x00000001},
+       {0x000167a4, 0x00000001},
+       {0x000167a8, 0x00000000},
+       {0x000167ac, 0x00000000},
+       {0x000167b0, 0x00000000},
+       {0x000167b4, 0x00000000},
+       {0x000167b8, 0x00000000},
+       {0x000167bc, 0x00000000},
+       {0x000167c0, 0x000000a0},
+       {0x000167c4, 0x000c0000},
+       {0x000167c8, 0x14021402},
+       {0x000167cc, 0x00001402},
+       {0x000167d0, 0x00000000},
+       {0x000167d4, 0x00000000},
+};
+
+static const u32 qca953x_1p0_radio_postamble[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xc4128f5c, 0xc4128f5c},
+       {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x0fd08f25, 0x0fd08f25},
+       {0x000160ac, 0xa4647c00, 0xa4647c00, 0x24646800, 0x24646800},
+       {0x000160b0, 0x01885f52, 0x01885f52, 0x00fe7f46, 0x00fe7f46},
+       {0x00016104, 0xb7a00001, 0xb7a00001, 0xfff80005, 0xfff80005},
+       {0x0001610c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000},
+       {0x00016140, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
+       {0x00016504, 0xb7a00001, 0xb7a00001, 0xfff80001, 0xfff80001},
+       {0x0001650c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000},
+       {0x00016540, 0x10804008, 0x10804008, 0x50804000, 0x50804000},
+};
+
+static const u32 qca953x_1p0_soc_preamble[][2] = {
+       /* Addr      allmodes  */
+       {0x00007000, 0x00000000},
+       {0x00007004, 0x00000000},
+       {0x00007008, 0x00000000},
+       {0x0000700c, 0x00000000},
+       {0x0000701c, 0x00000000},
+       {0x00007020, 0x00000000},
+       {0x00007024, 0x00000000},
+       {0x00007028, 0x00000000},
+       {0x0000702c, 0x00000000},
+       {0x00007030, 0x00000000},
+       {0x00007034, 0x00000002},
+       {0x00007038, 0x000004c2},
+       {0x00007048, 0x00000000},
+};
+
+static const u32 qca953x_1p0_common_rx_gain_bounds[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+       {0x00009e48, 0x5030201a, 0x5030201a, 0x50302018, 0x50302018},
+};
+
+static const u32 qca953x_1p0_common_wo_xlna_rx_gain_bounds[][5] = {
+       /* Addr      5G_HT20     5G_HT40     2G_HT40     2G_HT20   */
+       {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+       {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+};
+
+static const u32 qca953x_1p0_modes_xpa_tx_gain_table[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a2dc, 0xfffd5aaa},
+       {0x0000a2e0, 0xfffe9ccc},
+       {0x0000a2e4, 0xffffe0f0},
+       {0x0000a2e8, 0xfffcff00},
+       {0x0000a410, 0x000050da},
+       {0x0000a500, 0x00000000},
+       {0x0000a504, 0x04000002},
+       {0x0000a508, 0x08000004},
+       {0x0000a50c, 0x0c000006},
+       {0x0000a510, 0x0f00000a},
+       {0x0000a514, 0x1300000c},
+       {0x0000a518, 0x1700000e},
+       {0x0000a51c, 0x1b000064},
+       {0x0000a520, 0x1f000242},
+       {0x0000a524, 0x23000229},
+       {0x0000a528, 0x270002a2},
+       {0x0000a52c, 0x2c001203},
+       {0x0000a530, 0x30001803},
+       {0x0000a534, 0x33000881},
+       {0x0000a538, 0x38001809},
+       {0x0000a53c, 0x3a000814},
+       {0x0000a540, 0x3f001a0c},
+       {0x0000a544, 0x43001a0e},
+       {0x0000a548, 0x46001812},
+       {0x0000a54c, 0x49001884},
+       {0x0000a550, 0x4d001e84},
+       {0x0000a554, 0x50001e69},
+       {0x0000a558, 0x550006f4},
+       {0x0000a55c, 0x59000ad3},
+       {0x0000a560, 0x5e000ad5},
+       {0x0000a564, 0x61001ced},
+       {0x0000a568, 0x660018d4},
+       {0x0000a56c, 0x660018d4},
+       {0x0000a570, 0x660018d4},
+       {0x0000a574, 0x660018d4},
+       {0x0000a578, 0x660018d4},
+       {0x0000a57c, 0x660018d4},
+       {0x0000a600, 0x00000000},
+       {0x0000a604, 0x00000000},
+       {0x0000a608, 0x00000000},
+       {0x0000a60c, 0x03804000},
+       {0x0000a610, 0x0300ca02},
+       {0x0000a614, 0x00000e04},
+       {0x0000a618, 0x03014000},
+       {0x0000a61c, 0x00000000},
+       {0x0000a620, 0x00000000},
+       {0x0000a624, 0x03014000},
+       {0x0000a628, 0x03804c05},
+       {0x0000a62c, 0x0701de06},
+       {0x0000a630, 0x07819c07},
+       {0x0000a634, 0x0701dc07},
+       {0x0000a638, 0x0701dc07},
+       {0x0000a63c, 0x0701dc07},
+       {0x0000b2dc, 0xfffd5aaa},
+       {0x0000b2e0, 0xfffe9ccc},
+       {0x0000b2e4, 0xffffe0f0},
+       {0x0000b2e8, 0xfffcff00},
+       {0x00016044, 0x010002d4},
+       {0x00016048, 0x66482400},
+       {0x00016280, 0x01000015},
+       {0x00016444, 0x010002d4},
+       {0x00016448, 0x66482400},
+};
+
+static const u32 qca953x_1p0_modes_no_xpa_tx_gain_table[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a2dc, 0xffd5f552},
+       {0x0000a2e0, 0xffe60664},
+       {0x0000a2e4, 0xfff80780},
+       {0x0000a2e8, 0xfffff800},
+       {0x0000a410, 0x000050d6},
+       {0x0000a500, 0x00060020},
+       {0x0000a504, 0x04060060},
+       {0x0000a508, 0x080600a0},
+       {0x0000a50c, 0x0c068020},
+       {0x0000a510, 0x10068060},
+       {0x0000a514, 0x140680a0},
+       {0x0000a518, 0x18090040},
+       {0x0000a51c, 0x1b090080},
+       {0x0000a520, 0x1f0900c0},
+       {0x0000a524, 0x240c0041},
+       {0x0000a528, 0x280d0021},
+       {0x0000a52c, 0x2d0f0061},
+       {0x0000a530, 0x310f00a1},
+       {0x0000a534, 0x350e00a2},
+       {0x0000a538, 0x360e80a2},
+       {0x0000a53c, 0x380f00a2},
+       {0x0000a540, 0x3b0e00a3},
+       {0x0000a544, 0x3d110083},
+       {0x0000a548, 0x3e1100a3},
+       {0x0000a54c, 0x401100e3},
+       {0x0000a550, 0x421380e3},
+       {0x0000a554, 0x431780e3},
+       {0x0000a558, 0x461f80e3},
+       {0x0000a55c, 0x461f80e3},
+       {0x0000a560, 0x461f80e3},
+       {0x0000a564, 0x461f80e3},
+       {0x0000a568, 0x461f80e3},
+       {0x0000a56c, 0x461f80e3},
+       {0x0000a570, 0x461f80e3},
+       {0x0000a574, 0x461f80e3},
+       {0x0000a578, 0x461f80e3},
+       {0x0000a57c, 0x461f80e3},
+       {0x0000a600, 0x00000000},
+       {0x0000a604, 0x00000000},
+       {0x0000a608, 0x00000000},
+       {0x0000a60c, 0x00804201},
+       {0x0000a610, 0x01008201},
+       {0x0000a614, 0x0180c402},
+       {0x0000a618, 0x0180c603},
+       {0x0000a61c, 0x0180c603},
+       {0x0000a620, 0x01c10603},
+       {0x0000a624, 0x01c10704},
+       {0x0000a628, 0x02c18b05},
+       {0x0000a62c, 0x0301cc07},
+       {0x0000a630, 0x0301cc07},
+       {0x0000a634, 0x0301cc07},
+       {0x0000a638, 0x0301cc07},
+       {0x0000a63c, 0x0301cc07},
+       {0x0000b2dc, 0xffd5f552},
+       {0x0000b2e0, 0xffe60664},
+       {0x0000b2e4, 0xfff80780},
+       {0x0000b2e8, 0xfffff800},
+       {0x00016044, 0x049242db},
+       {0x00016048, 0x6c927a70},
+       {0x00016444, 0x049242db},
+       {0x00016448, 0x6c927a70},
+};
+
+static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = {
+       /* Addr      allmodes  */
+       {0x0000a2dc, 0xffd5f552},
+       {0x0000a2e0, 0xffe60664},
+       {0x0000a2e4, 0xfff80780},
+       {0x0000a2e8, 0xfffff800},
+       {0x0000a410, 0x000050de},
+       {0x0000a500, 0x00000061},
+       {0x0000a504, 0x04000063},
+       {0x0000a508, 0x08000065},
+       {0x0000a50c, 0x0c000261},
+       {0x0000a510, 0x10000263},
+       {0x0000a514, 0x14000265},
+       {0x0000a518, 0x18000482},
+       {0x0000a51c, 0x1b000484},
+       {0x0000a520, 0x1f000486},
+       {0x0000a524, 0x240008c2},
+       {0x0000a528, 0x28000cc1},
+       {0x0000a52c, 0x2d000ce3},
+       {0x0000a530, 0x31000ce5},
+       {0x0000a534, 0x350010e5},
+       {0x0000a538, 0x360012e5},
+       {0x0000a53c, 0x380014e5},
+       {0x0000a540, 0x3b0018e5},
+       {0x0000a544, 0x3d001d04},
+       {0x0000a548, 0x3e001d05},
+       {0x0000a54c, 0x40001d07},
+       {0x0000a550, 0x42001f27},
+       {0x0000a554, 0x43001f67},
+       {0x0000a558, 0x46001fe7},
+       {0x0000a55c, 0x47001f2b},
+       {0x0000a560, 0x49001f0d},
+       {0x0000a564, 0x4b001ed2},
+       {0x0000a568, 0x4c001ed4},
+       {0x0000a56c, 0x4e001f15},
+       {0x0000a570, 0x4f001ff6},
+       {0x0000a574, 0x4f001ff6},
+       {0x0000a578, 0x4f001ff6},
+       {0x0000a57c, 0x4f001ff6},
+       {0x0000a600, 0x00000000},
+       {0x0000a604, 0x00000000},
+       {0x0000a608, 0x00000000},
+       {0x0000a60c, 0x00804201},
+       {0x0000a610, 0x01008201},
+       {0x0000a614, 0x0180c402},
+       {0x0000a618, 0x0180c603},
+       {0x0000a61c, 0x0180c603},
+       {0x0000a620, 0x01c10603},
+       {0x0000a624, 0x01c10704},
+       {0x0000a628, 0x02c18b05},
+       {0x0000a62c, 0x02c14c07},
+       {0x0000a630, 0x01008704},
+       {0x0000a634, 0x01c10402},
+       {0x0000a638, 0x0301cc07},
+       {0x0000a63c, 0x0301cc07},
+       {0x0000b2dc, 0xffd5f552},
+       {0x0000b2e0, 0xffe60664},
+       {0x0000b2e4, 0xfff80780},
+       {0x0000b2e8, 0xfffff800},
+       {0x00016044, 0x049242db},
+       {0x00016048, 0x6c927a70},
+       {0x00016444, 0x049242db},
+       {0x00016448, 0x6c927a70},
+};
+
+#endif /* INITVALS_953X_H */
index 5e5d5cb..f622a98 100644 (file)
 #include "common.h"
 #include "mci.h"
 #include "dfs.h"
-
-/*
- * Header for the ath9k.ko driver core *only* -- hw code nor any other driver
- * should rely on this file or its contents.
- */
+#include "spectral.h"
 
 struct ath_node;
+struct ath_rate_table;
 
-/* Macro to expand scalars to 64-bit objects */
-
-#define        ito64(x) (sizeof(x) == 1) ?                     \
-       (((unsigned long long int)(x)) & (0xff)) :      \
-       (sizeof(x) == 2) ?                              \
-       (((unsigned long long int)(x)) & 0xffff) :      \
-       ((sizeof(x) == 4) ?                             \
-        (((unsigned long long int)(x)) & 0xffffffff) : \
-        (unsigned long long int)(x))
-
-/* increment with wrap-around */
-#define INCR(_l, _sz)   do {                   \
-               (_l)++;                         \
-               (_l) &= ((_sz) - 1);            \
-       } while (0)
-
-/* decrement with wrap-around */
-#define DECR(_l,  _sz)  do {                   \
-               (_l)--;                         \
-               (_l) &= ((_sz) - 1);            \
-       } while (0)
-
-#define TSF_TO_TU(_h,_l) \
-       ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
-
-#define        ATH_TXQ_SETUP(sc, i)        ((sc)->tx.txqsetup & (1<<i))
+extern struct ieee80211_ops ath9k_ops;
+extern int ath9k_modparam_nohwcrypt;
+extern int led_blink;
+extern bool is_ath9k_unloaded;
 
 struct ath_config {
        u16 txpowlimit;
@@ -70,6 +45,17 @@ struct ath_config {
 /* Descriptor Management */
 /*************************/
 
+#define ATH_TXSTATUS_RING_SIZE 512
+
+/* Macro to expand scalars to 64-bit objects */
+#define        ito64(x) (sizeof(x) == 1) ?                     \
+       (((unsigned long long int)(x)) & (0xff)) :      \
+       (sizeof(x) == 2) ?                              \
+       (((unsigned long long int)(x)) & 0xffff) :      \
+       ((sizeof(x) == 4) ?                             \
+        (((unsigned long long int)(x)) & 0xffffffff) : \
+        (unsigned long long int)(x))
+
 #define ATH_TXBUF_RESET(_bf) do {                              \
                (_bf)->bf_lastbf = NULL;                        \
                (_bf)->bf_next = NULL;                          \
@@ -77,23 +63,6 @@ struct ath_config {
                       sizeof(struct ath_buf_state));           \
        } while (0)
 
-/**
- * enum buffer_type - Buffer type flags
- *
- * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX)
- * @BUF_AGGR: Indicates whether the buffer can be aggregated
- *     (used in aggregation scheduling)
- */
-enum buffer_type {
-       BUF_AMPDU               = BIT(0),
-       BUF_AGGR                = BIT(1),
-};
-
-#define bf_isampdu(bf)         (bf->bf_state.bf_type & BUF_AMPDU)
-#define bf_isaggr(bf)          (bf->bf_state.bf_type & BUF_AGGR)
-
-#define ATH_TXSTATUS_RING_SIZE 512
-
 #define        DS2PHYS(_dd, _ds)                                               \
        ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc))
 #define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0)
@@ -113,11 +82,20 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 /* RX / TX */
 /***********/
 
+#define        ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i))
+
+/* increment with wrap-around */
+#define INCR(_l, _sz)   do {                   \
+               (_l)++;                         \
+               (_l) &= ((_sz) - 1);            \
+       } while (0)
+
 #define ATH_RXBUF               512
 #define ATH_TXBUF               512
 #define ATH_TXBUF_RESERVE       5
 #define ATH_MAX_QDEPTH          (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE)
 #define ATH_TXMAXTRY            13
+#define ATH_MAX_SW_RETRIES      30
 
 #define TID_TO_WME_AC(_tid)                            \
        ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE :   \
@@ -133,6 +111,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 #define ATH_AGGR_MIN_QDEPTH        2
 /* minimum h/w qdepth for non-aggregated traffic */
 #define ATH_NON_AGGR_MIN_QDEPTH    8
+#define ATH_TX_COMPLETE_POLL_INT   1000
+#define ATH_TXFIFO_DEPTH           8
+#define ATH_TX_ERROR               0x01
 
 #define IEEE80211_SEQ_SEQ_SHIFT    4
 #define IEEE80211_SEQ_MAX          4096
@@ -167,9 +148,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 
 #define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e))
 
-#define ATH_TX_COMPLETE_POLL_INT       1000
-
-#define ATH_TXFIFO_DEPTH 8
 struct ath_txq {
        int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */
        u32 axq_qnum; /* ath9k hardware queue number */
@@ -214,6 +192,21 @@ struct ath_rxbuf {
        dma_addr_t bf_buf_addr;
 };
 
+/**
+ * enum buffer_type - Buffer type flags
+ *
+ * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX)
+ * @BUF_AGGR: Indicates whether the buffer can be aggregated
+ *     (used in aggregation scheduling)
+ */
+enum buffer_type {
+       BUF_AMPDU               = BIT(0),
+       BUF_AGGR                = BIT(1),
+};
+
+#define bf_isampdu(bf)         (bf->bf_state.bf_type & BUF_AMPDU)
+#define bf_isaggr(bf)          (bf->bf_state.bf_type & BUF_AGGR)
+
 struct ath_buf_state {
        u8 bf_type;
        u8 bfs_paprd;
@@ -278,7 +271,6 @@ struct ath_tx_control {
        struct ieee80211_sta *sta;
 };
 
-#define ATH_TX_ERROR        0x01
 
 /**
  * @txq_map:  Index is mac80211 queue number.  This is
@@ -372,6 +364,22 @@ struct ath_vif {
        struct ath_buf *av_bcbuf;
 };
 
+struct ath9k_vif_iter_data {
+       u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */
+       u8 mask[ETH_ALEN]; /* bssid mask */
+       bool has_hw_macaddr;
+
+       int naps;      /* number of AP vifs */
+       int nmeshes;   /* number of mesh vifs */
+       int nstations; /* number of station vifs */
+       int nwds;      /* number of WDS vifs */
+       int nadhocs;   /* number of adhoc vifs */
+};
+
+void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
+                              struct ieee80211_vif *vif,
+                              struct ath9k_vif_iter_data *iter_data);
+
 /*******************/
 /* Beacon Handling */
 /*******************/
@@ -387,6 +395,9 @@ struct ath_vif {
 #define ATH_DEFAULT_BMISS_LIMIT        10
 #define IEEE80211_MS_TO_TU(x)           (((x) * 1000) / 1024)
 
+#define TSF_TO_TU(_h,_l) \
+       ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10))
+
 struct ath_beacon_config {
        int beacon_interval;
        u16 listen_interval;
@@ -420,12 +431,10 @@ struct ath_beacon {
 };
 
 void ath9k_beacon_tasklet(unsigned long data);
-bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
                         u32 changed);
 void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif);
-void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif);
 void ath9k_set_beacon(struct ath_softc *sc);
 bool ath9k_csa_is_finished(struct ath_softc *sc);
 
@@ -440,17 +449,14 @@ bool ath9k_csa_is_finished(struct ath_softc *sc);
 #define ATH_LONG_CALINTERVAL_INT  1000    /* 1000 ms */
 #define ATH_LONG_CALINTERVAL      30000   /* 30 seconds */
 #define ATH_RESTART_CALINTERVAL   1200000 /* 20 minutes */
-#define ATH_ANI_MAX_SKIP_COUNT  10
-
-#define ATH_PAPRD_TIMEOUT      100 /* msecs */
-#define ATH_PLL_WORK_INTERVAL   100
+#define ATH_ANI_MAX_SKIP_COUNT    10
+#define ATH_PAPRD_TIMEOUT         100 /* msecs */
+#define ATH_PLL_WORK_INTERVAL     100
 
 void ath_tx_complete_poll_work(struct work_struct *work);
 void ath_reset_work(struct work_struct *work);
-void ath_hw_check(struct work_struct *work);
+bool ath_hw_check(struct ath_softc *sc);
 void ath_hw_pll_work(struct work_struct *work);
-void ath_rx_poll(unsigned long data);
-void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon);
 void ath_paprd_calibrate(struct work_struct *work);
 void ath_ani_calibrate(unsigned long data);
 void ath_start_ani(struct ath_softc *sc);
@@ -477,20 +483,19 @@ enum bt_op_flags {
 };
 
 struct ath_btcoex {
-       bool hw_timer_enabled;
        spinlock_t btcoex_lock;
        struct timer_list period_timer; /* Timer for BT period */
+       struct timer_list no_stomp_timer;
        u32 bt_priority_cnt;
        unsigned long bt_priority_time;
        unsigned long op_flags;
        int bt_stomp_type; /* Types of BT stomping */
-       u32 btcoex_no_stomp; /* in usec */
+       u32 btcoex_no_stomp; /* in msec */
        u32 btcoex_period; /* in msec */
-       u32 btscan_no_stomp; /* in usec */
+       u32 btscan_no_stomp; /* in msec */
        u32 duty_cycle;
        u32 bt_wait_time;
        int rssi_count;
-       struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */
        struct ath_mci_profile mci;
        u8 stomp_audio;
 };
@@ -538,12 +543,6 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size)
 }
 #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */
 
-struct ath9k_wow_pattern {
-       u8 pattern_bytes[MAX_PATTERN_SIZE];
-       u8 mask_bytes[MAX_PATTERN_SIZE];
-       u32 pattern_len;
-};
-
 /********************/
 /*   LED Control    */
 /********************/
@@ -575,6 +574,12 @@ static inline void ath_fill_led_pin(struct ath_softc *sc)
 /* Wake on Wireless LAN */
 /************************/
 
+struct ath9k_wow_pattern {
+       u8 pattern_bytes[MAX_PATTERN_SIZE];
+       u8 mask_bytes[MAX_PATTERN_SIZE];
+       u32 pattern_len;
+};
+
 #ifdef CONFIG_ATH9K_WOW
 void ath9k_init_wow(struct ieee80211_hw *hw);
 int ath9k_suspend(struct ieee80211_hw *hw,
@@ -678,13 +683,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
  * Used when PCI device not fully initialized by bootrom/BIOS
 */
 #define DEFAULT_CACHELINE       32
-#define ATH_REGCLASSIDS_MAX     10
 #define ATH_CABQ_READY_TIME     80      /* % of beacon interval */
-#define ATH_MAX_SW_RETRIES      30
-#define ATH_CHAN_MAX            255
-
 #define ATH_TXPOWER_MAX         100     /* .5 dBm units */
-#define ATH_RATE_DUMMY_MARKER   0
 
 enum sc_op_flags {
        SC_OP_INVALID,
@@ -703,37 +703,6 @@ enum sc_op_flags {
 #define PS_BEACON_SYNC            BIT(4)
 #define PS_WAIT_FOR_ANI           BIT(5)
 
-struct ath_rate_table;
-
-struct ath9k_vif_iter_data {
-       u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */
-       u8 mask[ETH_ALEN]; /* bssid mask */
-       bool has_hw_macaddr;
-
-       int naps;      /* number of AP vifs */
-       int nmeshes;   /* number of mesh vifs */
-       int nstations; /* number of station vifs */
-       int nwds;      /* number of WDS vifs */
-       int nadhocs;   /* number of adhoc vifs */
-};
-
-/* enum spectral_mode:
- *
- * @SPECTRAL_DISABLED: spectral mode is disabled
- * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
- *     something else.
- * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
- *     is performed manually.
- * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels
- *     during a channel scan.
- */
-enum spectral_mode {
-       SPECTRAL_DISABLED = 0,
-       SPECTRAL_BACKGROUND,
-       SPECTRAL_MANUAL,
-       SPECTRAL_CHANSCAN,
-};
-
 struct ath_softc {
        struct ieee80211_hw *hw;
        struct device *dev;
@@ -751,12 +720,10 @@ struct ath_softc {
        spinlock_t sc_pcu_lock;
        struct mutex mutex;
        struct work_struct paprd_work;
-       struct work_struct hw_check_work;
        struct work_struct hw_reset_work;
        struct completion paprd_complete;
        wait_queue_head_t tx_wait;
 
-       unsigned int hw_busy_count;
        unsigned long sc_flags;
        unsigned long driver_data;
 
@@ -790,7 +757,6 @@ struct ath_softc {
        struct ath_beacon_config cur_beacon_conf;
        struct delayed_work tx_complete_work;
        struct delayed_work hw_pll_work;
-       struct timer_list rx_poll_timer;
        struct timer_list sleep_timer;
 
 #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
@@ -823,162 +789,6 @@ struct ath_softc {
 #endif
 };
 
-#define SPECTRAL_SCAN_BITMASK          0x10
-/* Radar info packet format, used for DFS and spectral formats. */
-struct ath_radar_info {
-       u8 pulse_length_pri;
-       u8 pulse_length_ext;
-       u8 pulse_bw_info;
-} __packed;
-
-/* The HT20 spectral data has 4 bytes of additional information at it's end.
- *
- * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]}
- * [7:0]: all bins  max_magnitude[9:2]
- * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]}
- * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
- */
-struct ath_ht20_mag_info {
-       u8 all_bins[3];
-       u8 max_exp;
-} __packed;
-
-#define SPECTRAL_HT20_NUM_BINS         56
-
-/* WARNING: don't actually use this struct! MAC may vary the amount of
- * data by -1/+2. This struct is for reference only.
- */
-struct ath_ht20_fft_packet {
-       u8 data[SPECTRAL_HT20_NUM_BINS];
-       struct ath_ht20_mag_info mag_info;
-       struct ath_radar_info radar_info;
-} __packed;
-
-#define SPECTRAL_HT20_TOTAL_DATA_LEN   (sizeof(struct ath_ht20_fft_packet))
-
-/* Dynamic 20/40 mode:
- *
- * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]}
- * [7:0]: lower bins  max_magnitude[9:2]
- * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]}
- * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]}
- * [7:0]: upper bins  max_magnitude[9:2]
- * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]}
- * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
- */
-struct ath_ht20_40_mag_info {
-       u8 lower_bins[3];
-       u8 upper_bins[3];
-       u8 max_exp;
-} __packed;
-
-#define SPECTRAL_HT20_40_NUM_BINS              128
-
-/* WARNING: don't actually use this struct! MAC may vary the amount of
- * data. This struct is for reference only.
- */
-struct ath_ht20_40_fft_packet {
-       u8 data[SPECTRAL_HT20_40_NUM_BINS];
-       struct ath_ht20_40_mag_info mag_info;
-       struct ath_radar_info radar_info;
-} __packed;
-
-
-#define SPECTRAL_HT20_40_TOTAL_DATA_LEN        (sizeof(struct ath_ht20_40_fft_packet))
-
-/* grabs the max magnitude from the all/upper/lower bins */
-static inline u16 spectral_max_magnitude(u8 *bins)
-{
-       return (bins[0] & 0xc0) >> 6 |
-              (bins[1] & 0xff) << 2 |
-              (bins[2] & 0x03) << 10;
-}
-
-/* return the max magnitude from the all/upper/lower bins */
-static inline u8 spectral_max_index(u8 *bins)
-{
-       s8 m = (bins[2] & 0xfc) >> 2;
-
-       /* TODO: this still doesn't always report the right values ... */
-       if (m > 32)
-               m |= 0xe0;
-       else
-               m &= ~0xe0;
-
-       return m + 29;
-}
-
-/* return the bitmap weight from the all/upper/lower bins */
-static inline u8 spectral_bitmap_weight(u8 *bins)
-{
-       return bins[0] & 0x3f;
-}
-
-/* FFT sample format given to userspace via debugfs.
- *
- * Please keep the type/length at the front position and change
- * other fields after adding another sample type
- *
- * TODO: this might need rework when switching to nl80211-based
- * interface.
- */
-enum ath_fft_sample_type {
-       ATH_FFT_SAMPLE_HT20 = 1,
-       ATH_FFT_SAMPLE_HT20_40,
-};
-
-struct fft_sample_tlv {
-       u8 type;        /* see ath_fft_sample */
-       __be16 length;
-       /* type dependent data follows */
-} __packed;
-
-struct fft_sample_ht20 {
-       struct fft_sample_tlv tlv;
-
-       u8 max_exp;
-
-       __be16 freq;
-       s8 rssi;
-       s8 noise;
-
-       __be16 max_magnitude;
-       u8 max_index;
-       u8 bitmap_weight;
-
-       __be64 tsf;
-
-       u8 data[SPECTRAL_HT20_NUM_BINS];
-} __packed;
-
-struct fft_sample_ht20_40 {
-       struct fft_sample_tlv tlv;
-
-       u8 channel_type;
-       __be16 freq;
-
-       s8 lower_rssi;
-       s8 upper_rssi;
-
-       __be64 tsf;
-
-       s8 lower_noise;
-       s8 upper_noise;
-
-       __be16 lower_max_magnitude;
-       __be16 upper_max_magnitude;
-
-       u8 lower_max_index;
-       u8 upper_max_index;
-
-       u8 lower_bitmap_weight;
-       u8 upper_bitmap_weight;
-
-       u8 max_exp;
-
-       u8 data[SPECTRAL_HT20_40_NUM_BINS];
-} __packed;
-
 /********/
 /* TX99 */
 /********/
@@ -999,19 +809,13 @@ static inline int ath9k_tx99_send(struct ath_softc *sc,
 }
 #endif /* CONFIG_ATH9K_TX99 */
 
-void ath9k_tasklet(unsigned long data);
-int ath_cabq_update(struct ath_softc *);
-
 static inline void ath_read_cachesize(struct ath_common *common, int *csz)
 {
        common->bus_ops->read_cachesize(common, csz);
 }
 
-extern struct ieee80211_ops ath9k_ops;
-extern int ath9k_modparam_nohwcrypt;
-extern int led_blink;
-extern bool is_ath9k_unloaded;
-
+void ath9k_tasklet(unsigned long data);
+int ath_cabq_update(struct ath_softc *);
 u8 ath9k_parse_mpdudensity(u8 mpdudensity);
 irqreturn_t ath_isr(int irq, void *dev);
 int ath_reset(struct ath_softc *sc);
@@ -1020,13 +824,12 @@ void ath_restart_work(struct ath_softc *sc);
 int ath9k_init_device(u16 devid, struct ath_softc *sc,
                    const struct ath_bus_ops *bus_ops);
 void ath9k_deinit_device(struct ath_softc *sc);
-void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
 void ath9k_reload_chainmask_settings(struct ath_softc *sc);
-
-void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
-int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
-                              enum spectral_mode spectral_mode);
-
+u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate);
+void ath_start_rfkill_poll(struct ath_softc *sc);
+void ath9k_rfkill_poll_state(struct ieee80211_hw *hw);
+void ath9k_ps_wakeup(struct ath_softc *sc);
+void ath9k_ps_restore(struct ath_softc *sc);
 
 #ifdef CONFIG_ATH9K_PCI
 int ath_pci_init(void);
@@ -1044,15 +847,4 @@ static inline int ath_ahb_init(void) { return 0; };
 static inline void ath_ahb_exit(void) {};
 #endif
 
-void ath9k_ps_wakeup(struct ath_softc *sc);
-void ath9k_ps_restore(struct ath_softc *sc);
-
-u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate);
-
-void ath_start_rfkill_poll(struct ath_softc *sc);
-void ath9k_rfkill_poll_state(struct ieee80211_hw *hw);
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
-                              struct ieee80211_vif *vif,
-                              struct ath9k_vif_iter_data *iter_data);
-
 #endif /* ATH9K_H */
index 17be353..2e8bba0 100644 (file)
@@ -274,18 +274,19 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
        return slot;
 }
 
-void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
+static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
        struct ath_vif *avp = (void *)vif->drv_priv;
-       u64 tsfadjust;
+       u32 tsfadjust;
 
        if (avp->av_bslot == 0)
                return;
 
-       tsfadjust = cur_conf->beacon_interval * avp->av_bslot / ATH_BCBUF;
-       avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust));
+       tsfadjust = cur_conf->beacon_interval * avp->av_bslot;
+       tsfadjust = TU_TO_USEC(tsfadjust) / ATH_BCBUF;
+       avp->tsf_adjust = cpu_to_le64(tsfadjust);
 
        ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n",
                (unsigned long long)tsfadjust, avp->av_bslot);
@@ -336,8 +337,14 @@ void ath9k_beacon_tasklet(unsigned long data)
 
                ath9k_hw_check_nav(ah);
 
-               if (!ath9k_hw_check_alive(ah))
-                       ieee80211_queue_work(sc->hw, &sc->hw_check_work);
+               /*
+                * If the previous beacon has not been transmitted
+                * and a MAC/BB hang has been identified, return
+                * here because a chip reset would have been
+                * initiated.
+                */
+               if (!ath_hw_check(sc))
+                       return;
 
                if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) {
                        ath_dbg(common, BSTUCK,
@@ -431,6 +438,33 @@ static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt,
        ath9k_hw_enable_interrupts(ah);
 }
 
+/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */
+static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu)
+{
+       u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo;
+
+       tsf_mod = tsf & (BIT(10) - 1);
+       tsf_hi = tsf >> 32;
+       tsf_lo = ((u32) tsf) >> 10;
+
+       mod_hi = tsf_hi % div_tu;
+       mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu;
+
+       return (mod_lo << 10) | tsf_mod;
+}
+
+static u32 ath9k_get_next_tbtt(struct ath_softc *sc, u64 tsf,
+                              unsigned int interval)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       unsigned int offset;
+
+       tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time);
+       offset = ath9k_mod_tsf64_tu(tsf, interval);
+
+       return (u32) tsf + TU_TO_USEC(interval) - offset;
+}
+
 /*
  * For multi-bss ap support beacons are either staggered evenly over N slots or
  * burst together.  For the former arrange for the SWBA to be delivered for each
@@ -446,7 +480,8 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc,
        /* NB: the beacon interval is kept internally in TU's */
        intval = TU_TO_USEC(conf->beacon_interval);
        intval /= ATH_BCBUF;
-       nexttbtt = intval;
+       nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah),
+                                      conf->beacon_interval);
 
        if (conf->enable_beacon)
                ah->imask |= ATH9K_INT_SWBA;
@@ -458,7 +493,7 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc,
                (conf->enable_beacon) ? "Enable" : "Disable",
                nexttbtt, intval, conf->beacon_interval);
 
-       ath9k_beacon_init(sc, nexttbtt, intval, true);
+       ath9k_beacon_init(sc, nexttbtt, intval, false);
 }
 
 /*
@@ -475,11 +510,9 @@ static void ath9k_beacon_config_sta(struct ath_softc *sc,
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        struct ath9k_beacon_state bs;
-       int dtimperiod, dtimcount, sleepduration;
-       int cfpperiod, cfpcount;
-       u32 nexttbtt = 0, intval, tsftu;
+       int dtim_intval, sleepduration;
+       u32 nexttbtt = 0, intval;
        u64 tsf;
-       int num_beacons, offset, dtim_dec_count, cfp_dec_count;
 
        /* No need to configure beacon if we are not associated */
        if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) {
@@ -492,53 +525,25 @@ static void ath9k_beacon_config_sta(struct ath_softc *sc,
        intval = conf->beacon_interval;
 
        /*
-        * Setup dtim and cfp parameters according to
+        * Setup dtim parameters according to
         * last beacon we received (which may be none).
         */
-       dtimperiod = conf->dtim_period;
-       dtimcount = conf->dtim_count;
-       if (dtimcount >= dtimperiod)    /* NB: sanity check */
-               dtimcount = 0;
-       cfpperiod = 1;                  /* NB: no PCF support yet */
-       cfpcount = 0;
-
+       dtim_intval = intval * conf->dtim_period;
        sleepduration = conf->listen_interval * intval;
 
        /*
         * Pull nexttbtt forward to reflect the current
-        * TSF and calculate dtim+cfp state for the result.
+        * TSF and calculate dtim state for the result.
         */
        tsf = ath9k_hw_gettsf64(ah);
-       tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
-
-       num_beacons = tsftu / intval + 1;
-       offset = tsftu % intval;
-       nexttbtt = tsftu - offset;
-       if (offset)
-               nexttbtt += intval;
-
-       /* DTIM Beacon every dtimperiod Beacon */
-       dtim_dec_count = num_beacons % dtimperiod;
-       /* CFP every cfpperiod DTIM Beacon */
-       cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
-       if (dtim_dec_count)
-               cfp_dec_count++;
-
-       dtimcount -= dtim_dec_count;
-       if (dtimcount < 0)
-               dtimcount += dtimperiod;
-
-       cfpcount -= cfp_dec_count;
-       if (cfpcount < 0)
-               cfpcount += cfpperiod;
-
-       bs.bs_intval = intval;
+       nexttbtt = ath9k_get_next_tbtt(sc, tsf, intval);
+
+       bs.bs_intval = TU_TO_USEC(intval);
+       bs.bs_dtimperiod = conf->dtim_period * bs.bs_intval;
        bs.bs_nexttbtt = nexttbtt;
-       bs.bs_dtimperiod = dtimperiod*intval;
-       bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
-       bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
-       bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
-       bs.bs_cfpmaxduration = 0;
+       bs.bs_nextdtim = nexttbtt;
+       if (conf->dtim_period > 1)
+               bs.bs_nextdtim = ath9k_get_next_tbtt(sc, tsf, dtim_intval);
 
        /*
         * Calculate the number of consecutive beacons to miss* before taking
@@ -566,18 +571,16 @@ static void ath9k_beacon_config_sta(struct ath_softc *sc,
         * XXX fixed at 100ms
         */
 
-       bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
+       bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
+                                                sleepduration));
        if (bs.bs_sleepduration > bs.bs_dtimperiod)
                bs.bs_sleepduration = bs.bs_dtimperiod;
 
        /* TSF out of range threshold fixed at 1 second */
        bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD;
 
-       ath_dbg(common, BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu);
-       ath_dbg(common, BEACON,
-               "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
-               bs.bs_bmissthreshold, bs.bs_sleepduration,
-               bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
+       ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n",
+               bs.bs_bmissthreshold, bs.bs_sleepduration);
 
        /* Set the computed STA beacon timers */
 
@@ -600,25 +603,11 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
 
        intval = TU_TO_USEC(conf->beacon_interval);
 
-       if (conf->ibss_creator) {
+       if (conf->ibss_creator)
                nexttbtt = intval;
-       } else {
-               u32 tbtt, offset, tsftu;
-               u64 tsf;
-
-               /*
-                * Pull nexttbtt forward to reflect the current
-                * sync'd TSF.
-                */
-               tsf = ath9k_hw_gettsf64(ah);
-               tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE;
-               offset = tsftu % conf->beacon_interval;
-               tbtt = tsftu - offset;
-               if (offset)
-                       tbtt += conf->beacon_interval;
-
-               nexttbtt = TU_TO_USEC(tbtt);
-       }
+       else
+               nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah),
+                                              conf->beacon_interval);
 
        if (conf->enable_beacon)
                ah->imask |= ATH9K_INT_SWBA;
@@ -640,7 +629,8 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc,
                set_bit(SC_OP_BEACONS, &sc->sc_flags);
 }
 
-bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif)
+static bool ath9k_allow_beacon_config(struct ath_softc *sc,
+                                     struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ath_vif *avp = (void *)vif->drv_priv;
@@ -711,12 +701,17 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
        unsigned long flags;
        bool skip_beacon = false;
 
+       if (vif->type == NL80211_IFTYPE_AP)
+               ath9k_set_tsfadjust(sc, vif);
+
+       if (!ath9k_allow_beacon_config(sc, vif))
+               return;
+
        if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
                ath9k_cache_beacon_config(sc, bss_conf);
                ath9k_set_beacon(sc);
                set_bit(SC_OP_BEACONS, &sc->sc_flags);
                return;
-
        }
 
        /*
index 9963b0b..3dfc2c7 100644 (file)
@@ -66,7 +66,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
                .bt_first_slot_time = 5,
                .bt_hold_rx_clear = true,
        };
-       u32 i, idx;
        bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity;
 
        if (AR_SREV_9300_20_OR_LATER(ah))
@@ -88,11 +87,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
                SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) |
                SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
                AR_BT_DISABLE_BT_ANT;
-
-       for (i = 0; i < 32; i++) {
-               idx = (debruijn32 << i) >> 27;
-               ah->hw_gen_timers.gen_timer_index[idx] = i;
-       }
 }
 EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw);
 
index a7e5a05..768c733 100644 (file)
@@ -98,10 +98,8 @@ struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
 {
        struct ieee80211_channel *curchan = chandef->chan;
        struct ath9k_channel *channel;
-       u8 chan_idx;
 
-       chan_idx = curchan->hw_value;
-       channel = &ah->channels[chan_idx];
+       channel = &ah->channels[curchan->hw_value];
        ath9k_cmn_update_ichannel(channel, chandef);
 
        return channel;
index 2f7dccf..b041052 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/export.h>
-#include <linux/relay.h>
 #include <asm/unaligned.h>
 
 #include "ath9k.h"
 #define REG_READ_D(_ah, _reg) \
        ath9k_hw_common(_ah)->ops->read((_ah), (_reg))
 
+void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause)
+{
+       if (sync_cause)
+               sc->debug.stats.istats.sync_cause_all++;
+       if (sync_cause & AR_INTR_SYNC_RTC_IRQ)
+               sc->debug.stats.istats.sync_rtc_irq++;
+       if (sync_cause & AR_INTR_SYNC_MAC_IRQ)
+               sc->debug.stats.istats.sync_mac_irq++;
+       if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS)
+               sc->debug.stats.istats.eeprom_illegal_access++;
+       if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT)
+               sc->debug.stats.istats.apb_timeout++;
+       if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT)
+               sc->debug.stats.istats.pci_mode_conflict++;
+       if (sync_cause & AR_INTR_SYNC_HOST1_FATAL)
+               sc->debug.stats.istats.host1_fatal++;
+       if (sync_cause & AR_INTR_SYNC_HOST1_PERR)
+               sc->debug.stats.istats.host1_perr++;
+       if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR)
+               sc->debug.stats.istats.trcv_fifo_perr++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP)
+               sc->debug.stats.istats.radm_cpl_ep++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT)
+               sc->debug.stats.istats.radm_cpl_dllp_abort++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT)
+               sc->debug.stats.istats.radm_cpl_tlp_abort++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR)
+               sc->debug.stats.istats.radm_cpl_ecrc_err++;
+       if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT)
+               sc->debug.stats.istats.radm_cpl_timeout++;
+       if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT)
+               sc->debug.stats.istats.local_timeout++;
+       if (sync_cause & AR_INTR_SYNC_PM_ACCESS)
+               sc->debug.stats.istats.pm_access++;
+       if (sync_cause & AR_INTR_SYNC_MAC_AWAKE)
+               sc->debug.stats.istats.mac_awake++;
+       if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP)
+               sc->debug.stats.istats.mac_asleep++;
+       if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS)
+               sc->debug.stats.istats.mac_sleep_access++;
+}
 
 static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf,
                                      size_t count, loff_t *ppos)
@@ -1016,293 +1056,6 @@ static const struct file_operations fops_recv = {
        .llseek = default_llseek,
 };
 
-static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
-                                      size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       char *mode = "";
-       unsigned int len;
-
-       switch (sc->spectral_mode) {
-       case SPECTRAL_DISABLED:
-               mode = "disable";
-               break;
-       case SPECTRAL_BACKGROUND:
-               mode = "background";
-               break;
-       case SPECTRAL_CHANSCAN:
-               mode = "chanscan";
-               break;
-       case SPECTRAL_MANUAL:
-               mode = "manual";
-               break;
-       }
-       len = strlen(mode);
-       return simple_read_from_buffer(user_buf, count, ppos, mode, len);
-}
-
-static ssize_t write_file_spec_scan_ctl(struct file *file,
-                                       const char __user *user_buf,
-                                       size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       char buf[32];
-       ssize_t len;
-
-       if (config_enabled(CONFIG_ATH9K_TX99))
-               return -EOPNOTSUPP;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-
-       if (strncmp("trigger", buf, 7) == 0) {
-               ath9k_spectral_scan_trigger(sc->hw);
-       } else if (strncmp("background", buf, 9) == 0) {
-               ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
-               ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
-       } else if (strncmp("chanscan", buf, 8) == 0) {
-               ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
-               ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
-       } else if (strncmp("manual", buf, 6) == 0) {
-               ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
-               ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
-       } else if (strncmp("disable", buf, 7) == 0) {
-               ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
-               ath_dbg(common, CONFIG, "spectral scan: disabled\n");
-       } else {
-               return -EINVAL;
-       }
-
-       return count;
-}
-
-static const struct file_operations fops_spec_scan_ctl = {
-       .read = read_file_spec_scan_ctl,
-       .write = write_file_spec_scan_ctl,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_spectral_short_repeat(struct file *file,
-                                              char __user *user_buf,
-                                              size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       char buf[32];
-       unsigned int len;
-
-       len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_spectral_short_repeat(struct file *file,
-                                               const char __user *user_buf,
-                                               size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       unsigned long val;
-       char buf[32];
-       ssize_t len;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-       if (kstrtoul(buf, 0, &val))
-               return -EINVAL;
-
-       if (val < 0 || val > 1)
-               return -EINVAL;
-
-       sc->spec_config.short_repeat = val;
-       return count;
-}
-
-static const struct file_operations fops_spectral_short_repeat = {
-       .read = read_file_spectral_short_repeat,
-       .write = write_file_spectral_short_repeat,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_spectral_count(struct file *file,
-                                       char __user *user_buf,
-                                       size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       char buf[32];
-       unsigned int len;
-
-       len = sprintf(buf, "%d\n", sc->spec_config.count);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_spectral_count(struct file *file,
-                                        const char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       unsigned long val;
-       char buf[32];
-       ssize_t len;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-       if (kstrtoul(buf, 0, &val))
-               return -EINVAL;
-
-       if (val < 0 || val > 255)
-               return -EINVAL;
-
-       sc->spec_config.count = val;
-       return count;
-}
-
-static const struct file_operations fops_spectral_count = {
-       .read = read_file_spectral_count,
-       .write = write_file_spectral_count,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_spectral_period(struct file *file,
-                                        char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       char buf[32];
-       unsigned int len;
-
-       len = sprintf(buf, "%d\n", sc->spec_config.period);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_spectral_period(struct file *file,
-                                         const char __user *user_buf,
-                                         size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       unsigned long val;
-       char buf[32];
-       ssize_t len;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-       if (kstrtoul(buf, 0, &val))
-               return -EINVAL;
-
-       if (val < 0 || val > 255)
-               return -EINVAL;
-
-       sc->spec_config.period = val;
-       return count;
-}
-
-static const struct file_operations fops_spectral_period = {
-       .read = read_file_spectral_period,
-       .write = write_file_spectral_period,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static ssize_t read_file_spectral_fft_period(struct file *file,
-                                            char __user *user_buf,
-                                            size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       char buf[32];
-       unsigned int len;
-
-       len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t write_file_spectral_fft_period(struct file *file,
-                                             const char __user *user_buf,
-                                             size_t count, loff_t *ppos)
-{
-       struct ath_softc *sc = file->private_data;
-       unsigned long val;
-       char buf[32];
-       ssize_t len;
-
-       len = min(count, sizeof(buf) - 1);
-       if (copy_from_user(buf, user_buf, len))
-               return -EFAULT;
-
-       buf[len] = '\0';
-       if (kstrtoul(buf, 0, &val))
-               return -EINVAL;
-
-       if (val < 0 || val > 15)
-               return -EINVAL;
-
-       sc->spec_config.fft_period = val;
-       return count;
-}
-
-static const struct file_operations fops_spectral_fft_period = {
-       .read = read_file_spectral_fft_period,
-       .write = write_file_spectral_fft_period,
-       .open = simple_open,
-       .owner = THIS_MODULE,
-       .llseek = default_llseek,
-};
-
-static struct dentry *create_buf_file_handler(const char *filename,
-                                             struct dentry *parent,
-                                             umode_t mode,
-                                             struct rchan_buf *buf,
-                                             int *is_global)
-{
-       struct dentry *buf_file;
-
-       buf_file = debugfs_create_file(filename, mode, parent, buf,
-                                      &relay_file_operations);
-       *is_global = 1;
-       return buf_file;
-}
-
-static int remove_buf_file_handler(struct dentry *dentry)
-{
-       debugfs_remove(dentry);
-
-       return 0;
-}
-
-void ath_debug_send_fft_sample(struct ath_softc *sc,
-                              struct fft_sample_tlv *fft_sample_tlv)
-{
-       int length;
-       if (!sc->rfs_chan_spec_scan)
-               return;
-
-       length = __be16_to_cpu(fft_sample_tlv->length) +
-                sizeof(*fft_sample_tlv);
-       relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
-}
-
-static struct rchan_callbacks rfs_spec_scan_cb = {
-       .create_buf_file = create_buf_file_handler,
-       .remove_buf_file = remove_buf_file_handler,
-};
-
-
 static ssize_t read_file_regidx(struct file *file, char __user *user_buf,
                                 size_t count, loff_t *ppos)
 {
@@ -1772,10 +1525,7 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw,
 
 void ath9k_deinit_debug(struct ath_softc *sc)
 {
-       if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
-               relay_close(sc->rfs_chan_spec_scan);
-               sc->rfs_chan_spec_scan = NULL;
-       }
+       ath9k_spectral_deinit_debug(sc);
 }
 
 int ath9k_init_debug(struct ath_hw *ah)
@@ -1795,6 +1545,7 @@ int ath9k_init_debug(struct ath_hw *ah)
 
        ath9k_dfs_init_debug(sc);
        ath9k_tx99_init_debug(sc);
+       ath9k_spectral_init_debug(sc);
 
        debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_dma);
@@ -1841,23 +1592,6 @@ int ath9k_init_debug(struct ath_hw *ah)
                            &fops_base_eeprom);
        debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
                            &fops_modal_eeprom);
-       sc->rfs_chan_spec_scan = relay_open("spectral_scan",
-                                           sc->debug.debugfs_phy,
-                                           1024, 256, &rfs_spec_scan_cb,
-                                           NULL);
-       debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc,
-                           &fops_spec_scan_ctl);
-       debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc,
-                           &fops_spectral_short_repeat);
-       debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_spectral_count);
-       debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc, &fops_spectral_period);
-       debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
-                           sc->debug.debugfs_phy, sc,
-                           &fops_spectral_fft_period);
        debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
                           sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
        debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
index d6e3fa4..ec02d38 100644 (file)
@@ -292,11 +292,11 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
                           struct ieee80211_vif *vif,
                           struct ieee80211_sta *sta,
                           struct dentry *dir);
-void ath_debug_send_fft_sample(struct ath_softc *sc,
-                              struct fft_sample_tlv *fft_sample);
 void ath9k_debug_stat_ant(struct ath_softc *sc,
                          struct ath_hw_antcomb_conf *div_ant_conf,
                          int main_rssi_avg, int alt_rssi_avg);
+void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause);
+
 #else
 
 #define RX_STAT_INC(c) /* NOP */
@@ -331,6 +331,11 @@ static inline void ath9k_debug_stat_ant(struct ath_softc *sc,
 
 }
 
+static inline void
+ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause)
+{
+}
+
 #endif /* CONFIG_ATH9K_DEBUGFS */
 
 #endif /* DEBUG_H */
index 7187d36..857bb28 100644 (file)
@@ -158,8 +158,8 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
                return;
        }
 
-       ard.rssi = rs->rs_rssi_ctl0;
-       ard.ext_rssi = rs->rs_rssi_ext0;
+       ard.rssi = rs->rs_rssi_ctl[0];
+       ard.ext_rssi = rs->rs_rssi_ext[0];
 
        /*
         * hardware stores this as 8 bit signed value.
index b409171..07b806c 100644 (file)
@@ -1085,31 +1085,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
 
 static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz)
 {
-#define EEP_MAP4K_SPURCHAN \
-       (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan)
-       struct ath_common *common = ath9k_hw_common(ah);
-
-       u16 spur_val = AR_NO_SPUR;
-
-       ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n",
-               i, is2GHz, ah->config.spurchans[i][is2GHz]);
-
-       switch (ah->config.spurmode) {
-       case SPUR_DISABLE:
-               break;
-       case SPUR_ENABLE_IOCTL:
-               spur_val = ah->config.spurchans[i][is2GHz];
-               ath_dbg(common, ANI, "Getting spur val from new loc. %d\n",
-                       spur_val);
-               break;
-       case SPUR_ENABLE_EEPROM:
-               spur_val = EEP_MAP4K_SPURCHAN;
-               break;
-       }
-
-       return spur_val;
-
-#undef EEP_MAP4K_SPURCHAN
+       return ah->eeprom.map4k.modalHeader.spurChans[i].spurChan;
 }
 
 const struct eeprom_ops eep_4k_ops = {
index e1d0c21..5ba1385 100644 (file)
@@ -1004,31 +1004,7 @@ static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah,
 static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah,
                                            u16 i, bool is2GHz)
 {
-#define EEP_MAP9287_SPURCHAN \
-       (ah->eeprom.map9287.modalHeader.spurChans[i].spurChan)
-
-       struct ath_common *common = ath9k_hw_common(ah);
-       u16 spur_val = AR_NO_SPUR;
-
-       ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n",
-               i, is2GHz, ah->config.spurchans[i][is2GHz]);
-
-       switch (ah->config.spurmode) {
-       case SPUR_DISABLE:
-               break;
-       case SPUR_ENABLE_IOCTL:
-               spur_val = ah->config.spurchans[i][is2GHz];
-               ath_dbg(common, ANI, "Getting spur val from new loc. %d\n",
-                       spur_val);
-               break;
-       case SPUR_ENABLE_EEPROM:
-               spur_val = EEP_MAP9287_SPURCHAN;
-               break;
-       }
-
-       return spur_val;
-
-#undef EEP_MAP9287_SPURCHAN
+       return ah->eeprom.map9287.modalHeader.spurChans[i].spurChan;
 }
 
 const struct eeprom_ops eep_ar9287_ops = {
index 39107e3..3218ca9 100644 (file)
@@ -1348,31 +1348,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
 
 static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz)
 {
-#define EEP_DEF_SPURCHAN \
-       (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan)
-       struct ath_common *common = ath9k_hw_common(ah);
-
-       u16 spur_val = AR_NO_SPUR;
-
-       ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n",
-               i, is2GHz, ah->config.spurchans[i][is2GHz]);
-
-       switch (ah->config.spurmode) {
-       case SPUR_DISABLE:
-               break;
-       case SPUR_ENABLE_IOCTL:
-               spur_val = ah->config.spurchans[i][is2GHz];
-               ath_dbg(common, ANI, "Getting spur val from new loc. %d\n",
-                       spur_val);
-               break;
-       case SPUR_ENABLE_EEPROM:
-               spur_val = EEP_DEF_SPURCHAN;
-               break;
-       }
-
-       return spur_val;
-
-#undef EEP_DEF_SPURCHAN
+       return ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan;
 }
 
 const struct eeprom_ops eep_def_ops = {
index c34f212..b1956bf 100644 (file)
@@ -157,36 +157,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc)
        }
 }
 
-static void ath9k_gen_timer_start(struct ath_hw *ah,
-                                 struct ath_gen_timer *timer,
-                                 u32 trig_timeout,
-                                 u32 timer_period)
-{
-       ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period);
-
-       if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
-               ath9k_hw_disable_interrupts(ah);
-               ah->imask |= ATH9K_INT_GENTIMER;
-               ath9k_hw_set_interrupts(ah);
-               ath9k_hw_enable_interrupts(ah);
-       }
-}
-
-static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
-{
-       struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
-
-       ath9k_hw_gen_timer_stop(ah, timer);
-
-       /* if no timer is enabled, turn off interrupt mask */
-       if (timer_table->timer_mask.val == 0) {
-               ath9k_hw_disable_interrupts(ah);
-               ah->imask &= ~ATH9K_INT_GENTIMER;
-               ath9k_hw_set_interrupts(ah);
-               ath9k_hw_enable_interrupts(ah);
-       }
-}
-
 static void ath_mci_ftp_adjust(struct ath_softc *sc)
 {
        struct ath_btcoex *btcoex = &sc->btcoex;
@@ -257,19 +227,9 @@ static void ath_btcoex_period_timer(unsigned long data)
 
        spin_unlock_bh(&btcoex->btcoex_lock);
 
-       /*
-        * btcoex_period is in msec while (btocex/btscan_)no_stomp are in usec,
-        * ensure that we properly convert btcoex_period to usec
-        * for any comparision with (btcoex/btscan_)no_stomp.
-        */
-       if (btcoex->btcoex_period * 1000 != btcoex->btcoex_no_stomp) {
-               if (btcoex->hw_timer_enabled)
-                       ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
-
-               ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period,
-                                     timer_period * 10);
-               btcoex->hw_timer_enabled = true;
-       }
+       if (btcoex->btcoex_period != btcoex->btcoex_no_stomp)
+               mod_timer(&btcoex->no_stomp_timer,
+                        jiffies + msecs_to_jiffies(timer_period));
 
        ath9k_ps_restore(sc);
 
@@ -282,7 +242,7 @@ skip_hw_wakeup:
  * Generic tsf based hw timer which configures weight
  * registers to time slice between wlan and bt traffic
  */
-static void ath_btcoex_no_stomp_timer(void *arg)
+static void ath_btcoex_no_stomp_timer(unsigned long arg)
 {
        struct ath_softc *sc = (struct ath_softc *)arg;
        struct ath_hw *ah = sc->sc_ah;
@@ -311,24 +271,18 @@ static int ath_init_btcoex_timer(struct ath_softc *sc)
        struct ath_btcoex *btcoex = &sc->btcoex;
 
        btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
-       btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 1000 *
+       btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
                btcoex->btcoex_period / 100;
-       btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * 1000 *
+       btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
                                   btcoex->btcoex_period / 100;
 
        setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
                        (unsigned long) sc);
+       setup_timer(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer,
+                       (unsigned long) sc);
 
        spin_lock_init(&btcoex->btcoex_lock);
 
-       btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
-                       ath_btcoex_no_stomp_timer,
-                       ath_btcoex_no_stomp_timer,
-                       (void *) sc, AR_FIRST_NDP_TIMER);
-
-       if (!btcoex->no_stomp_timer)
-               return -ENOMEM;
-
        return 0;
 }
 
@@ -343,10 +297,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
        ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
 
        /* make sure duty cycle timer is also stopped when resuming */
-       if (btcoex->hw_timer_enabled) {
-               ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
-               btcoex->hw_timer_enabled = false;
-       }
+       del_timer_sync(&btcoex->no_stomp_timer);
 
        btcoex->bt_priority_cnt = 0;
        btcoex->bt_priority_time = jiffies;
@@ -363,24 +314,16 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
 void ath9k_btcoex_timer_pause(struct ath_softc *sc)
 {
        struct ath_btcoex *btcoex = &sc->btcoex;
-       struct ath_hw *ah = sc->sc_ah;
 
        del_timer_sync(&btcoex->period_timer);
-
-       if (btcoex->hw_timer_enabled) {
-               ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
-               btcoex->hw_timer_enabled = false;
-       }
+       del_timer_sync(&btcoex->no_stomp_timer);
 }
 
 void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
 {
        struct ath_btcoex *btcoex = &sc->btcoex;
 
-       if (btcoex->hw_timer_enabled) {
-               ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
-               btcoex->hw_timer_enabled = false;
-       }
+       del_timer_sync(&btcoex->no_stomp_timer);
 }
 
 u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
@@ -400,12 +343,6 @@ u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
 
 void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status)
 {
-       struct ath_hw *ah = sc->sc_ah;
-
-       if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE)
-               if (status & ATH9K_INT_GENTIMER)
-                       ath_gen_timer_isr(sc->sc_ah);
-
        if (status & ATH9K_INT_MCI)
                ath_mci_intr(sc);
 }
@@ -447,10 +384,6 @@ void ath9k_deinit_btcoex(struct ath_softc *sc)
 {
        struct ath_hw *ah = sc->sc_ah;
 
-        if ((sc->btcoex.no_stomp_timer) &&
-           ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE)
-               ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer);
-
        if (ath9k_hw_mci_is_enabled(ah))
                ath_mci_cleanup(sc);
 }
index 055d7c2..58da346 100644 (file)
@@ -600,10 +600,15 @@ void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw);
 struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv);
 
 #ifdef CONFIG_MAC80211_LEDS
+void ath9k_configure_leds(struct ath9k_htc_priv *priv);
 void ath9k_init_leds(struct ath9k_htc_priv *priv);
 void ath9k_deinit_leds(struct ath9k_htc_priv *priv);
 void ath9k_led_work(struct work_struct *work);
 #else
+static inline void ath9k_configure_leds(struct ath9k_htc_priv *priv)
+{
+}
+
 static inline void ath9k_init_leds(struct ath9k_htc_priv *priv)
 {
 }
index e0c03bd..8b57577 100644 (file)
@@ -70,11 +70,11 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
        struct ath9k_beacon_state bs;
        enum ath9k_int imask = 0;
        int dtimperiod, dtimcount, sleepduration;
-       int cfpperiod, cfpcount, bmiss_timeout;
+       int bmiss_timeout;
        u32 nexttbtt = 0, intval, tsftu;
        __be32 htc_imask = 0;
        u64 tsf;
-       int num_beacons, offset, dtim_dec_count, cfp_dec_count;
+       int num_beacons, offset, dtim_dec_count;
        int ret __attribute__ ((unused));
        u8 cmd_rsp;
 
@@ -84,7 +84,7 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
        bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval);
 
        /*
-        * Setup dtim and cfp parameters according to
+        * Setup dtim parameters according to
         * last beacon we received (which may be none).
         */
        dtimperiod = bss_conf->dtim_period;
@@ -93,8 +93,6 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
        dtimcount = 1;
        if (dtimcount >= dtimperiod)    /* NB: sanity check */
                dtimcount = 0;
-       cfpperiod = 1;                  /* NB: no PCF support yet */
-       cfpcount = 0;
 
        sleepduration = intval;
        if (sleepduration <= 0)
@@ -102,7 +100,7 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
 
        /*
         * Pull nexttbtt forward to reflect the current
-        * TSF and calculate dtim+cfp state for the result.
+        * TSF and calculate dtim state for the result.
         */
        tsf = ath9k_hw_gettsf64(priv->ah);
        tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE;
@@ -115,26 +113,14 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
 
        /* DTIM Beacon every dtimperiod Beacon */
        dtim_dec_count = num_beacons % dtimperiod;
-       /* CFP every cfpperiod DTIM Beacon */
-       cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod;
-       if (dtim_dec_count)
-               cfp_dec_count++;
-
        dtimcount -= dtim_dec_count;
        if (dtimcount < 0)
                dtimcount += dtimperiod;
 
-       cfpcount -= cfp_dec_count;
-       if (cfpcount < 0)
-               cfpcount += cfpperiod;
-
-       bs.bs_intval = intval;
-       bs.bs_nexttbtt = nexttbtt;
-       bs.bs_dtimperiod = dtimperiod*intval;
-       bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval;
-       bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod;
-       bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod;
-       bs.bs_cfpmaxduration = 0;
+       bs.bs_intval = TU_TO_USEC(intval);
+       bs.bs_nexttbtt = TU_TO_USEC(nexttbtt);
+       bs.bs_dtimperiod = dtimperiod * bs.bs_intval;
+       bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount * bs.bs_intval;
 
        /*
         * Calculate the number of consecutive beacons to miss* before taking
@@ -161,7 +147,8 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
         * XXX fixed at 100ms
         */
 
-       bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration);
+       bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
+                                                sleepduration));
        if (bs.bs_sleepduration > bs.bs_dtimperiod)
                bs.bs_sleepduration = bs.bs_dtimperiod;
 
@@ -170,10 +157,8 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv,
 
        ath_dbg(common, CONFIG, "intval: %u tsf: %llu tsftu: %u\n",
                intval, tsf, tsftu);
-       ath_dbg(common, CONFIG,
-               "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n",
-               bs.bs_bmissthreshold, bs.bs_sleepduration,
-               bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext);
+       ath_dbg(common, CONFIG, "bmiss: %u sleep: %u\n",
+               bs.bs_bmissthreshold, bs.bs_sleepduration);
 
        /* Set the computed STA beacon timers */
 
index 105582d..50f74a2 100644 (file)
@@ -255,6 +255,17 @@ void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
        cancel_work_sync(&priv->led_work);
 }
 
+
+void ath9k_configure_leds(struct ath9k_htc_priv *priv)
+{
+       /* Configure gpio 1 for output */
+       ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
+                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
+       /* LED off, active low */
+       ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
+
+}
+
 void ath9k_init_leds(struct ath9k_htc_priv *priv)
 {
        int ret;
@@ -268,11 +279,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv)
        else
                priv->ah->led_pin = ATH_LED_PIN_DEF;
 
-       /* Configure gpio 1 for output */
-       ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
-                           AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
-       /* LED off, active low */
-       ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
+       ath9k_configure_leds(priv);
 
        snprintf(priv->led_name, sizeof(priv->led_name),
                "ath9k_htc-%s", wiphy_name(priv->hw->wiphy));
index bcdb50a..f4e1de2 100644 (file)
@@ -999,6 +999,8 @@ int ath9k_htc_resume(struct htc_target *htc_handle)
 
        ret = ath9k_init_htc_services(priv, priv->ah->hw_version.devid,
                                      priv->ah->hw_version.usbdev);
+       ath9k_configure_leds(priv);
+
        return ret;
 }
 #endif
index 9a2657f..608d739 100644 (file)
@@ -127,21 +127,26 @@ static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
        struct ath9k_vif_iter_data *iter_data = data;
        int i;
 
-       for (i = 0; i < ETH_ALEN; i++)
-               iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
+       if (iter_data->hw_macaddr != NULL) {
+               for (i = 0; i < ETH_ALEN; i++)
+                       iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
+       } else {
+               iter_data->hw_macaddr = mac;
+       }
 }
 
-static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
+static void ath9k_htc_set_mac_bssid_mask(struct ath9k_htc_priv *priv,
                                     struct ieee80211_vif *vif)
 {
        struct ath_common *common = ath9k_hw_common(priv->ah);
        struct ath9k_vif_iter_data iter_data;
 
        /*
-        * Use the hardware MAC address as reference, the hardware uses it
-        * together with the BSSID mask when matching addresses.
+        * Pick the MAC address of the first interface as the new hardware
+        * MAC address. The hardware will use it together with the BSSID mask
+        * when matching addresses.
         */
-       iter_data.hw_macaddr = common->macaddr;
+       iter_data.hw_macaddr = NULL;
        memset(&iter_data.mask, 0xff, ETH_ALEN);
 
        if (vif)
@@ -153,6 +158,10 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
                ath9k_htc_bssid_iter, &iter_data);
 
        memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
+
+       if (iter_data.hw_macaddr)
+               memcpy(common->macaddr, iter_data.hw_macaddr, ETH_ALEN);
+
        ath_hw_setbssidmask(common);
 }
 
@@ -1063,7 +1072,7 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
                goto out;
        }
 
-       ath9k_htc_set_bssid_mask(priv, vif);
+       ath9k_htc_set_mac_bssid_mask(priv, vif);
 
        priv->vif_slot |= (1 << avp->index);
        priv->nvifs++;
@@ -1128,7 +1137,7 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
 
        ath9k_htc_set_opmode(priv);
 
-       ath9k_htc_set_bssid_mask(priv, vif);
+       ath9k_htc_set_mac_bssid_mask(priv, vif);
 
        /*
         * Stop ANI only if there are no associated station interfaces.
index c028df7..b41e008 100644 (file)
@@ -1077,7 +1077,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv,
 
        if (ieee80211_is_beacon(hdr->frame_control) &&
            !is_zero_ether_addr(common->curbssid) &&
-           ether_addr_equal(hdr->addr3, common->curbssid)) {
+           ether_addr_equal_64bits(hdr->addr3, common->curbssid)) {
                s8 rssi = rxbuf->rxstatus.rs_rssi;
 
                if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER))
index 4f9378d..a47ea84 100644 (file)
@@ -49,9 +49,10 @@ static inline bool ath9k_hw_calibrate(struct ath_hw *ah,
        return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal);
 }
 
-static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
+static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked,
+                                  u32 *sync_cause_p)
 {
-       return ath9k_hw_ops(ah)->get_isr(ah, masked);
+       return ath9k_hw_ops(ah)->get_isr(ah, masked, sync_cause_p);
 }
 
 static inline void ath9k_hw_set_txdesc(struct ath_hw *ah, void *ds,
@@ -106,6 +107,21 @@ static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable)
 
 /* Private hardware call ops */
 
+static inline void ath9k_hw_init_hang_checks(struct ath_hw *ah)
+{
+       ath9k_hw_private_ops(ah)->init_hang_checks(ah);
+}
+
+static inline bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
+{
+       return ath9k_hw_private_ops(ah)->detect_mac_hang(ah);
+}
+
+static inline bool ath9k_hw_detect_bb_hang(struct ath_hw *ah)
+{
+       return ath9k_hw_private_ops(ah)->detect_bb_hang(ah);
+}
+
 /* PHY ops */
 
 static inline int ath9k_hw_rf_set_freq(struct ath_hw *ah,
@@ -231,4 +247,31 @@ static inline void ath9k_hw_set_radar_params(struct ath_hw *ah)
        ath9k_hw_private_ops(ah)->set_radar_params(ah, &ah->radar_conf);
 }
 
+static inline void ath9k_hw_init_cal_settings(struct ath_hw *ah)
+{
+       ath9k_hw_private_ops(ah)->init_cal_settings(ah);
+}
+
+static inline u32 ath9k_hw_compute_pll_control(struct ath_hw *ah,
+                                              struct ath9k_channel *chan)
+{
+       return ath9k_hw_private_ops(ah)->compute_pll_control(ah, chan);
+}
+
+static inline void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah)
+{
+       if (!ath9k_hw_private_ops(ah)->init_mode_gain_regs)
+               return;
+
+       ath9k_hw_private_ops(ah)->init_mode_gain_regs(ah);
+}
+
+static inline void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah)
+{
+       if (!ath9k_hw_private_ops(ah)->ani_cache_ini_regs)
+               return;
+
+       ath9k_hw_private_ops(ah)->ani_cache_ini_regs(ah);
+}
+
 #endif /* ATH9K_HW_OPS_H */
index 4ee24b1..ce41658 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/time.h>
+#include <linux/bitops.h>
 #include <asm/unaligned.h>
 
 #include "hw.h"
@@ -36,99 +37,6 @@ MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards.");
 MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards");
 MODULE_LICENSE("Dual BSD/GPL");
 
-static int __init ath9k_init(void)
-{
-       return 0;
-}
-module_init(ath9k_init);
-
-static void __exit ath9k_exit(void)
-{
-       return;
-}
-module_exit(ath9k_exit);
-
-/* Private hardware callbacks */
-
-static void ath9k_hw_init_cal_settings(struct ath_hw *ah)
-{
-       ath9k_hw_private_ops(ah)->init_cal_settings(ah);
-}
-
-static u32 ath9k_hw_compute_pll_control(struct ath_hw *ah,
-                                       struct ath9k_channel *chan)
-{
-       return ath9k_hw_private_ops(ah)->compute_pll_control(ah, chan);
-}
-
-static void ath9k_hw_init_mode_gain_regs(struct ath_hw *ah)
-{
-       if (!ath9k_hw_private_ops(ah)->init_mode_gain_regs)
-               return;
-
-       ath9k_hw_private_ops(ah)->init_mode_gain_regs(ah);
-}
-
-static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah)
-{
-       /* You will not have this callback if using the old ANI */
-       if (!ath9k_hw_private_ops(ah)->ani_cache_ini_regs)
-               return;
-
-       ath9k_hw_private_ops(ah)->ani_cache_ini_regs(ah);
-}
-
-/********************/
-/* Helper Functions */
-/********************/
-
-#ifdef CONFIG_ATH9K_DEBUGFS
-
-void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause)
-{
-       struct ath_softc *sc = common->priv;
-       if (sync_cause)
-               sc->debug.stats.istats.sync_cause_all++;
-       if (sync_cause & AR_INTR_SYNC_RTC_IRQ)
-               sc->debug.stats.istats.sync_rtc_irq++;
-       if (sync_cause & AR_INTR_SYNC_MAC_IRQ)
-               sc->debug.stats.istats.sync_mac_irq++;
-       if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS)
-               sc->debug.stats.istats.eeprom_illegal_access++;
-       if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT)
-               sc->debug.stats.istats.apb_timeout++;
-       if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT)
-               sc->debug.stats.istats.pci_mode_conflict++;
-       if (sync_cause & AR_INTR_SYNC_HOST1_FATAL)
-               sc->debug.stats.istats.host1_fatal++;
-       if (sync_cause & AR_INTR_SYNC_HOST1_PERR)
-               sc->debug.stats.istats.host1_perr++;
-       if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR)
-               sc->debug.stats.istats.trcv_fifo_perr++;
-       if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP)
-               sc->debug.stats.istats.radm_cpl_ep++;
-       if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT)
-               sc->debug.stats.istats.radm_cpl_dllp_abort++;
-       if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT)
-               sc->debug.stats.istats.radm_cpl_tlp_abort++;
-       if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR)
-               sc->debug.stats.istats.radm_cpl_ecrc_err++;
-       if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT)
-               sc->debug.stats.istats.radm_cpl_timeout++;
-       if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT)
-               sc->debug.stats.istats.local_timeout++;
-       if (sync_cause & AR_INTR_SYNC_PM_ACCESS)
-               sc->debug.stats.istats.pm_access++;
-       if (sync_cause & AR_INTR_SYNC_MAC_AWAKE)
-               sc->debug.stats.istats.mac_awake++;
-       if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP)
-               sc->debug.stats.istats.mac_asleep++;
-       if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS)
-               sc->debug.stats.istats.mac_sleep_access++;
-}
-#endif
-
-
 static void ath9k_hw_set_clockrate(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -337,6 +245,9 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah)
        case AR9300_DEVID_QCA955X:
                ah->hw_version.macVersion = AR_SREV_VERSION_9550;
                return;
+       case AR9300_DEVID_AR953X:
+               ah->hw_version.macVersion = AR_SREV_VERSION_9531;
+               return;
        }
 
        val = REG_READ(ah, AR_SREV) & AR_SREV_ID;
@@ -438,21 +349,13 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah)
 
 static void ath9k_hw_init_config(struct ath_hw *ah)
 {
-       int i;
+       struct ath_common *common = ath9k_hw_common(ah);
 
        ah->config.dma_beacon_response_time = 1;
        ah->config.sw_beacon_response_time = 6;
-       ah->config.additional_swba_backoff = 0;
-       ah->config.ack_6mb = 0x0;
        ah->config.cwm_ignore_extcca = 0;
-       ah->config.pcie_clock_req = 0;
        ah->config.analog_shiftreg = 1;
 
-       for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
-               ah->config.spurchans[i][0] = AR_NO_SPUR;
-               ah->config.spurchans[i][1] = AR_NO_SPUR;
-       }
-
        ah->config.rx_intr_mitigation = true;
 
        /*
@@ -473,6 +376,24 @@ static void ath9k_hw_init_config(struct ath_hw *ah)
         */
        if (num_possible_cpus() > 1)
                ah->config.serialize_regmode = SER_REG_MODE_AUTO;
+
+       if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) {
+               if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI ||
+                   ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) &&
+                    !ah->is_pciexpress)) {
+                       ah->config.serialize_regmode = SER_REG_MODE_ON;
+               } else {
+                       ah->config.serialize_regmode = SER_REG_MODE_OFF;
+               }
+       }
+
+       ath_dbg(common, RESET, "serialize_regmode is %d\n",
+               ah->config.serialize_regmode);
+
+       if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+               ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1;
+       else
+               ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD;
 }
 
 static void ath9k_hw_init_defaults(struct ath_hw *ah)
@@ -485,16 +406,24 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah)
        ah->hw_version.magic = AR5416_MAGIC;
        ah->hw_version.subvendorid = 0;
 
-       ah->atim_window = 0;
-       ah->sta_id1_defaults =
-               AR_STA_ID1_CRPT_MIC_ENABLE |
-               AR_STA_ID1_MCAST_KSRCH;
+       ah->sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE |
+                              AR_STA_ID1_MCAST_KSRCH;
        if (AR_SREV_9100(ah))
                ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX;
+
        ah->slottime = ATH9K_SLOT_TIME_9;
        ah->globaltxtimeout = (u32) -1;
        ah->power_mode = ATH9K_PM_UNDEFINED;
        ah->htc_reset_init = true;
+
+       ah->ani_function = ATH9K_ANI_ALL;
+       if (!AR_SREV_9300_20_OR_LATER(ah))
+               ah->ani_function &= ~ATH9K_ANI_MRC_CCK;
+
+       if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+               ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S);
+       else
+               ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
 }
 
 static int ath9k_hw_init_macaddr(struct ath_hw *ah)
@@ -576,6 +505,31 @@ static int __ath9k_hw_init(struct ath_hw *ah)
 
        ath9k_hw_read_revisions(ah);
 
+       switch (ah->hw_version.macVersion) {
+       case AR_SREV_VERSION_5416_PCI:
+       case AR_SREV_VERSION_5416_PCIE:
+       case AR_SREV_VERSION_9160:
+       case AR_SREV_VERSION_9100:
+       case AR_SREV_VERSION_9280:
+       case AR_SREV_VERSION_9285:
+       case AR_SREV_VERSION_9287:
+       case AR_SREV_VERSION_9271:
+       case AR_SREV_VERSION_9300:
+       case AR_SREV_VERSION_9330:
+       case AR_SREV_VERSION_9485:
+       case AR_SREV_VERSION_9340:
+       case AR_SREV_VERSION_9462:
+       case AR_SREV_VERSION_9550:
+       case AR_SREV_VERSION_9565:
+       case AR_SREV_VERSION_9531:
+               break;
+       default:
+               ath_err(common,
+                       "Mac Chip Rev 0x%02x.%x is not supported by this driver\n",
+                       ah->hw_version.macVersion, ah->hw_version.macRev);
+               return -EOPNOTSUPP;
+       }
+
        /*
         * Read back AR_WA into a permanent copy and set bits 14 and 17.
         * We need to do this to avoid RMW of this register. We cannot
@@ -609,50 +563,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
                return -EIO;
        }
 
-       if (NR_CPUS > 1 && ah->config.serialize_regmode == SER_REG_MODE_AUTO) {
-               if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI ||
-                   ((AR_SREV_9160(ah) || AR_SREV_9280(ah) || AR_SREV_9287(ah)) &&
-                    !ah->is_pciexpress)) {
-                       ah->config.serialize_regmode =
-                               SER_REG_MODE_ON;
-               } else {
-                       ah->config.serialize_regmode =
-                               SER_REG_MODE_OFF;
-               }
-       }
-
-       ath_dbg(common, RESET, "serialize_regmode is %d\n",
-               ah->config.serialize_regmode);
-
-       if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
-               ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD >> 1;
-       else
-               ah->config.max_txtrig_level = MAX_TX_FIFO_THRESHOLD;
-
-       switch (ah->hw_version.macVersion) {
-       case AR_SREV_VERSION_5416_PCI:
-       case AR_SREV_VERSION_5416_PCIE:
-       case AR_SREV_VERSION_9160:
-       case AR_SREV_VERSION_9100:
-       case AR_SREV_VERSION_9280:
-       case AR_SREV_VERSION_9285:
-       case AR_SREV_VERSION_9287:
-       case AR_SREV_VERSION_9271:
-       case AR_SREV_VERSION_9300:
-       case AR_SREV_VERSION_9330:
-       case AR_SREV_VERSION_9485:
-       case AR_SREV_VERSION_9340:
-       case AR_SREV_VERSION_9462:
-       case AR_SREV_VERSION_9550:
-       case AR_SREV_VERSION_9565:
-               break;
-       default:
-               ath_err(common,
-                       "Mac Chip Rev 0x%02x.%x is not supported by this driver\n",
-                       ah->hw_version.macVersion, ah->hw_version.macRev);
-               return -EOPNOTSUPP;
-       }
-
        if (AR_SREV_9271(ah) || AR_SREV_9100(ah) || AR_SREV_9340(ah) ||
            AR_SREV_9330(ah) || AR_SREV_9550(ah))
                ah->is_pciexpress = false;
@@ -660,10 +570,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
        ah->hw_version.phyRev = REG_READ(ah, AR_PHY_CHIP_ID);
        ath9k_hw_init_cal_settings(ah);
 
-       ah->ani_function = ATH9K_ANI_ALL;
-       if (!AR_SREV_9300_20_OR_LATER(ah))
-               ah->ani_function &= ~ATH9K_ANI_MRC_CCK;
-
        if (!ah->is_pciexpress)
                ath9k_hw_disablepcie(ah);
 
@@ -682,15 +588,7 @@ static int __ath9k_hw_init(struct ath_hw *ah)
                return r;
        }
 
-       if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
-               ah->tx_trig_level = (AR_FTRIG_256B >> AR_FTRIG_S);
-       else
-               ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
-
-       if (AR_SREV_9330(ah))
-               ah->bb_watchdog_timeout_ms = 85;
-       else
-               ah->bb_watchdog_timeout_ms = 25;
+       ath9k_hw_init_hang_checks(ah);
 
        common->state = ATH_HW_INITIALIZED;
 
@@ -723,6 +621,7 @@ int ath9k_hw_init(struct ath_hw *ah)
        case AR9300_DEVID_AR9462:
        case AR9485_DEVID_AR1111:
        case AR9300_DEVID_AR9565:
+       case AR9300_DEVID_AR953X:
                break;
        default:
                if (common->bus_ops->ath_bus_type == ATH_USB)
@@ -858,7 +757,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
                /* program BB PLL phase_shift */
                REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3,
                              AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1);
-       } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah)) {
+       } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) {
                u32 regval, pll2_divint, pll2_divfrac, refdiv;
 
                REG_WRITE(ah, AR_RTC_PLL_CONTROL, 0x1142c);
@@ -868,9 +767,15 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
                udelay(100);
 
                if (ah->is_clk_25mhz) {
-                       pll2_divint = 0x54;
-                       pll2_divfrac = 0x1eb85;
-                       refdiv = 3;
+                       if (AR_SREV_9531(ah)) {
+                               pll2_divint = 0x1c;
+                               pll2_divfrac = 0xa3d2;
+                               refdiv = 1;
+                       } else {
+                               pll2_divint = 0x54;
+                               pll2_divfrac = 0x1eb85;
+                               refdiv = 3;
+                       }
                } else {
                        if (AR_SREV_9340(ah)) {
                                pll2_divint = 88;
@@ -884,7 +789,10 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
                }
 
                regval = REG_READ(ah, AR_PHY_PLL_MODE);
-               regval |= (0x1 << 16);
+               if (AR_SREV_9531(ah))
+                       regval |= (0x1 << 22);
+               else
+                       regval |= (0x1 << 16);
                REG_WRITE(ah, AR_PHY_PLL_MODE, regval);
                udelay(100);
 
@@ -894,14 +802,33 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
 
                regval = REG_READ(ah, AR_PHY_PLL_MODE);
                if (AR_SREV_9340(ah))
-                       regval = (regval & 0x80071fff) | (0x1 << 30) |
-                                (0x1 << 13) | (0x4 << 26) | (0x18 << 19);
+                       regval = (regval & 0x80071fff) |
+                               (0x1 << 30) |
+                               (0x1 << 13) |
+                               (0x4 << 26) |
+                               (0x18 << 19);
+               else if (AR_SREV_9531(ah))
+                       regval = (regval & 0x01c00fff) |
+                               (0x1 << 31) |
+                               (0x2 << 29) |
+                               (0xa << 25) |
+                               (0x1 << 19) |
+                               (0x6 << 12);
                else
-                       regval = (regval & 0x80071fff) | (0x3 << 30) |
-                                (0x1 << 13) | (0x4 << 26) | (0x60 << 19);
+                       regval = (regval & 0x80071fff) |
+                               (0x3 << 30) |
+                               (0x1 << 13) |
+                               (0x4 << 26) |
+                               (0x60 << 19);
                REG_WRITE(ah, AR_PHY_PLL_MODE, regval);
-               REG_WRITE(ah, AR_PHY_PLL_MODE,
-                         REG_READ(ah, AR_PHY_PLL_MODE) & 0xfffeffff);
+
+               if (AR_SREV_9531(ah))
+                       REG_WRITE(ah, AR_PHY_PLL_MODE,
+                                 REG_READ(ah, AR_PHY_PLL_MODE) & 0xffbfffff);
+               else
+                       REG_WRITE(ah, AR_PHY_PLL_MODE,
+                                 REG_READ(ah, AR_PHY_PLL_MODE) & 0xfffeffff);
+
                udelay(1000);
        }
 
@@ -1281,6 +1208,42 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled,
        *coef_exponent = coef_exp - 16;
 }
 
+/* AR9330 WAR:
+ * call external reset function to reset WMAC if:
+ * - doing a cold reset
+ * - we have pending frames in the TX queues.
+ */
+static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type)
+{
+       int i, npend = 0;
+
+       for (i = 0; i < AR_NUM_QCU; i++) {
+               npend = ath9k_hw_numtxpending(ah, i);
+               if (npend)
+                       break;
+       }
+
+       if (ah->external_reset &&
+           (npend || type == ATH9K_RESET_COLD)) {
+               int reset_err = 0;
+
+               ath_dbg(ath9k_hw_common(ah), RESET,
+                       "reset MAC via external reset\n");
+
+               reset_err = ah->external_reset();
+               if (reset_err) {
+                       ath_err(ath9k_hw_common(ah),
+                               "External reset failed, err=%d\n",
+                               reset_err);
+                       return false;
+               }
+
+               REG_WRITE(ah, AR_RTC_RESET, 1);
+       }
+
+       return true;
+}
+
 static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
 {
        u32 rst_flags;
@@ -1331,38 +1294,8 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
        }
 
        if (AR_SREV_9330(ah)) {
-               int npend = 0;
-               int i;
-
-               /* AR9330 WAR:
-                * call external reset function to reset WMAC if:
-                * - doing a cold reset
-                * - we have pending frames in the TX queues
-                */
-
-               for (i = 0; i < AR_NUM_QCU; i++) {
-                       npend = ath9k_hw_numtxpending(ah, i);
-                       if (npend)
-                               break;
-               }
-
-               if (ah->external_reset &&
-                   (npend || type == ATH9K_RESET_COLD)) {
-                       int reset_err = 0;
-
-                       ath_dbg(ath9k_hw_common(ah), RESET,
-                               "reset MAC via external reset\n");
-
-                       reset_err = ah->external_reset();
-                       if (reset_err) {
-                               ath_err(ath9k_hw_common(ah),
-                                       "External reset failed, err=%d\n",
-                                       reset_err);
-                               return false;
-                       }
-
-                       REG_WRITE(ah, AR_RTC_RESET, 1);
-               }
+               if (!ath9k_hw_ar9330_reset_war(ah, type))
+                       return false;
        }
 
        if (ath9k_hw_mci_is_enabled(ah))
@@ -1372,7 +1305,12 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type)
 
        REGWRITE_BUFFER_FLUSH(ah);
 
-       udelay(50);
+       if (AR_SREV_9300_20_OR_LATER(ah))
+               udelay(50);
+       else if (AR_SREV_9100(ah))
+               udelay(10000);
+       else
+               udelay(100);
 
        REG_WRITE(ah, AR_RTC_RC, 0);
        if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) {
@@ -1408,8 +1346,7 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah)
 
        REGWRITE_BUFFER_FLUSH(ah);
 
-       if (!AR_SREV_9300_20_OR_LATER(ah))
-               udelay(2);
+       udelay(2);
 
        if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah))
                REG_WRITE(ah, AR_RC, 0);
@@ -1485,7 +1422,6 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah,
        if (AR_SREV_9330(ah))
                ar9003_hw_internal_regulator_apply(ah);
        ath9k_hw_init_pll(ah, chan);
-       ath9k_hw_set_rfmode(ah, chan);
 
        return true;
 }
@@ -1574,76 +1510,6 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah)
        }
 }
 
-static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states,
-                              int *hang_state, int *hang_pos)
-{
-       static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
-       u32 chain_state, dcs_pos, i;
-
-       for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
-               chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
-               for (i = 0; i < 3; i++) {
-                       if (chain_state == dcu_chain_state[i]) {
-                               *hang_state = chain_state;
-                               *hang_pos = dcs_pos;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-#define DCU_COMPLETE_STATE        1
-#define DCU_COMPLETE_STATE_MASK 0x3
-#define NUM_STATUS_READS         50
-static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
-{
-       u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
-       u32 i, hang_pos, hang_state, num_state = 6;
-
-       comp_state = REG_READ(ah, AR_DMADBG_6);
-
-       if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
-               ath_dbg(ath9k_hw_common(ah), RESET,
-                       "MAC Hang signature not found at DCU complete\n");
-               return false;
-       }
-
-       chain_state = REG_READ(ah, dcs_reg);
-       if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
-               goto hang_check_iter;
-
-       dcs_reg = AR_DMADBG_5;
-       num_state = 4;
-       chain_state = REG_READ(ah, dcs_reg);
-       if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
-               goto hang_check_iter;
-
-       ath_dbg(ath9k_hw_common(ah), RESET,
-               "MAC Hang signature 1 not found\n");
-       return false;
-
-hang_check_iter:
-       ath_dbg(ath9k_hw_common(ah), RESET,
-               "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
-               chain_state, comp_state, hang_state, hang_pos);
-
-       for (i = 0; i < NUM_STATUS_READS; i++) {
-               chain_state = REG_READ(ah, dcs_reg);
-               chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
-               comp_state = REG_READ(ah, AR_DMADBG_6);
-
-               if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
-                                       DCU_COMPLETE_STATE) ||
-                   (chain_state != hang_state))
-                       return false;
-       }
-
-       ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n");
-
-       return true;
-}
-
 void ath9k_hw_check_nav(struct ath_hw *ah)
 {
        struct ath_common *common = ath9k_hw_common(ah);
@@ -1718,7 +1584,6 @@ static void ath9k_hw_reset_opmode(struct ath_hw *ah,
 
        REG_RMW(ah, AR_STA_ID1, macStaId1
                  | AR_STA_ID1_RTS_USE_DEF
-                 | (ah->config.ack_6mb ? AR_STA_ID1_ACKCTS_6MB : 0)
                  | ah->sta_id1_defaults,
                  ~AR_STA_ID1_SADH_MASK);
        ath_hw_setbssidmask(common);
@@ -1777,7 +1642,7 @@ static void ath9k_hw_init_desc(struct ath_hw *ah)
                }
 #ifdef __BIG_ENDIAN
                else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
-                        AR_SREV_9550(ah))
+                        AR_SREV_9550(ah) || AR_SREV_9531(ah))
                        REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0);
                else
                        REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD);
@@ -1907,7 +1772,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        /* Save TSF before chip reset, a cold reset clears it */
        tsf = ath9k_hw_gettsf64(ah);
        getrawmonotonic(&ts);
-       usec = ts.tv_sec * 1000 + ts.tv_nsec / 1000;
+       usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
 
        saveLedState = REG_READ(ah, AR_CFG_LED) &
                (AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
@@ -1941,7 +1806,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
 
        /* Restore TSF */
        getrawmonotonic(&ts);
-       usec = ts.tv_sec * 1000 + ts.tv_nsec / 1000 - usec;
+       usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec;
        ath9k_hw_settsf64(ah, tsf + usec);
 
        if (AR_SREV_9280_20_OR_LATER(ah))
@@ -1954,6 +1819,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        if (r)
                return r;
 
+       ath9k_hw_set_rfmode(ah, chan);
+
        if (ath9k_hw_mci_is_enabled(ah))
                ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep);
 
@@ -2048,10 +1915,11 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
        ath9k_hw_loadnf(ah, chan);
        ath9k_hw_start_nfcal(ah, true);
 
-       if (AR_SREV_9300_20_OR_LATER(ah)) {
+       if (AR_SREV_9300_20_OR_LATER(ah))
                ar9003_hw_bb_watchdog_config(ah);
+
+       if (ah->config.hw_hang_checks & HW_PHYRESTART_CLC_WAR)
                ar9003_hw_disable_phy_restart(ah);
-       }
 
        ath9k_hw_apply_gpio_override(ah);
 
@@ -2175,7 +2043,11 @@ static bool ath9k_hw_set_power_awake(struct ath_hw *ah)
 
        REG_SET_BIT(ah, AR_RTC_FORCE_WAKE,
                    AR_RTC_FORCE_WAKE_EN);
-       udelay(50);
+
+       if (AR_SREV_9100(ah))
+               udelay(10000);
+       else
+               udelay(50);
 
        for (i = POWER_UP_TIME / 50; i > 0; i--) {
                val = REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
@@ -2264,9 +2136,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
        case NL80211_IFTYPE_ADHOC:
                REG_SET_BIT(ah, AR_TXCFG,
                            AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY);
-               REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon +
-                         TU_TO_USEC(ah->atim_window ? ah->atim_window : 1));
-               flags |= AR_NDP_TIMER_EN;
        case NL80211_IFTYPE_MESH_POINT:
        case NL80211_IFTYPE_AP:
                REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon);
@@ -2287,7 +2156,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period)
        REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period);
        REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period);
        REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period);
-       REG_WRITE(ah, AR_NDP_PERIOD, beacon_period);
 
        REGWRITE_BUFFER_FLUSH(ah);
 
@@ -2304,12 +2172,9 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
 
        ENABLE_REGWRITE_BUFFER(ah);
 
-       REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt));
-
-       REG_WRITE(ah, AR_BEACON_PERIOD,
-                 TU_TO_USEC(bs->bs_intval));
-       REG_WRITE(ah, AR_DMA_BEACON_PERIOD,
-                 TU_TO_USEC(bs->bs_intval));
+       REG_WRITE(ah, AR_NEXT_TBTT_TIMER, bs->bs_nexttbtt);
+       REG_WRITE(ah, AR_BEACON_PERIOD, bs->bs_intval);
+       REG_WRITE(ah, AR_DMA_BEACON_PERIOD, bs->bs_intval);
 
        REGWRITE_BUFFER_FLUSH(ah);
 
@@ -2337,9 +2202,8 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
 
        ENABLE_REGWRITE_BUFFER(ah);
 
-       REG_WRITE(ah, AR_NEXT_DTIM,
-                 TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP));
-       REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP));
+       REG_WRITE(ah, AR_NEXT_DTIM, bs->bs_nextdtim - SLEEP_SLOP);
+       REG_WRITE(ah, AR_NEXT_TIM, nextTbtt - SLEEP_SLOP);
 
        REG_WRITE(ah, AR_SLEEP1,
                  SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT)
@@ -2353,8 +2217,8 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
        REG_WRITE(ah, AR_SLEEP2,
                  SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT));
 
-       REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval));
-       REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod));
+       REG_WRITE(ah, AR_TIM_PERIOD, beaconintval);
+       REG_WRITE(ah, AR_DTIM_PERIOD, dtimperiod);
 
        REGWRITE_BUFFER_FLUSH(ah);
 
@@ -2612,13 +2476,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
            ah->eep_ops->get_eeprom(ah, EEP_PAPRD))
                        pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
 
-       /*
-        * Fast channel change across bands is available
-        * only for AR9462 and AR9565.
-        */
-       if (AR_SREV_9462(ah) || AR_SREV_9565(ah))
-               pCap->hw_caps |= ATH9K_HW_CAP_FCC_BAND_SWITCH;
-
        return 0;
 }
 
@@ -2990,20 +2847,6 @@ static const struct ath_gen_timer_configuration gen_tmr_configuration[] =
 
 /* HW generic timer primitives */
 
-/* compute and clear index of rightmost 1 */
-static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask)
-{
-       u32 b;
-
-       b = *mask;
-       b &= (0-b);
-       *mask &= ~b;
-       b *= debruijn32;
-       b >>= 27;
-
-       return timer_table->gen_timer_index[b];
-}
-
 u32 ath9k_hw_gettsf32(struct ath_hw *ah)
 {
        return REG_READ(ah, AR_TSF_L32);
@@ -3019,6 +2862,10 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
        struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
        struct ath_gen_timer *timer;
 
+       if ((timer_index < AR_FIRST_NDP_TIMER) ||
+               (timer_index >= ATH_MAX_GEN_TIMER))
+               return NULL;
+
        timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
        if (timer == NULL)
                return NULL;
@@ -3036,23 +2883,13 @@ EXPORT_SYMBOL(ath_gen_timer_alloc);
 
 void ath9k_hw_gen_timer_start(struct ath_hw *ah,
                              struct ath_gen_timer *timer,
-                             u32 trig_timeout,
+                             u32 timer_next,
                              u32 timer_period)
 {
        struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
-       u32 tsf, timer_next;
-
-       BUG_ON(!timer_period);
-
-       set_bit(timer->index, &timer_table->timer_mask.timer_bits);
+       u32 mask = 0;
 
-       tsf = ath9k_hw_gettsf32(ah);
-
-       timer_next = tsf + trig_timeout;
-
-       ath_dbg(ath9k_hw_common(ah), BTCOEX,
-               "current tsf %x period %x timer_next %x\n",
-               tsf, timer_period, timer_next);
+       timer_table->timer_mask |= BIT(timer->index);
 
        /*
         * Program generic timer registers
@@ -3078,10 +2915,19 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
                                       (1 << timer->index));
        }
 
-       /* Enable both trigger and thresh interrupt masks */
-       REG_SET_BIT(ah, AR_IMR_S5,
-               (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
-               SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
+       if (timer->trigger)
+               mask |= SM(AR_GENTMR_BIT(timer->index),
+                          AR_IMR_S5_GENTIMER_TRIG);
+       if (timer->overflow)
+               mask |= SM(AR_GENTMR_BIT(timer->index),
+                          AR_IMR_S5_GENTIMER_THRESH);
+
+       REG_SET_BIT(ah, AR_IMR_S5, mask);
+
+       if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
+               ah->imask |= ATH9K_INT_GENTIMER;
+               ath9k_hw_set_interrupts(ah);
+       }
 }
 EXPORT_SYMBOL(ath9k_hw_gen_timer_start);
 
@@ -3089,11 +2935,6 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
 {
        struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
 
-       if ((timer->index < AR_FIRST_NDP_TIMER) ||
-               (timer->index >= ATH_MAX_GEN_TIMER)) {
-               return;
-       }
-
        /* Clear generic timer enable bits. */
        REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
                        gen_tmr_configuration[timer->index].mode_mask);
@@ -3113,7 +2954,12 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
                (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
                SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
 
-       clear_bit(timer->index, &timer_table->timer_mask.timer_bits);
+       timer_table->timer_mask &= ~BIT(timer->index);
+
+       if (timer_table->timer_mask == 0) {
+               ah->imask &= ~ATH9K_INT_GENTIMER;
+               ath9k_hw_set_interrupts(ah);
+       }
 }
 EXPORT_SYMBOL(ath9k_hw_gen_timer_stop);
 
@@ -3134,32 +2980,32 @@ void ath_gen_timer_isr(struct ath_hw *ah)
 {
        struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
        struct ath_gen_timer *timer;
-       struct ath_common *common = ath9k_hw_common(ah);
-       u32 trigger_mask, thresh_mask, index;
+       unsigned long trigger_mask, thresh_mask;
+       unsigned int index;
 
        /* get hardware generic timer interrupt status */
        trigger_mask = ah->intr_gen_timer_trigger;
        thresh_mask = ah->intr_gen_timer_thresh;
-       trigger_mask &= timer_table->timer_mask.val;
-       thresh_mask &= timer_table->timer_mask.val;
-
-       trigger_mask &= ~thresh_mask;
+       trigger_mask &= timer_table->timer_mask;
+       thresh_mask &= timer_table->timer_mask;
 
-       while (thresh_mask) {
-               index = rightmost_index(timer_table, &thresh_mask);
+       for_each_set_bit(index, &thresh_mask, ARRAY_SIZE(timer_table->timers)) {
                timer = timer_table->timers[index];
-               BUG_ON(!timer);
-               ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n",
-                       index);
+               if (!timer)
+                   continue;
+               if (!timer->overflow)
+                   continue;
+
+               trigger_mask &= ~BIT(index);
                timer->overflow(timer->arg);
        }
 
-       while (trigger_mask) {
-               index = rightmost_index(timer_table, &trigger_mask);
+       for_each_set_bit(index, &trigger_mask, ARRAY_SIZE(timer_table->timers)) {
                timer = timer_table->timers[index];
-               BUG_ON(!timer);
-               ath_dbg(common, BTCOEX,
-                       "Gen timer[%d] trigger\n", index);
+               if (!timer)
+                   continue;
+               if (!timer->trigger)
+                   continue;
                timer->trigger(timer->arg);
        }
 }
index b1ff546..e766399 100644 (file)
@@ -52,6 +52,7 @@
 #define AR9300_DEVID_QCA955X   0x0038
 #define AR9485_DEVID_AR1111    0x0037
 #define AR9300_DEVID_AR9565     0x0036
+#define AR9300_DEVID_AR953X     0x003d
 
 #define AR5416_AR9100_DEVID    0x000b
 
 #define CAB_TIMEOUT_VAL             10
 #define BEACON_TIMEOUT_VAL          10
 #define MIN_BEACON_TIMEOUT_VAL      1
-#define SLEEP_SLOP                  3
+#define SLEEP_SLOP                  TU_TO_USEC(3)
 
 #define INIT_CONFIG_STATUS          0x00000000
 #define INIT_RSSI_THR               0x00000700
@@ -277,13 +278,25 @@ struct ath9k_hw_capabilities {
        u8 txs_len;
 };
 
+#define AR_NO_SPUR             0x8000
+#define AR_BASE_FREQ_2GHZ      2300
+#define AR_BASE_FREQ_5GHZ      4900
+#define AR_SPUR_FEEQ_BOUND_HT40 19
+#define AR_SPUR_FEEQ_BOUND_HT20 10
+
+enum ath9k_hw_hang_checks {
+       HW_BB_WATCHDOG            = BIT(0),
+       HW_PHYRESTART_CLC_WAR     = BIT(1),
+       HW_BB_RIFS_HANG           = BIT(2),
+       HW_BB_DFS_HANG            = BIT(3),
+       HW_BB_RX_CLEAR_STUCK_HANG = BIT(4),
+       HW_MAC_HANG               = BIT(5),
+};
+
 struct ath9k_ops_config {
        int dma_beacon_response_time;
        int sw_beacon_response_time;
-       int additional_swba_backoff;
-       int ack_6mb;
        u32 cwm_ignore_extcca;
-       u8 pcie_clock_req;
        u32 pcie_waen;
        u8 analog_shiftreg;
        u32 ofdm_trig_low;
@@ -294,20 +307,9 @@ struct ath9k_ops_config {
        int serialize_regmode;
        bool rx_intr_mitigation;
        bool tx_intr_mitigation;
-#define SPUR_DISABLE           0
-#define SPUR_ENABLE_IOCTL      1
-#define SPUR_ENABLE_EEPROM     2
-#define AR_SPUR_5413_1         1640
-#define AR_SPUR_5413_2         1200
-#define AR_NO_SPUR             0x8000
-#define AR_BASE_FREQ_2GHZ      2300
-#define AR_BASE_FREQ_5GHZ      4900
-#define AR_SPUR_FEEQ_BOUND_HT40 19
-#define AR_SPUR_FEEQ_BOUND_HT20 10
-       int spurmode;
-       u16 spurchans[AR_EEPROM_MODAL_SPURS][2];
        u8 max_txtrig_level;
        u16 ani_poll_interval; /* ANI poll interval in ms */
+       u16 hw_hang_checks;
 
        /* Platform specific config */
        u32 aspm_l1_fix;
@@ -460,10 +462,6 @@ struct ath9k_beacon_state {
        u32 bs_intval;
 #define ATH9K_TSFOOR_THRESHOLD    0x00004240 /* 16k us */
        u32 bs_dtimperiod;
-       u16 bs_cfpperiod;
-       u16 bs_cfpmaxduration;
-       u32 bs_cfpnext;
-       u16 bs_timoffset;
        u16 bs_bmissthreshold;
        u32 bs_sleepduration;
        u32 bs_tsfoor_threshold;
@@ -499,12 +497,6 @@ struct ath9k_hw_version {
 
 #define AR_GENTMR_BIT(_index)  (1 << (_index))
 
-/*
- * Using de Bruijin sequence to look up 1's index in a 32 bit number
- * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001
- */
-#define debruijn32 0x077CB531U
-
 struct ath_gen_timer_configuration {
        u32 next_addr;
        u32 period_addr;
@@ -520,12 +512,8 @@ struct ath_gen_timer {
 };
 
 struct ath_gen_timer_table {
-       u32 gen_timer_index[32];
        struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER];
-       union {
-               unsigned long timer_bits;
-               u16 val;
-       } timer_mask;
+       u16 timer_mask;
 };
 
 struct ath_hw_antcomb_conf {
@@ -596,6 +584,10 @@ struct ath_hw_radar_conf {
  *     register settings through the register initialization.
  */
 struct ath_hw_private_ops {
+       void (*init_hang_checks)(struct ath_hw *ah);
+       bool (*detect_mac_hang)(struct ath_hw *ah);
+       bool (*detect_bb_hang)(struct ath_hw *ah);
+
        /* Calibration ops */
        void (*init_cal_settings)(struct ath_hw *ah);
        bool (*init_cal)(struct ath_hw *ah, struct ath9k_channel *chan);
@@ -690,7 +682,8 @@ struct ath_hw_ops {
                          struct ath9k_channel *chan,
                          u8 rxchainmask,
                          bool longcal);
-       bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked);
+       bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked,
+                       u32 *sync_cause_p);
        void (*set_txdesc)(struct ath_hw *ah, void *ds,
                           struct ath_tx_info *i);
        int (*proc_txdesc)(struct ath_hw *ah, void *ds,
@@ -786,7 +779,6 @@ struct ath_hw {
        u32 txurn_interrupt_mask;
        atomic_t intr_ref_cnt;
        bool chip_fullsleep;
-       u32 atim_window;
        u32 modes_index;
 
        /* Calibration */
@@ -1018,13 +1010,6 @@ bool ath9k_hw_check_alive(struct ath_hw *ah);
 
 bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode);
 
-#ifdef CONFIG_ATH9K_DEBUGFS
-void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause);
-#else
-static inline void ath9k_debug_sync_cause(struct ath_common *common,
-                                         u32 sync_cause) {}
-#endif
-
 /* Generic hw timer primitives */
 struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
                                          void (*trigger)(void *),
@@ -1059,6 +1044,7 @@ void ar9002_hw_enable_async_fifo(struct ath_hw *ah);
  * Code specific to AR9003, we stuff these here to avoid callbacks
  * for older families
  */
+bool ar9003_hw_bb_watchdog_check(struct ath_hw *ah);
 void ar9003_hw_bb_watchdog_config(struct ath_hw *ah);
 void ar9003_hw_bb_watchdog_read(struct ath_hw *ah);
 void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah);
index d878769..c36de30 100644 (file)
@@ -470,7 +470,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
 
        sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
        sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
-
        ath_cabq_update(sc);
 
        sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
@@ -705,7 +704,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
        ah->reg_ops.read = ath9k_ioread32;
        ah->reg_ops.write = ath9k_iowrite32;
        ah->reg_ops.rmw = ath9k_reg_rmw;
-       atomic_set(&ah->intr_ref_cnt, -1);
        sc->sc_ah = ah;
        pCap = &ah->caps;
 
@@ -765,10 +763,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
 
        setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
        INIT_WORK(&sc->hw_reset_work, ath_reset_work);
-       INIT_WORK(&sc->hw_check_work, ath_hw_check);
        INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
        INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
-       setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc);
 
        /*
         * Cache line size is used to size and align various
@@ -899,7 +895,7 @@ static const struct ieee80211_iface_combination if_comb[] = {
        }
 };
 
-void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
+static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
 {
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
index aed7e29..30dcef5 100644 (file)
@@ -65,50 +65,26 @@ void ath_tx_complete_poll_work(struct work_struct *work)
 /*
  * Checks if the BB/MAC is hung.
  */
-void ath_hw_check(struct work_struct *work)
+bool ath_hw_check(struct ath_softc *sc)
 {
-       struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-       unsigned long flags;
-       int busy;
-       u8 is_alive, nbeacon = 1;
        enum ath_reset_type type;
+       bool is_alive;
 
        ath9k_ps_wakeup(sc);
+
        is_alive = ath9k_hw_check_alive(sc->sc_ah);
 
-       if ((is_alive && !AR_SREV_9300(sc->sc_ah)) || sc->tx99_state)
-               goto out;
-       else if (!is_alive && AR_SREV_9300(sc->sc_ah)) {
+       if (!is_alive) {
                ath_dbg(common, RESET,
-                       "DCU stuck is detected. Schedule chip reset\n");
+                       "HW hang detected, schedule chip reset\n");
                type = RESET_TYPE_MAC_HANG;
-               goto sched_reset;
-       }
-
-       spin_lock_irqsave(&common->cc_lock, flags);
-       busy = ath_update_survey_stats(sc);
-       spin_unlock_irqrestore(&common->cc_lock, flags);
-
-       ath_dbg(common, RESET, "Possible baseband hang, busy=%d (try %d)\n",
-               busy, sc->hw_busy_count + 1);
-       if (busy >= 99) {
-               if (++sc->hw_busy_count >= 3) {
-                       type = RESET_TYPE_BB_HANG;
-                       goto sched_reset;
-               }
-       } else if (busy >= 0) {
-               sc->hw_busy_count = 0;
-               nbeacon = 3;
+               ath9k_queue_reset(sc, type);
        }
 
-       ath_start_rx_poll(sc, nbeacon);
-       goto out;
-
-sched_reset:
-       ath9k_queue_reset(sc, type);
-out:
        ath9k_ps_restore(sc);
+
+       return is_alive;
 }
 
 /*
@@ -162,29 +138,6 @@ void ath_hw_pll_work(struct work_struct *work)
 }
 
 /*
- * RX Polling - monitors baseband hangs.
- */
-void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon)
-{
-       if (!AR_SREV_9300(sc->sc_ah))
-               return;
-
-       if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags))
-               return;
-
-       mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies
-                 (nbeacon * sc->cur_beacon_conf.beacon_interval));
-}
-
-void ath_rx_poll(unsigned long data)
-{
-       struct ath_softc *sc = (struct ath_softc *)data;
-
-       if (!test_bit(SC_OP_INVALID, &sc->sc_flags))
-               ieee80211_queue_work(sc->hw, &sc->hw_check_work);
-}
-
-/*
  * PA Pre-distortion.
  */
 static void ath_paprd_activate(struct ath_softc *sc)
@@ -409,10 +362,10 @@ void ath_ani_calibrate(unsigned long data)
 
        /* Call ANI routine if necessary */
        if (aniflag) {
-               spin_lock_irqsave(&common->cc_lock, flags);
+               spin_lock(&common->cc_lock);
                ath9k_hw_ani_monitor(ah, ah->curchan);
                ath_update_survey_stats(sc);
-               spin_unlock_irqrestore(&common->cc_lock, flags);
+               spin_unlock(&common->cc_lock);
        }
 
        /* Perform calibration if necessary */
index 6a18f9d..5f72758 100644 (file)
@@ -481,8 +481,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
                            | AR_Q_MISC_CBR_INCR_DIS0);
                value = (qi->tqi_readyTime -
                         (ah->config.sw_beacon_response_time -
-                         ah->config.dma_beacon_response_time) -
-                        ah->config.additional_swba_backoff) * 1024;
+                         ah->config.dma_beacon_response_time)) * 1024;
                REG_WRITE(ah, AR_QRDYTIMECFG(q),
                          value | AR_Q_RDYTIMECFG_EN);
                REG_SET_BIT(ah, AR_DMISC(q),
@@ -550,25 +549,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds,
 
        if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) {
                rs->rs_rssi = ATH9K_RSSI_BAD;
-               rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD;
-               rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD;
-               rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD;
-               rs->rs_rssi_ext0 = ATH9K_RSSI_BAD;
-               rs->rs_rssi_ext1 = ATH9K_RSSI_BAD;
-               rs->rs_rssi_ext2 = ATH9K_RSSI_BAD;
+               rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD;
+               rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD;
+               rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD;
+               rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD;
+               rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD;
+               rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD;
        } else {
                rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined);
-               rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0,
+               rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0,
                                                AR_RxRSSIAnt00);
-               rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0,
+               rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0,
                                                AR_RxRSSIAnt01);
-               rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0,
+               rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0,
                                                AR_RxRSSIAnt02);
-               rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4,
+               rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4,
                                                AR_RxRSSIAnt10);
-               rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4,
+               rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4,
                                                AR_RxRSSIAnt11);
-               rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4,
+               rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4,
                                                AR_RxRSSIAnt12);
        }
        if (ads.ds_rxstatus8 & AR_RxKeyIdxValid)
@@ -923,11 +922,29 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah)
                        mask2 |= AR_IMR_S2_CST;
        }
 
+       if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) {
+               if (ints & ATH9K_INT_BB_WATCHDOG) {
+                       mask |= AR_IMR_BCNMISC;
+                       mask2 |= AR_IMR_S2_BB_WATCHDOG;
+               }
+       }
+
        ath_dbg(common, INTERRUPT, "new IMR 0x%x\n", mask);
        REG_WRITE(ah, AR_IMR, mask);
-       ah->imrs2_reg &= ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC |
-                          AR_IMR_S2_CABEND | AR_IMR_S2_CABTO |
-                          AR_IMR_S2_TSFOOR | AR_IMR_S2_GTT | AR_IMR_S2_CST);
+       ah->imrs2_reg &= ~(AR_IMR_S2_TIM |
+                          AR_IMR_S2_DTIM |
+                          AR_IMR_S2_DTIMSYNC |
+                          AR_IMR_S2_CABEND |
+                          AR_IMR_S2_CABTO |
+                          AR_IMR_S2_TSFOOR |
+                          AR_IMR_S2_GTT |
+                          AR_IMR_S2_CST);
+
+       if (ah->config.hw_hang_checks & HW_BB_WATCHDOG) {
+               if (ints & ATH9K_INT_BB_WATCHDOG)
+                       ah->imrs2_reg &= ~AR_IMR_S2_BB_WATCHDOG;
+       }
+
        ah->imrs2_reg |= mask2;
        REG_WRITE(ah, AR_IMR_S2, ah->imrs2_reg);
 
index e3eed81..1027137 100644 (file)
@@ -133,12 +133,8 @@ struct ath_rx_status {
        u8 rs_rate;
        u8 rs_antenna;
        u8 rs_more;
-       int8_t rs_rssi_ctl0;
-       int8_t rs_rssi_ctl1;
-       int8_t rs_rssi_ctl2;
-       int8_t rs_rssi_ext0;
-       int8_t rs_rssi_ext1;
-       int8_t rs_rssi_ext2;
+       int8_t rs_rssi_ctl[3];
+       int8_t rs_rssi_ext[3];
        u8 rs_isaggr;
        u8 rs_firstaggr;
        u8 rs_moreaggr;
index b1dcf89..d0c3aec 100644 (file)
@@ -170,7 +170,6 @@ void ath9k_ps_restore(struct ath_softc *sc)
 static void __ath_cancel_work(struct ath_softc *sc)
 {
        cancel_work_sync(&sc->paprd_work);
-       cancel_work_sync(&sc->hw_check_work);
        cancel_delayed_work_sync(&sc->tx_complete_work);
        cancel_delayed_work_sync(&sc->hw_pll_work);
 
@@ -194,7 +193,6 @@ void ath_restart_work(struct ath_softc *sc)
                ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work,
                                     msecs_to_jiffies(ATH_PLL_WORK_INTERVAL));
 
-       ath_start_rx_poll(sc, 3);
        ath_start_ani(sc);
 }
 
@@ -204,11 +202,7 @@ static bool ath_prepare_reset(struct ath_softc *sc)
        bool ret = true;
 
        ieee80211_stop_queues(sc->hw);
-
-       sc->hw_busy_count = 0;
        ath_stop_ani(sc);
-       del_timer_sync(&sc->rx_poll_timer);
-
        ath9k_hw_disable_interrupts(ah);
 
        if (!ath_drain_all_txq(sc))
@@ -336,7 +330,6 @@ static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chand
        struct ieee80211_hw *hw = sc->hw;
        struct ath9k_channel *hchan;
        struct ieee80211_channel *chan = chandef->chan;
-       unsigned long flags;
        bool offchannel;
        int pos = chan->hw_value;
        int old_pos = -1;
@@ -354,9 +347,9 @@ static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chand
                chan->center_freq, chandef->width);
 
        /* update survey stats for the old channel before switching */
-       spin_lock_irqsave(&common->cc_lock, flags);
+       spin_lock_bh(&common->cc_lock);
        ath_update_survey_stats(sc);
-       spin_unlock_irqrestore(&common->cc_lock, flags);
+       spin_unlock_bh(&common->cc_lock);
 
        ath9k_cmn_get_channel(hw, ah, chandef);
 
@@ -427,12 +420,6 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
        an->vif = vif;
 
        ath_tx_node_init(sc, an);
-
-       if (sta->ht_cap.ht_supported) {
-               an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
-                                    sta->ht_cap.ampdu_factor);
-               an->mpdudensity = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density);
-       }
 }
 
 static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
@@ -454,14 +441,8 @@ void ath9k_tasklet(unsigned long data)
        ath9k_ps_wakeup(sc);
        spin_lock(&sc->sc_pcu_lock);
 
-       if ((status & ATH9K_INT_FATAL) ||
-           (status & ATH9K_INT_BB_WATCHDOG)) {
-
-               if (status & ATH9K_INT_FATAL)
-                       type = RESET_TYPE_FATAL_INT;
-               else
-                       type = RESET_TYPE_BB_WATCHDOG;
-
+       if (status & ATH9K_INT_FATAL) {
+               type = RESET_TYPE_FATAL_INT;
                ath9k_queue_reset(sc, type);
 
                /*
@@ -473,6 +454,28 @@ void ath9k_tasklet(unsigned long data)
                goto out;
        }
 
+       if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
+           (status & ATH9K_INT_BB_WATCHDOG)) {
+               spin_lock(&common->cc_lock);
+               ath_hw_cycle_counters_update(common);
+               ar9003_hw_bb_watchdog_dbg_info(ah);
+               spin_unlock(&common->cc_lock);
+
+               if (ar9003_hw_bb_watchdog_check(ah)) {
+                       type = RESET_TYPE_BB_WATCHDOG;
+                       ath9k_queue_reset(sc, type);
+
+                       /*
+                        * Increment the ref. counter here so that
+                        * interrupts are enabled in the reset routine.
+                        */
+                       atomic_inc(&ah->intr_ref_cnt);
+                       ath_dbg(common, ANY,
+                               "BB_WATCHDOG: Skipping interrupts\n");
+                       goto out;
+               }
+       }
+
        spin_lock_irqsave(&sc->sc_pm_lock, flags);
        if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) {
                /*
@@ -508,6 +511,9 @@ void ath9k_tasklet(unsigned long data)
                wake_up(&sc->tx_wait);
        }
 
+       if (status & ATH9K_INT_GENTIMER)
+               ath_gen_timer_isr(sc->sc_ah);
+
        ath9k_btcoex_handle_interrupt(sc, status);
 
        /* re-enable hardware interrupt */
@@ -538,6 +544,7 @@ irqreturn_t ath_isr(int irq, void *dev)
        struct ath_hw *ah = sc->sc_ah;
        struct ath_common *common = ath9k_hw_common(ah);
        enum ath9k_int status;
+       u32 sync_cause = 0;
        bool sched = false;
 
        /*
@@ -564,7 +571,8 @@ irqreturn_t ath_isr(int irq, void *dev)
         * bits we haven't explicitly enabled so we mask the
         * value to insure we only process bits we requested.
         */
-       ath9k_hw_getisr(ah, &status);   /* NB: clears ISR too */
+       ath9k_hw_getisr(ah, &status, &sync_cause); /* NB: clears ISR too */
+       ath9k_debug_sync_cause(sc, sync_cause);
        status &= ah->imask;    /* discard unasked-for bits */
 
        /*
@@ -588,16 +596,9 @@ irqreturn_t ath_isr(int irq, void *dev)
            !(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)))
                goto chip_reset;
 
-       if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) &&
-           (status & ATH9K_INT_BB_WATCHDOG)) {
-
-               spin_lock(&common->cc_lock);
-               ath_hw_cycle_counters_update(common);
-               ar9003_hw_bb_watchdog_dbg_info(ah);
-               spin_unlock(&common->cc_lock);
-
+       if ((ah->config.hw_hang_checks & HW_BB_WATCHDOG) &&
+           (status & ATH9K_INT_BB_WATCHDOG))
                goto chip_reset;
-       }
 
 #ifdef CONFIG_ATH9K_WOW
        if (status & ATH9K_INT_BMISS) {
@@ -727,11 +728,13 @@ static int ath9k_start(struct ieee80211_hw *hw)
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
                ah->imask |= ATH9K_INT_RXHP |
-                            ATH9K_INT_RXLP |
-                            ATH9K_INT_BB_WATCHDOG;
+                            ATH9K_INT_RXLP;
        else
                ah->imask |= ATH9K_INT_RX;
 
+       if (ah->config.hw_hang_checks & HW_BB_WATCHDOG)
+               ah->imask |= ATH9K_INT_BB_WATCHDOG;
+
        ah->imask |= ATH9K_INT_GTT;
 
        if (ah->caps.hw_caps & ATH9K_HW_CAP_HT)
@@ -757,6 +760,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
         */
        ath9k_cmn_init_crypto(sc->sc_ah);
 
+       ath9k_hw_reset_tsf(ah);
+
        spin_unlock_bh(&sc->sc_pcu_lock);
 
        mutex_unlock(&sc->mutex);
@@ -853,7 +858,6 @@ static void ath9k_stop(struct ieee80211_hw *hw)
        mutex_lock(&sc->mutex);
 
        ath_cancel_work(sc);
-       del_timer_sync(&sc->rx_poll_timer);
 
        if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
                ath_dbg(common, ANY, "Device not present\n");
@@ -987,8 +991,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
        struct ath_common *common = ath9k_hw_common(ah);
 
        /*
-        * Use the hardware MAC address as reference, the hardware uses it
-        * together with the BSSID mask when matching addresses.
+        * Pick the MAC address of the first interface as the new hardware
+        * MAC address. The hardware will use it together with the BSSID mask
+        * when matching addresses.
         */
        memset(iter_data, 0, sizeof(*iter_data));
        memset(&iter_data->mask, 0xff, ETH_ALEN);
@@ -1657,13 +1662,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
-           (changed & BSS_CHANGED_BEACON_INT)) {
-               if (ah->opmode == NL80211_IFTYPE_AP &&
-                   bss_conf->enable_beacon)
-                       ath9k_set_tsfadjust(sc, vif);
-               if (ath9k_allow_beacon_config(sc, vif))
-                       ath9k_beacon_config(sc, vif, changed);
-       }
+           (changed & BSS_CHANGED_BEACON_INT))
+               ath9k_beacon_config(sc, vif, changed);
 
        if (changed & BSS_CHANGED_ERP_SLOT) {
                if (bss_conf->use_short_slot)
@@ -1788,13 +1788,12 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
        struct ath_common *common = ath9k_hw_common(sc->sc_ah);
        struct ieee80211_supported_band *sband;
        struct ieee80211_channel *chan;
-       unsigned long flags;
        int pos;
 
        if (config_enabled(CONFIG_ATH9K_TX99))
                return -EOPNOTSUPP;
 
-       spin_lock_irqsave(&common->cc_lock, flags);
+       spin_lock_bh(&common->cc_lock);
        if (idx == 0)
                ath_update_survey_stats(sc);
 
@@ -1808,7 +1807,7 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
                sband = hw->wiphy->bands[IEEE80211_BAND_5GHZ];
 
        if (!sband || idx >= sband->n_channels) {
-               spin_unlock_irqrestore(&common->cc_lock, flags);
+               spin_unlock_bh(&common->cc_lock);
                return -ENOENT;
        }
 
@@ -1816,7 +1815,7 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx,
        pos = chan->hw_value;
        memcpy(survey, &sc->survey[pos], sizeof(*survey));
        survey->channel = chan;
-       spin_unlock_irqrestore(&common->cc_lock, flags);
+       spin_unlock_bh(&common->cc_lock);
 
        return 0;
 }
index 0ac1b5f..71799fc 100644 (file)
@@ -200,7 +200,7 @@ skip_tuning:
        if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE)
                btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE;
 
-       btcoex->btcoex_no_stomp =  btcoex->btcoex_period * 1000 *
+       btcoex->btcoex_no_stomp =  btcoex->btcoex_period *
                (100 - btcoex->duty_cycle) / 100;
 
        ath9k_hw_btcoex_enable(sc->sc_ah);
index e9a5857..55724b0 100644 (file)
@@ -412,6 +412,16 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         0x11AD, /* LITEON */
+                        0x06B2),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0842),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
                         0x6671),
          .driver_data = ATH9K_PCI_AR9565_1ANT },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -424,6 +434,16 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
                         0x1B9A, /* XAVI */
                         0x2812),
          .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x1B9A, /* XAVI */
+                        0x28A1),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x218A),
+         .driver_data = ATH9K_PCI_AR9565_1ANT },
 
        /* WB335 1-ANT / Antenna Diversity */
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
@@ -469,22 +489,17 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         0x11AD, /* LITEON */
-                        0x0682),
+                        0x06A2),
          .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
-                        PCI_VENDOR_ID_AZWAVE,
-                        0x213A),
-         .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
-       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
-                        0x0036,
-                        PCI_VENDOR_ID_LENOVO,
-                        0x3026),
+                        0x11AD, /* LITEON */
+                        0x0682),
          .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
-                        PCI_VENDOR_ID_LENOVO,
-                        0x4026),
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x213A),
          .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
@@ -504,37 +519,35 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_DELL,
-                        0x020E),
+                        0x020C),
          .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV },
 
-       /* WB335 2-ANT */
+       /* WB335 2-ANT / Antenna-Diversity */
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_SAMSUNG,
                         0x411A),
-         .driver_data = ATH9K_PCI_AR9565_2ANT },
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_SAMSUNG,
                         0x411B),
-         .driver_data = ATH9K_PCI_AR9565_2ANT },
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_SAMSUNG,
                         0x411C),
-         .driver_data = ATH9K_PCI_AR9565_2ANT },
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_SAMSUNG,
                         0x411D),
-         .driver_data = ATH9K_PCI_AR9565_2ANT },
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_SAMSUNG,
                         0x411E),
-         .driver_data = ATH9K_PCI_AR9565_2ANT },
-
-       /* WB335 2-ANT / Antenna-Diversity */
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
                         PCI_VENDOR_ID_ATHEROS,
@@ -562,11 +575,31 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
          .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
+                        0x11AD, /* LITEON */
+                        0x0832),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        0x11AD, /* LITEON */
+                        0x0692),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
                         PCI_VENDOR_ID_AZWAVE,
                         0x2130),
          .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x213B),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_AZWAVE,
+                        0x2182),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
                         0x144F, /* ASKEY */
                         0x7202),
          .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -577,6 +610,11 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
          .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
        { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
                         0x0036,
+                        0x1B9A, /* XAVI */
+                        0x28A2),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
                         0x185F, /* WNC */
                         0x3027),
          .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
@@ -590,6 +628,31 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = {
                         PCI_VENDOR_ID_FOXCONN,
                         0xE07F),
          .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_FOXCONN,
+                        0xE081),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x3026),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_LENOVO,
+                        0x4026),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_ASUSTEK,
+                        0x85F2),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
+       { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS,
+                        0x0036,
+                        PCI_VENDOR_ID_DELL,
+                        0x020E),
+         .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV },
 
        /* PCI-E AR9565 (WB335) */
        { PCI_VDEVICE(ATHEROS, 0x0036),
index 95ddca5..f7cc5b3 100644 (file)
@@ -15,7 +15,6 @@
  */
 
 #include <linux/dma-mapping.h>
-#include <linux/relay.h>
 #include "ath9k.h"
 #include "ar9003_mac.h"
 
@@ -420,7 +419,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
                rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL;
        }
 
-       if (AR_SREV_9550(sc->sc_ah))
+       if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
                rfilt |= ATH9K_RX_FILTER_4ADDRESS;
 
        return rfilt;
@@ -851,20 +850,15 @@ static int ath9k_process_rate(struct ath_common *common,
        enum ieee80211_band band;
        unsigned int i = 0;
        struct ath_softc __maybe_unused *sc = common->priv;
+       struct ath_hw *ah = sc->sc_ah;
 
-       band = hw->conf.chandef.chan->band;
+       band = ah->curchan->chan->band;
        sband = hw->wiphy->bands[band];
 
-       switch (hw->conf.chandef.width) {
-       case NL80211_CHAN_WIDTH_5:
+       if (IS_CHAN_QUARTER_RATE(ah->curchan))
                rxs->flag |= RX_FLAG_5MHZ;
-               break;
-       case NL80211_CHAN_WIDTH_10:
+       else if (IS_CHAN_HALF_RATE(ah->curchan))
                rxs->flag |= RX_FLAG_10MHZ;
-               break;
-       default:
-               break;
-       }
 
        if (rx_stats->rs_rate & 0x80) {
                /* HT rate */
@@ -906,6 +900,7 @@ static void ath9k_process_rssi(struct ath_common *common,
        struct ath_hw *ah = common->ah;
        int last_rssi;
        int rssi = rx_stats->rs_rssi;
+       int i, j;
 
        /*
         * RSSI is not available for subframes in an A-MPDU.
@@ -924,6 +919,20 @@ static void ath9k_process_rssi(struct ath_common *common,
                return;
        }
 
+       for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) {
+               s8 rssi;
+
+               if (!(ah->rxchainmask & BIT(i)))
+                       continue;
+
+               rssi = rx_stats->rs_rssi_ctl[i];
+               if (rssi != ATH9K_RSSI_BAD) {
+                   rxs->chains |= BIT(j);
+                   rxs->chain_signal[j] = ah->noise + rssi;
+               }
+               j++;
+       }
+
        /*
         * Update Beacon RSSI, this is used by ANI.
         */
@@ -960,186 +969,6 @@ static void ath9k_process_tsf(struct ath_rx_status *rs,
                rxs->mactime += 0x100000000ULL;
 }
 
-#ifdef CONFIG_ATH9K_DEBUGFS
-static s8 fix_rssi_inv_only(u8 rssi_val)
-{
-       if (rssi_val == 128)
-               rssi_val = 0;
-       return (s8) rssi_val;
-}
-#endif
-
-/* returns 1 if this was a spectral frame, even if not handled. */
-static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
-                          struct ath_rx_status *rs, u64 tsf)
-{
-#ifdef CONFIG_ATH9K_DEBUGFS
-       struct ath_hw *ah = sc->sc_ah;
-       u8 num_bins, *bins, *vdata = (u8 *)hdr;
-       struct fft_sample_ht20 fft_sample_20;
-       struct fft_sample_ht20_40 fft_sample_40;
-       struct fft_sample_tlv *tlv;
-       struct ath_radar_info *radar_info;
-       int len = rs->rs_datalen;
-       int dc_pos;
-       u16 fft_len, length, freq = ah->curchan->chan->center_freq;
-       enum nl80211_channel_type chan_type;
-
-       /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
-        * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
-        * yet, but this is supposed to be possible as well.
-        */
-       if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
-           rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
-           rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
-               return 0;
-
-       /* check if spectral scan bit is set. This does not have to be checked
-        * if received through a SPECTRAL phy error, but shouldn't hurt.
-        */
-       radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
-       if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
-               return 0;
-
-       chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
-       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
-           (chan_type == NL80211_CHAN_HT40PLUS)) {
-               fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
-               num_bins = SPECTRAL_HT20_40_NUM_BINS;
-               bins = (u8 *)fft_sample_40.data;
-       } else {
-               fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
-               num_bins = SPECTRAL_HT20_NUM_BINS;
-               bins = (u8 *)fft_sample_20.data;
-       }
-
-       /* Variation in the data length is possible and will be fixed later */
-       if ((len > fft_len + 2) || (len < fft_len - 1))
-               return 1;
-
-       switch (len - fft_len) {
-       case 0:
-               /* length correct, nothing to do. */
-               memcpy(bins, vdata, num_bins);
-               break;
-       case -1:
-               /* first byte missing, duplicate it. */
-               memcpy(&bins[1], vdata, num_bins - 1);
-               bins[0] = vdata[0];
-               break;
-       case 2:
-               /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
-               memcpy(bins, vdata, 30);
-               bins[30] = vdata[31];
-               memcpy(&bins[31], &vdata[33], num_bins - 31);
-               break;
-       case 1:
-               /* MAC added 2 extra bytes AND first byte is missing. */
-               bins[0] = vdata[0];
-               memcpy(&bins[1], vdata, 30);
-               bins[31] = vdata[31];
-               memcpy(&bins[32], &vdata[33], num_bins - 32);
-               break;
-       default:
-               return 1;
-       }
-
-       /* DC value (value in the middle) is the blind spot of the spectral
-        * sample and invalid, interpolate it.
-        */
-       dc_pos = num_bins / 2;
-       bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
-
-       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
-           (chan_type == NL80211_CHAN_HT40PLUS)) {
-               s8 lower_rssi, upper_rssi;
-               s16 ext_nf;
-               u8 lower_max_index, upper_max_index;
-               u8 lower_bitmap_w, upper_bitmap_w;
-               u16 lower_mag, upper_mag;
-               struct ath9k_hw_cal_data *caldata = ah->caldata;
-               struct ath_ht20_40_mag_info *mag_info;
-
-               if (caldata)
-                       ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
-                                       caldata->nfCalHist[3].privNF);
-               else
-                       ext_nf = ATH_DEFAULT_NOISE_FLOOR;
-
-               length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
-               fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
-               fft_sample_40.tlv.length = __cpu_to_be16(length);
-               fft_sample_40.freq = __cpu_to_be16(freq);
-               fft_sample_40.channel_type = chan_type;
-
-               if (chan_type == NL80211_CHAN_HT40PLUS) {
-                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
-                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0);
-
-                       fft_sample_40.lower_noise = ah->noise;
-                       fft_sample_40.upper_noise = ext_nf;
-               } else {
-                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0);
-                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
-
-                       fft_sample_40.lower_noise = ext_nf;
-                       fft_sample_40.upper_noise = ah->noise;
-               }
-               fft_sample_40.lower_rssi = lower_rssi;
-               fft_sample_40.upper_rssi = upper_rssi;
-
-               mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
-               lower_mag = spectral_max_magnitude(mag_info->lower_bins);
-               upper_mag = spectral_max_magnitude(mag_info->upper_bins);
-               fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
-               fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
-               lower_max_index = spectral_max_index(mag_info->lower_bins);
-               upper_max_index = spectral_max_index(mag_info->upper_bins);
-               fft_sample_40.lower_max_index = lower_max_index;
-               fft_sample_40.upper_max_index = upper_max_index;
-               lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
-               upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
-               fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
-               fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
-               fft_sample_40.max_exp = mag_info->max_exp & 0xf;
-
-               fft_sample_40.tsf = __cpu_to_be64(tsf);
-
-               tlv = (struct fft_sample_tlv *)&fft_sample_40;
-       } else {
-               u8 max_index, bitmap_w;
-               u16 magnitude;
-               struct ath_ht20_mag_info *mag_info;
-
-               length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
-               fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
-               fft_sample_20.tlv.length = __cpu_to_be16(length);
-               fft_sample_20.freq = __cpu_to_be16(freq);
-
-               fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0);
-               fft_sample_20.noise = ah->noise;
-
-               mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
-               magnitude = spectral_max_magnitude(mag_info->all_bins);
-               fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
-               max_index = spectral_max_index(mag_info->all_bins);
-               fft_sample_20.max_index = max_index;
-               bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
-               fft_sample_20.bitmap_weight = bitmap_w;
-               fft_sample_20.max_exp = mag_info->max_exp & 0xf;
-
-               fft_sample_20.tsf = __cpu_to_be64(tsf);
-
-               tlv = (struct fft_sample_tlv *)&fft_sample_20;
-       }
-
-       ath_debug_send_fft_sample(sc, tlv);
-       return 1;
-#else
-       return 0;
-#endif
-}
-
 static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr)
 {
        struct ath_hw *ah = sc->sc_ah;
@@ -1148,7 +977,7 @@ static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr)
        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))
+                   ether_addr_equal_64bits(hdr->addr3, common->curbssid))
                        return true;
        }
 
@@ -1243,9 +1072,13 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
        }
 
        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);
+
+       /*
+        * This shouldn't happen, but have a safety check anyway.
+        */
+       if (WARN_ON(!ah->curchan)) {
+               ret = -EINVAL;
+               goto exit;
        }
 
        if (ath9k_process_rate(common, hw, rx_stats, rx_status)) {
@@ -1255,8 +1088,8 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
 
        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->band = ah->curchan->chan->band;
+       rx_status->freq = ah->curchan->chan->center_freq;
        rx_status->antenna = rx_stats->rs_antenna;
        rx_status->flag |= RX_FLAG_MACTIME_END;
 
index 9ad0073..b1fd3fa 100644 (file)
 #define AR_IMR_S2              0x00ac
 #define AR_IMR_S2_QCU_TXURN    0x000003FF
 #define AR_IMR_S2_QCU_TXURN_S  0
+#define AR_IMR_S2_BB_WATCHDOG  0x00010000
 #define AR_IMR_S2_CST          0x00400000
 #define AR_IMR_S2_GTT          0x00800000
 #define AR_IMR_S2_TIM          0x01000000
 #define AR_SREV_REVISION_9565_101       1
 #define AR_SREV_REVISION_9565_11        2
 #define AR_SREV_VERSION_9550           0x400
+#define AR_SREV_VERSION_9531            0x500
+#define AR_SREV_REVISION_9531_10        0
+#define AR_SREV_REVISION_9531_11        1
 
 #define AR_SREV_5416(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \
 #define AR_SREV_9580(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
        ((_ah)->hw_version.macRev >= AR_SREV_REVISION_9580_10))
-
 #define AR_SREV_9580_10(_ah) \
        (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9580) && \
        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9580_10))
 
+#define AR_SREV_9531(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531))
+#define AR_SREV_9531_10(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
+        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_10))
+#define AR_SREV_9531_11(_ah) \
+       (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
+        ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11))
+
 /* NOTE: When adding chips newer than Peacock, add chip check here */
 #define AR_SREV_9580_10_OR_LATER(_ah) \
        (AR_SREV_9580(_ah))
diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/spectral.c
new file mode 100644 (file)
index 0000000..99f4de9
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/relay.h>
+#include "ath9k.h"
+
+static s8 fix_rssi_inv_only(u8 rssi_val)
+{
+       if (rssi_val == 128)
+               rssi_val = 0;
+       return (s8) rssi_val;
+}
+
+static void ath_debug_send_fft_sample(struct ath_softc *sc,
+                                     struct fft_sample_tlv *fft_sample_tlv)
+{
+       int length;
+       if (!sc->rfs_chan_spec_scan)
+               return;
+
+       length = __be16_to_cpu(fft_sample_tlv->length) +
+                sizeof(*fft_sample_tlv);
+       relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
+}
+
+/* returns 1 if this was a spectral frame, even if not handled. */
+int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+                   struct ath_rx_status *rs, u64 tsf)
+{
+       struct ath_hw *ah = sc->sc_ah;
+       u8 num_bins, *bins, *vdata = (u8 *)hdr;
+       struct fft_sample_ht20 fft_sample_20;
+       struct fft_sample_ht20_40 fft_sample_40;
+       struct fft_sample_tlv *tlv;
+       struct ath_radar_info *radar_info;
+       int len = rs->rs_datalen;
+       int dc_pos;
+       u16 fft_len, length, freq = ah->curchan->chan->center_freq;
+       enum nl80211_channel_type chan_type;
+
+       /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
+        * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
+        * yet, but this is supposed to be possible as well.
+        */
+       if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
+           rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
+           rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
+               return 0;
+
+       /* check if spectral scan bit is set. This does not have to be checked
+        * if received through a SPECTRAL phy error, but shouldn't hurt.
+        */
+       radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
+       if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
+               return 0;
+
+       chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
+       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
+           (chan_type == NL80211_CHAN_HT40PLUS)) {
+               fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
+               num_bins = SPECTRAL_HT20_40_NUM_BINS;
+               bins = (u8 *)fft_sample_40.data;
+       } else {
+               fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
+               num_bins = SPECTRAL_HT20_NUM_BINS;
+               bins = (u8 *)fft_sample_20.data;
+       }
+
+       /* Variation in the data length is possible and will be fixed later */
+       if ((len > fft_len + 2) || (len < fft_len - 1))
+               return 1;
+
+       switch (len - fft_len) {
+       case 0:
+               /* length correct, nothing to do. */
+               memcpy(bins, vdata, num_bins);
+               break;
+       case -1:
+               /* first byte missing, duplicate it. */
+               memcpy(&bins[1], vdata, num_bins - 1);
+               bins[0] = vdata[0];
+               break;
+       case 2:
+               /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
+               memcpy(bins, vdata, 30);
+               bins[30] = vdata[31];
+               memcpy(&bins[31], &vdata[33], num_bins - 31);
+               break;
+       case 1:
+               /* MAC added 2 extra bytes AND first byte is missing. */
+               bins[0] = vdata[0];
+               memcpy(&bins[1], vdata, 30);
+               bins[31] = vdata[31];
+               memcpy(&bins[32], &vdata[33], num_bins - 32);
+               break;
+       default:
+               return 1;
+       }
+
+       /* DC value (value in the middle) is the blind spot of the spectral
+        * sample and invalid, interpolate it.
+        */
+       dc_pos = num_bins / 2;
+       bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
+
+       if ((chan_type == NL80211_CHAN_HT40MINUS) ||
+           (chan_type == NL80211_CHAN_HT40PLUS)) {
+               s8 lower_rssi, upper_rssi;
+               s16 ext_nf;
+               u8 lower_max_index, upper_max_index;
+               u8 lower_bitmap_w, upper_bitmap_w;
+               u16 lower_mag, upper_mag;
+               struct ath9k_hw_cal_data *caldata = ah->caldata;
+               struct ath_ht20_40_mag_info *mag_info;
+
+               if (caldata)
+                       ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
+                                       caldata->nfCalHist[3].privNF);
+               else
+                       ext_nf = ATH_DEFAULT_NOISE_FLOOR;
+
+               length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
+               fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
+               fft_sample_40.tlv.length = __cpu_to_be16(length);
+               fft_sample_40.freq = __cpu_to_be16(freq);
+               fft_sample_40.channel_type = chan_type;
+
+               if (chan_type == NL80211_CHAN_HT40PLUS) {
+                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
+                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
+
+                       fft_sample_40.lower_noise = ah->noise;
+                       fft_sample_40.upper_noise = ext_nf;
+               } else {
+                       lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
+                       upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
+
+                       fft_sample_40.lower_noise = ext_nf;
+                       fft_sample_40.upper_noise = ah->noise;
+               }
+               fft_sample_40.lower_rssi = lower_rssi;
+               fft_sample_40.upper_rssi = upper_rssi;
+
+               mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
+               lower_mag = spectral_max_magnitude(mag_info->lower_bins);
+               upper_mag = spectral_max_magnitude(mag_info->upper_bins);
+               fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
+               fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
+               lower_max_index = spectral_max_index(mag_info->lower_bins);
+               upper_max_index = spectral_max_index(mag_info->upper_bins);
+               fft_sample_40.lower_max_index = lower_max_index;
+               fft_sample_40.upper_max_index = upper_max_index;
+               lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
+               upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
+               fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
+               fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
+               fft_sample_40.max_exp = mag_info->max_exp & 0xf;
+
+               fft_sample_40.tsf = __cpu_to_be64(tsf);
+
+               tlv = (struct fft_sample_tlv *)&fft_sample_40;
+       } else {
+               u8 max_index, bitmap_w;
+               u16 magnitude;
+               struct ath_ht20_mag_info *mag_info;
+
+               length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
+               fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
+               fft_sample_20.tlv.length = __cpu_to_be16(length);
+               fft_sample_20.freq = __cpu_to_be16(freq);
+
+               fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
+               fft_sample_20.noise = ah->noise;
+
+               mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
+               magnitude = spectral_max_magnitude(mag_info->all_bins);
+               fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
+               max_index = spectral_max_index(mag_info->all_bins);
+               fft_sample_20.max_index = max_index;
+               bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
+               fft_sample_20.bitmap_weight = bitmap_w;
+               fft_sample_20.max_exp = mag_info->max_exp & 0xf;
+
+               fft_sample_20.tsf = __cpu_to_be64(tsf);
+
+               tlv = (struct fft_sample_tlv *)&fft_sample_20;
+       }
+
+       ath_debug_send_fft_sample(sc, tlv);
+
+       return 1;
+}
+
+/*********************/
+/* spectral_scan_ctl */
+/*********************/
+
+static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char *mode = "";
+       unsigned int len;
+
+       switch (sc->spectral_mode) {
+       case SPECTRAL_DISABLED:
+               mode = "disable";
+               break;
+       case SPECTRAL_BACKGROUND:
+               mode = "background";
+               break;
+       case SPECTRAL_CHANSCAN:
+               mode = "chanscan";
+               break;
+       case SPECTRAL_MANUAL:
+               mode = "manual";
+               break;
+       }
+       len = strlen(mode);
+       return simple_read_from_buffer(user_buf, count, ppos, mode, len);
+}
+
+static ssize_t write_file_spec_scan_ctl(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+       char buf[32];
+       ssize_t len;
+
+       if (config_enabled(CONFIG_ATH9K_TX99))
+               return -EOPNOTSUPP;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+
+       if (strncmp("trigger", buf, 7) == 0) {
+               ath9k_spectral_scan_trigger(sc->hw);
+       } else if (strncmp("background", buf, 9) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
+               ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
+       } else if (strncmp("chanscan", buf, 8) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
+               ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
+       } else if (strncmp("manual", buf, 6) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
+               ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
+       } else if (strncmp("disable", buf, 7) == 0) {
+               ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
+               ath_dbg(common, CONFIG, "spectral scan: disabled\n");
+       } else {
+               return -EINVAL;
+       }
+
+       return count;
+}
+
+static const struct file_operations fops_spec_scan_ctl = {
+       .read = read_file_spec_scan_ctl,
+       .write = write_file_spec_scan_ctl,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+/*************************/
+/* spectral_short_repeat */
+/*************************/
+
+static ssize_t read_file_spectral_short_repeat(struct file *file,
+                                              char __user *user_buf,
+                                              size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_short_repeat(struct file *file,
+                                               const char __user *user_buf,
+                                               size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 1)
+               return -EINVAL;
+
+       sc->spec_config.short_repeat = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_short_repeat = {
+       .read = read_file_spectral_short_repeat,
+       .write = write_file_spectral_short_repeat,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+/******************/
+/* spectral_count */
+/******************/
+
+static ssize_t read_file_spectral_count(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.count);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_count(struct file *file,
+                                        const char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 255)
+               return -EINVAL;
+
+       sc->spec_config.count = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_count = {
+       .read = read_file_spectral_count,
+       .write = write_file_spectral_count,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+/*******************/
+/* spectral_period */
+/*******************/
+
+static ssize_t read_file_spectral_period(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.period);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_period(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 255)
+               return -EINVAL;
+
+       sc->spec_config.period = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_period = {
+       .read = read_file_spectral_period,
+       .write = write_file_spectral_period,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+/***********************/
+/* spectral_fft_period */
+/***********************/
+
+static ssize_t read_file_spectral_fft_period(struct file *file,
+                                            char __user *user_buf,
+                                            size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       char buf[32];
+       unsigned int len;
+
+       len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t write_file_spectral_fft_period(struct file *file,
+                                             const char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct ath_softc *sc = file->private_data;
+       unsigned long val;
+       char buf[32];
+       ssize_t len;
+
+       len = min(count, sizeof(buf) - 1);
+       if (copy_from_user(buf, user_buf, len))
+               return -EFAULT;
+
+       buf[len] = '\0';
+       if (kstrtoul(buf, 0, &val))
+               return -EINVAL;
+
+       if (val < 0 || val > 15)
+               return -EINVAL;
+
+       sc->spec_config.fft_period = val;
+       return count;
+}
+
+static const struct file_operations fops_spectral_fft_period = {
+       .read = read_file_spectral_fft_period,
+       .write = write_file_spectral_fft_period,
+       .open = simple_open,
+       .owner = THIS_MODULE,
+       .llseek = default_llseek,
+};
+
+/*******************/
+/* Relay interface */
+/*******************/
+
+static struct dentry *create_buf_file_handler(const char *filename,
+                                             struct dentry *parent,
+                                             umode_t mode,
+                                             struct rchan_buf *buf,
+                                             int *is_global)
+{
+       struct dentry *buf_file;
+
+       buf_file = debugfs_create_file(filename, mode, parent, buf,
+                                      &relay_file_operations);
+       *is_global = 1;
+       return buf_file;
+}
+
+static int remove_buf_file_handler(struct dentry *dentry)
+{
+       debugfs_remove(dentry);
+
+       return 0;
+}
+
+static struct rchan_callbacks rfs_spec_scan_cb = {
+       .create_buf_file = create_buf_file_handler,
+       .remove_buf_file = remove_buf_file_handler,
+};
+
+/*********************/
+/* Debug Init/Deinit */
+/*********************/
+
+void ath9k_spectral_deinit_debug(struct ath_softc *sc)
+{
+       if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
+               relay_close(sc->rfs_chan_spec_scan);
+               sc->rfs_chan_spec_scan = NULL;
+       }
+}
+
+void ath9k_spectral_init_debug(struct ath_softc *sc)
+{
+       sc->rfs_chan_spec_scan = relay_open("spectral_scan",
+                                           sc->debug.debugfs_phy,
+                                           1024, 256, &rfs_spec_scan_cb,
+                                           NULL);
+       debugfs_create_file("spectral_scan_ctl",
+                           S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spec_scan_ctl);
+       debugfs_create_file("spectral_short_repeat",
+                           S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_short_repeat);
+       debugfs_create_file("spectral_count",
+                           S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_count);
+       debugfs_create_file("spectral_period",
+                           S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_period);
+       debugfs_create_file("spectral_fft_period",
+                           S_IRUSR | S_IWUSR,
+                           sc->debug.debugfs_phy, sc,
+                           &fops_spectral_fft_period);
+}
diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/spectral.h
new file mode 100644 (file)
index 0000000..ead6341
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2013 Qualcomm Atheros, Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SPECTRAL_H
+#define SPECTRAL_H
+
+/* enum spectral_mode:
+ *
+ * @SPECTRAL_DISABLED: spectral mode is disabled
+ * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
+ *     something else.
+ * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
+ *     is performed manually.
+ * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels
+ *     during a channel scan.
+ */
+enum spectral_mode {
+       SPECTRAL_DISABLED = 0,
+       SPECTRAL_BACKGROUND,
+       SPECTRAL_MANUAL,
+       SPECTRAL_CHANSCAN,
+};
+
+#define SPECTRAL_SCAN_BITMASK          0x10
+/* Radar info packet format, used for DFS and spectral formats. */
+struct ath_radar_info {
+       u8 pulse_length_pri;
+       u8 pulse_length_ext;
+       u8 pulse_bw_info;
+} __packed;
+
+/* The HT20 spectral data has 4 bytes of additional information at it's end.
+ *
+ * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]}
+ * [7:0]: all bins  max_magnitude[9:2]
+ * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]}
+ * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
+ */
+struct ath_ht20_mag_info {
+       u8 all_bins[3];
+       u8 max_exp;
+} __packed;
+
+#define SPECTRAL_HT20_NUM_BINS         56
+
+/* WARNING: don't actually use this struct! MAC may vary the amount of
+ * data by -1/+2. This struct is for reference only.
+ */
+struct ath_ht20_fft_packet {
+       u8 data[SPECTRAL_HT20_NUM_BINS];
+       struct ath_ht20_mag_info mag_info;
+       struct ath_radar_info radar_info;
+} __packed;
+
+#define SPECTRAL_HT20_TOTAL_DATA_LEN   (sizeof(struct ath_ht20_fft_packet))
+
+/* Dynamic 20/40 mode:
+ *
+ * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]}
+ * [7:0]: lower bins  max_magnitude[9:2]
+ * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]}
+ * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]}
+ * [7:0]: upper bins  max_magnitude[9:2]
+ * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]}
+ * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
+ */
+struct ath_ht20_40_mag_info {
+       u8 lower_bins[3];
+       u8 upper_bins[3];
+       u8 max_exp;
+} __packed;
+
+#define SPECTRAL_HT20_40_NUM_BINS              128
+
+/* WARNING: don't actually use this struct! MAC may vary the amount of
+ * data. This struct is for reference only.
+ */
+struct ath_ht20_40_fft_packet {
+       u8 data[SPECTRAL_HT20_40_NUM_BINS];
+       struct ath_ht20_40_mag_info mag_info;
+       struct ath_radar_info radar_info;
+} __packed;
+
+
+#define SPECTRAL_HT20_40_TOTAL_DATA_LEN        (sizeof(struct ath_ht20_40_fft_packet))
+
+/* grabs the max magnitude from the all/upper/lower bins */
+static inline u16 spectral_max_magnitude(u8 *bins)
+{
+       return (bins[0] & 0xc0) >> 6 |
+              (bins[1] & 0xff) << 2 |
+              (bins[2] & 0x03) << 10;
+}
+
+/* return the max magnitude from the all/upper/lower bins */
+static inline u8 spectral_max_index(u8 *bins)
+{
+       s8 m = (bins[2] & 0xfc) >> 2;
+
+       /* TODO: this still doesn't always report the right values ... */
+       if (m > 32)
+               m |= 0xe0;
+       else
+               m &= ~0xe0;
+
+       return m + 29;
+}
+
+/* return the bitmap weight from the all/upper/lower bins */
+static inline u8 spectral_bitmap_weight(u8 *bins)
+{
+       return bins[0] & 0x3f;
+}
+
+/* FFT sample format given to userspace via debugfs.
+ *
+ * Please keep the type/length at the front position and change
+ * other fields after adding another sample type
+ *
+ * TODO: this might need rework when switching to nl80211-based
+ * interface.
+ */
+enum ath_fft_sample_type {
+       ATH_FFT_SAMPLE_HT20 = 1,
+       ATH_FFT_SAMPLE_HT20_40,
+};
+
+struct fft_sample_tlv {
+       u8 type;        /* see ath_fft_sample */
+       __be16 length;
+       /* type dependent data follows */
+} __packed;
+
+struct fft_sample_ht20 {
+       struct fft_sample_tlv tlv;
+
+       u8 max_exp;
+
+       __be16 freq;
+       s8 rssi;
+       s8 noise;
+
+       __be16 max_magnitude;
+       u8 max_index;
+       u8 bitmap_weight;
+
+       __be64 tsf;
+
+       u8 data[SPECTRAL_HT20_NUM_BINS];
+} __packed;
+
+struct fft_sample_ht20_40 {
+       struct fft_sample_tlv tlv;
+
+       u8 channel_type;
+       __be16 freq;
+
+       s8 lower_rssi;
+       s8 upper_rssi;
+
+       __be64 tsf;
+
+       s8 lower_noise;
+       s8 upper_noise;
+
+       __be16 lower_max_magnitude;
+       __be16 upper_max_magnitude;
+
+       u8 lower_max_index;
+       u8 upper_max_index;
+
+       u8 lower_bitmap_weight;
+       u8 upper_bitmap_weight;
+
+       u8 max_exp;
+
+       u8 data[SPECTRAL_HT20_40_NUM_BINS];
+} __packed;
+
+void ath9k_spectral_init_debug(struct ath_softc *sc);
+void ath9k_spectral_deinit_debug(struct ath_softc *sc);
+
+void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
+int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
+                              enum spectral_mode spectral_mode);
+
+#ifdef CONFIG_ATH9K_DEBUGFS
+int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
+                   struct ath_rx_status *rs, u64 tsf);
+#else
+static inline int ath_process_fft(struct ath_softc *sc,
+                                 struct ieee80211_hdr *hdr,
+                                 struct ath_rx_status *rs, u64 tsf)
+{
+       return 0;
+}
+#endif /* CONFIG_ATH9K_DEBUGFS */
+
+#endif /* SPECTRAL_H */
index f1cde81..1b3230f 100644 (file)
@@ -197,7 +197,6 @@ int ath9k_suspend(struct ieee80211_hw *hw,
 
        ath_cancel_work(sc);
        ath_stop_ani(sc);
-       del_timer_sync(&sc->rx_poll_timer);
 
        if (test_bit(SC_OP_INVALID, &sc->sc_flags)) {
                ath_dbg(common, ANY, "Device not present\n");
index 24846d9..e8d0e7f 100644 (file)
@@ -174,14 +174,7 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
 static struct ath_atx_tid *
 ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb)
 {
-       struct ieee80211_hdr *hdr;
-       u8 tidno = 0;
-
-       hdr = (struct ieee80211_hdr *) skb->data;
-       if (ieee80211_is_data_qos(hdr->frame_control))
-               tidno = ieee80211_get_qos_ctl(hdr)[0];
-
-       tidno &= IEEE80211_QOS_CTL_TID_MASK;
+       u8 tidno = skb->priority & IEEE80211_QOS_CTL_TID_MASK;
        return ATH_AN_2_TID(an, tidno);
 }
 
@@ -781,11 +774,6 @@ static u32 ath_lookup_rate(struct ath_softc *sc, struct ath_buf *bf,
        if (bt_aggr_limit)
                aggr_limit = bt_aggr_limit;
 
-       /*
-        * h/w can accept aggregates up to 16 bit lengths (65535).
-        * The IE, however can hold up to 65536, which shows up here
-        * as zero. Ignore 65536 since we  are constrained by hw.
-        */
        if (tid->an->maxampdu)
                aggr_limit = min(aggr_limit, tid->an->maxampdu);
 
@@ -1276,6 +1264,10 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf,
                                if (!rts_thresh || (len > rts_thresh))
                                        rts = true;
                        }
+
+                       if (!aggr)
+                               len = fi->framelen;
+
                        ath_buf_set_rate(sc, bf, &info, len, rts);
                }
 
@@ -1406,8 +1398,8 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
         * has already been added.
         */
        if (sta->ht_cap.ht_supported) {
-               an->maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
-                                    sta->ht_cap.ampdu_factor);
+               an->maxampdu = (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+                                     sta->ht_cap.ampdu_factor)) - 1;
                density = ath9k_parse_mpdudensity(sta->ht_cap.ampdu_density);
                an->mpdudensity = density;
        }
index 3d70cd2..1c0af9c 100644 (file)
@@ -37,7 +37,6 @@
  *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/seq_file.h>
index 5ab68fd..4c8cdb0 100644 (file)
@@ -37,7 +37,6 @@
  *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
index e935f61..1b1b207 100644 (file)
@@ -37,7 +37,6 @@
  *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
@@ -536,7 +535,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len)
                return;
 
        /* and only beacons from the associated BSSID, please */
-       if (!ether_addr_equal(hdr->addr3, ar->common.curbssid) ||
+       if (!ether_addr_equal_64bits(hdr->addr3, ar->common.curbssid) ||
            !ar->common.curaid)
                return;
 
@@ -602,8 +601,8 @@ static void carl9170_ba_check(struct ar9170 *ar, void *data, unsigned int len)
 
                if (bar->start_seq_num == entry_bar->start_seq_num &&
                    TID_CHECK(bar->control, entry_bar->control) &&
-                   ether_addr_equal(bar->ra, entry_bar->ta) &&
-                   ether_addr_equal(bar->ta, entry_bar->ra)) {
+                   ether_addr_equal_64bits(bar->ra, entry_bar->ta) &&
+                   ether_addr_equal_64bits(bar->ta, entry_bar->ra)) {
                        struct ieee80211_tx_info *tx_info;
 
                        tx_info = IEEE80211_SKB_CB(entry_skb);
index e3f696e..4cadfd4 100644 (file)
@@ -37,7 +37,6 @@
  *    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/etherdevice.h>
index 8f37562..750626b 100644 (file)
@@ -2060,22 +2060,28 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len)
        case WCN36XX_HAL_OTA_TX_COMPL_IND:
        case WCN36XX_HAL_MISSED_BEACON_IND:
        case WCN36XX_HAL_DELETE_STA_CONTEXT_IND:
-               mutex_lock(&wcn->hal_ind_mutex);
                msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL);
-               if (msg_ind) {
-                       msg_ind->msg_len = len;
-                       msg_ind->msg = kmalloc(len, GFP_KERNEL);
-                       memcpy(msg_ind->msg, buf, len);
-                       list_add_tail(&msg_ind->list, &wcn->hal_ind_queue);
-                       queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work);
-                       wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n");
+               if (!msg_ind)
+                       goto nomem;
+               msg_ind->msg_len = len;
+               msg_ind->msg = kmalloc(len, GFP_KERNEL);
+               if (!msg_ind->msg) {
+                       kfree(msg_ind);
+nomem:
+                       /*
+                        * FIXME: Do something smarter then just
+                        * printing an error.
+                        */
+                       wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n",
+                                   msg_header->msg_type);
+                       break;
                }
+               memcpy(msg_ind->msg, buf, len);
+               mutex_lock(&wcn->hal_ind_mutex);
+               list_add_tail(&msg_ind->list, &wcn->hal_ind_queue);
+               queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work);
                mutex_unlock(&wcn->hal_ind_mutex);
-               if (msg_ind)
-                       break;
-               /* FIXME: Do something smarter then just printing an error. */
-               wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n",
-                           msg_header->msg_type);
+               wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n");
                break;
        default:
                wcn36xx_err("SMD_EVENT (%d) not supported\n",
index 8205d3e..10919f9 100644 (file)
@@ -156,6 +156,19 @@ void wil6210_enable_irq(struct wil6210_priv *wil)
        iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) +
                  offsetof(struct RGF_ICR, ICC));
 
+       /* interrupt moderation parameters */
+       if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) {
+               /* disable interrupt moderation for monitor
+                * to get better timestamp precision
+                */
+               iowrite32(0, wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+       } else {
+               iowrite32(WIL6210_ITR_TRSH,
+                         wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
+               iowrite32(BIT_DMA_ITR_CNT_CRL_EN,
+                         wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL));
+       }
+
        wil6210_unmask_irq_pseudo(wil);
        wil6210_unmask_irq_tx(wil);
        wil6210_unmask_irq_rx(wil);
index d505b26..9b88440 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/ip.h>
 #include <linux/ipv6.h>
 #include <net/ipv6.h>
+#include <asm/processor.h>
 
 #include "wil6210.h"
 #include "wmi.h"
@@ -377,6 +378,8 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil,
        }
        skb_trim(skb, dmalen);
 
+       prefetch(skb->data);
+
        wil_hex_dump_txrx("Rx ", DUMP_PREFIX_OFFSET, 16, 1,
                          skb->data, skb_headlen(skb), false);
 
@@ -673,9 +676,12 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
        if (skb->ip_summed != CHECKSUM_PARTIAL)
                return 0;
 
+       d->dma.b11 = ETH_HLEN; /* MAC header length */
+
        switch (skb->protocol) {
        case cpu_to_be16(ETH_P_IP):
                protocol = ip_hdr(skb)->protocol;
+               d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);
                break;
        case cpu_to_be16(ETH_P_IPV6):
                protocol = ipv6_hdr(skb)->nexthdr;
@@ -701,8 +707,6 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil,
        }
 
        d->dma.ip_length = skb_network_header_len(skb);
-       d->dma.b11 = ETH_HLEN; /* MAC header length */
-       d->dma.b11 |= BIT(DMA_CFG_DESC_TX_OFFLOAD_CFG_L3T_IPV4_POS);
        /* Enable TCP/UDP checksum */
        d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS);
        /* Calculate pseudo-header */
index c4a5163..1f91eaf 100644 (file)
@@ -39,6 +39,7 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1)
 #define WIL6210_MAX_TX_RINGS   (24) /* HW limit */
 #define WIL6210_MAX_CID                (8) /* HW limit */
 #define WIL6210_NAPI_BUDGET    (16) /* arbitrary */
+#define WIL6210_ITR_TRSH       (10000) /* arbitrary - about 15 IRQs/msec */
 
 /* Hardware definitions begin */
 
index b73b7e3..bf93ea8 100644 (file)
@@ -39,7 +39,6 @@
 
 ******************************************************************************/
 
-#include <linux/init.h>
 #include <linux/interrupt.h>
 
 #include <linux/kernel.h>
index 5e2749d..4cfb4d9 100644 (file)
@@ -32,7 +32,6 @@
 #ifdef __IN_PCMCIA_PACKAGE__
 #include <pcmcia/k_compat.h>
 #endif
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/ptrace.h>
index 64d5973..5cd97e3 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/netdevice.h>
 #include "atmel.h"
 
index 2082402..5681b98 100644 (file)
@@ -36,7 +36,6 @@ brcmfmac-objs += \
 brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
                dhd_sdio.o \
                bcmsdh.o \
-               bcmsdh_sdmmc.o \
                sdio_chip.o
 brcmfmac-$(CONFIG_BRCMFMAC_USB) += \
                usb.o
index 06848e4..c229210 100644 (file)
@@ -41,9 +41,6 @@ struct brcmf_proto_bcdc_dcmd {
        __le32 status;  /* status code returned from the device */
 };
 
-/* Max valid buffer size that can be sent to the dongle */
-#define BCDC_MAX_MSG_SIZE      (ETH_FRAME_LEN+ETH_FCS_LEN)
-
 /* BCDC flag definitions */
 #define BCDC_DCMD_ERROR                0x01            /* 1=cmd failed */
 #define BCDC_DCMD_SET          0x02            /* 0=get, 1=set cmd */
@@ -101,32 +98,41 @@ struct brcmf_proto_bcdc_header {
                                         * plus any space that might be needed
                                         * for bus alignment padding.
                                         */
-#define ROUND_UP_MARGIN        2048    /* Biggest bus block size possible for
-                                * round off at the end of buffer
-                                * Currently is SDIO
-                                */
-
 struct brcmf_bcdc {
        u16 reqid;
        u8 bus_header[BUS_HEADER_LEN];
        struct brcmf_proto_bcdc_dcmd msg;
-       unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN];
+       unsigned char buf[BRCMF_DCMD_MAXLEN];
 };
 
-static int brcmf_proto_bcdc_msg(struct brcmf_pub *drvr)
+
+static int
+brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
+                    uint len, bool set)
 {
        struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd;
-       int len = le32_to_cpu(bcdc->msg.len) +
-                       sizeof(struct brcmf_proto_bcdc_dcmd);
+       struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg;
+       u32 flags;
 
        brcmf_dbg(BCDC, "Enter\n");
 
-       /* NOTE : bcdc->msg.len holds the desired length of the buffer to be
-        *        returned. Only up to BCDC_MAX_MSG_SIZE of this buffer area
-        *        is actually sent to the dongle
-        */
-       if (len > BCDC_MAX_MSG_SIZE)
-               len = BCDC_MAX_MSG_SIZE;
+       memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
+
+       msg->cmd = cpu_to_le32(cmd);
+       msg->len = cpu_to_le32(len);
+       flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
+       if (set)
+               flags |= BCDC_DCMD_SET;
+       flags = (flags & ~BCDC_DCMD_IF_MASK) |
+               (ifidx << BCDC_DCMD_IF_SHIFT);
+       msg->flags = cpu_to_le32(flags);
+
+       if (buf)
+               memcpy(bcdc->buf, buf, len);
+
+       len += sizeof(*msg);
+       if (len > BRCMF_TX_IOCTL_MAX_MSG_SIZE)
+               len = BRCMF_TX_IOCTL_MAX_MSG_SIZE;
 
        /* Send request */
        return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len);
@@ -161,19 +167,7 @@ brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
 
        brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
 
-       memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
-
-       msg->cmd = cpu_to_le32(cmd);
-       msg->len = cpu_to_le32(len);
-       flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT);
-       flags = (flags & ~BCDC_DCMD_IF_MASK) |
-               (ifidx << BCDC_DCMD_IF_SHIFT);
-       msg->flags = cpu_to_le32(flags);
-
-       if (buf)
-               memcpy(bcdc->buf, buf, len);
-
-       ret = brcmf_proto_bcdc_msg(drvr);
+       ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false);
        if (ret < 0) {
                brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n",
                          ret);
@@ -227,19 +221,7 @@ brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd,
 
        brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len);
 
-       memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd));
-
-       msg->cmd = cpu_to_le32(cmd);
-       msg->len = cpu_to_le32(len);
-       flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT) | BCDC_DCMD_SET;
-       flags = (flags & ~BCDC_DCMD_IF_MASK) |
-               (ifidx << BCDC_DCMD_IF_SHIFT);
-       msg->flags = cpu_to_le32(flags);
-
-       if (buf)
-               memcpy(bcdc->buf, buf, len);
-
-       ret = brcmf_proto_bcdc_msg(drvr);
+       ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true);
        if (ret < 0)
                goto done;
 
@@ -347,6 +329,15 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
        return 0;
 }
 
+static int
+brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset,
+                       struct sk_buff *pktbuf)
+{
+       brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf);
+       return brcmf_bus_txdata(drvr->bus_if, pktbuf);
+}
+
+
 int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
 {
        struct brcmf_bcdc *bcdc;
@@ -361,15 +352,15 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr)
                goto fail;
        }
 
-       drvr->proto->hdrpush = brcmf_proto_bcdc_hdrpush;
        drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull;
        drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd;
        drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd;
+       drvr->proto->txdata = brcmf_proto_bcdc_txdata;
        drvr->proto->pd = bcdc;
 
        drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES;
        drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN +
-                       sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN;
+                       sizeof(struct brcmf_proto_bcdc_dcmd);
        return 0;
 
 fail:
index 91651ec..34c993d 100644 (file)
 #include <linux/completion.h>
 #include <linux/scatterlist.h>
 #include <linux/mmc/sdio.h>
+#include <linux/mmc/core.h>
 #include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_device.h>
 #include <linux/platform_data/brcmfmac-sdio.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <net/cfg80211.h>
 
 #include <defs.h>
 #include <brcm_hw_ids.h>
 #include "dhd_bus.h"
 #include "dhd_dbg.h"
 #include "sdio_host.h"
+#include "sdio_chip.h"
 
 #define SDIOH_API_ACCESS_RETRY_LIMIT   2
 
+#define DMA_ALIGN_MASK 0x03
 
-static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id)
+#define SDIO_FUNC1_BLOCKSIZE           64
+#define SDIO_FUNC2_BLOCKSIZE           512
+/* Maximum milliseconds to wait for F2 to come up */
+#define SDIO_WAIT_F2RDY        3000
+
+
+static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
@@ -54,27 +70,46 @@ static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id)
                sdiodev->irq_en = false;
        }
 
-       brcmf_sdbrcm_isr(sdiodev->bus);
+       brcmf_sdio_isr(sdiodev->bus);
 
        return IRQ_HANDLED;
 }
 
-static void brcmf_sdio_ib_irqhandler(struct sdio_func *func)
+static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
 
        brcmf_dbg(INTR, "IB intr triggered\n");
 
-       brcmf_sdbrcm_isr(sdiodev->bus);
+       brcmf_sdio_isr(sdiodev->bus);
 }
 
 /* dummy handler for SDIO function 2 interrupt */
-static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func)
+static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
+{
+}
+
+static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
+{
+       bool is_err = false;
+#ifdef CONFIG_PM_SLEEP
+       is_err = atomic_read(&sdiodev->suspend);
+#endif
+       return is_err;
+}
+
+static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
+                                      wait_queue_head_t *wq)
 {
+#ifdef CONFIG_PM_SLEEP
+       int retry = 0;
+       while (atomic_read(&sdiodev->suspend) && retry++ != 30)
+               wait_event_timeout(*wq, false, HZ/100);
+#endif
 }
 
-int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
+int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
 {
        int ret = 0;
        u8 data;
@@ -84,7 +119,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
                brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
                          sdiodev->pdata->oob_irq_nr);
                ret = request_irq(sdiodev->pdata->oob_irq_nr,
-                                 brcmf_sdio_oob_irqhandler,
+                                 brcmf_sdiod_oob_irqhandler,
                                  sdiodev->pdata->oob_irq_flags,
                                  "brcmf_oob_intr",
                                  &sdiodev->func[1]->dev);
@@ -108,36 +143,36 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
                sdio_claim_host(sdiodev->func[1]);
 
                /* must configure SDIO_CCCR_IENx to enable irq */
-               data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
+               data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret);
                data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1;
-               brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
+               brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret);
 
                /* redirect, configure and enable io for interrupt signal */
                data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE;
                if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
                        data |= SDIO_SEPINT_ACT_HI;
-               brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
+               brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret);
 
                sdio_release_host(sdiodev->func[1]);
        } else {
                brcmf_dbg(SDIO, "Entering\n");
                sdio_claim_host(sdiodev->func[1]);
-               sdio_claim_irq(sdiodev->func[1], brcmf_sdio_ib_irqhandler);
-               sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler);
+               sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
+               sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
                sdio_release_host(sdiodev->func[1]);
        }
 
        return 0;
 }
 
-int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
+int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
 {
        brcmf_dbg(SDIO, "Entering\n");
 
        if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) {
                sdio_claim_host(sdiodev->func[1]);
-               brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
-               brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
+               brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
+               brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
                sdio_release_host(sdiodev->func[1]);
 
                if (sdiodev->oob_irq_requested) {
@@ -160,29 +195,141 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev)
        return 0;
 }
 
+static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
+                                       uint regaddr, u8 byte)
+{
+       int err_ret;
+
+       /*
+        * Can only directly write to some F0 registers.
+        * Handle CCCR_IENx and CCCR_ABORT command
+        * as a special case.
+        */
+       if ((regaddr == SDIO_CCCR_ABORT) ||
+           (regaddr == SDIO_CCCR_IENx))
+               sdio_writeb(func, byte, regaddr, &err_ret);
+       else
+               sdio_f0_writeb(func, byte, regaddr, &err_ret);
+
+       return err_ret;
+}
+
+static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn,
+                                   u32 addr, u8 regsz, void *data, bool write)
+{
+       struct sdio_func *func;
+       int ret;
+
+       brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+                 write, fn, addr, regsz);
+
+       brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
+       if (brcmf_sdiod_pm_resume_error(sdiodev))
+               return -EIO;
+
+       /* only allow byte access on F0 */
+       if (WARN_ON(regsz > 1 && !fn))
+               return -EINVAL;
+       func = sdiodev->func[fn];
+
+       switch (regsz) {
+       case sizeof(u8):
+               if (write) {
+                       if (fn)
+                               sdio_writeb(func, *(u8 *)data, addr, &ret);
+                       else
+                               ret = brcmf_sdiod_f0_writeb(func, addr,
+                                                           *(u8 *)data);
+               } else {
+                       if (fn)
+                               *(u8 *)data = sdio_readb(func, addr, &ret);
+                       else
+                               *(u8 *)data = sdio_f0_readb(func, addr, &ret);
+               }
+               break;
+       case sizeof(u16):
+               if (write)
+                       sdio_writew(func, *(u16 *)data, addr, &ret);
+               else
+                       *(u16 *)data = sdio_readw(func, addr, &ret);
+               break;
+       case sizeof(u32):
+               if (write)
+                       sdio_writel(func, *(u32 *)data, addr, &ret);
+               else
+                       *(u32 *)data = sdio_readl(func, addr, &ret);
+               break;
+       default:
+               brcmf_err("invalid size: %d\n", regsz);
+               break;
+       }
+
+       if (ret) {
+               /*
+                * SleepCSR register access can fail when
+                * waking up the device so reduce this noise
+                * in the logs.
+                */
+               if (addr != SBSDIO_FUNC1_SLEEPCSR)
+                       brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
+                                 write ? "write" : "read", fn, addr, ret);
+               else
+                       brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
+                                 write ? "write" : "read", fn, addr, ret);
+       }
+       return ret;
+}
+
+static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
+                                  u8 regsz, void *data, bool write)
+{
+       u8 func_num;
+       s32 retry = 0;
+       int ret;
+
+       /*
+        * figure out how to read the register based on address range
+        * 0x00 ~ 0x7FF: function 0 CCCR and FBR
+        * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
+        * The rest: function 1 silicon backplane core registers
+        */
+       if ((addr & ~REG_F0_REG_MASK) == 0)
+               func_num = SDIO_FUNC_0;
+       else
+               func_num = SDIO_FUNC_1;
+
+       do {
+               if (!write)
+                       memset(data, 0, regsz);
+               /* for retry wait for 1 ms till bus get settled down */
+               if (retry)
+                       usleep_range(1000, 2000);
+               ret = brcmf_sdiod_request_data(sdiodev, func_num, addr, regsz,
+                                              data, write);
+       } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
+
+       if (ret != 0)
+               brcmf_err("failed with %d\n", ret);
+
+       return ret;
+}
+
 static int
-brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
+brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
 {
        int err = 0, i;
        u8 addr[3];
-       s32 retry;
 
        addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
        addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
        addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
 
        for (i = 0; i < 3; i++) {
-               retry = 0;
-               do {
-                       if (retry)
-                               usleep_range(1000, 2000);
-                       err = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE,
-                                       SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW + i,
-                                       &addr[i]);
-               } while (err != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
-
+               err = brcmf_sdiod_regrw_helper(sdiodev,
+                                              SBSDIO_FUNC1_SBADDRLOW + i,
+                                              sizeof(u8), &addr[i], true);
                if (err) {
-                       brcmf_err("failed at addr:0x%0x\n",
+                       brcmf_err("failed at addr: 0x%0x\n",
                                  SBSDIO_FUNC1_SBADDRLOW + i);
                        break;
                }
@@ -192,13 +339,13 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
 }
 
 static int
-brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
+brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
 {
        uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
        int err = 0;
 
        if (bar0 != sdiodev->sbwad) {
-               err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0);
+               err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
                if (err)
                        return err;
 
@@ -213,62 +360,14 @@ brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
        return 0;
 }
 
-int
-brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
-                       void *data, bool write)
-{
-       u8 func_num, reg_size;
-       s32 retry = 0;
-       int ret;
-
-       /*
-        * figure out how to read the register based on address range
-        * 0x00 ~ 0x7FF: function 0 CCCR and FBR
-        * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
-        * The rest: function 1 silicon backplane core registers
-        */
-       if ((addr & ~REG_F0_REG_MASK) == 0) {
-               func_num = SDIO_FUNC_0;
-               reg_size = 1;
-       } else if ((addr & ~REG_F1_MISC_MASK) == 0) {
-               func_num = SDIO_FUNC_1;
-               reg_size = 1;
-       } else {
-               func_num = SDIO_FUNC_1;
-               reg_size = 4;
-
-               ret = brcmf_sdio_addrprep(sdiodev, reg_size, &addr);
-               if (ret)
-                       goto done;
-       }
-
-       do {
-               if (!write)
-                       memset(data, 0, reg_size);
-               if (retry)      /* wait for 1 ms till bus get settled down */
-                       usleep_range(1000, 2000);
-               if (reg_size == 1)
-                       ret = brcmf_sdioh_request_byte(sdiodev, write,
-                                                      func_num, addr, data);
-               else
-                       ret = brcmf_sdioh_request_word(sdiodev, write,
-                                                      func_num, addr, data, 4);
-       } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
-
-done:
-       if (ret != 0)
-               brcmf_err("failed with %d\n", ret);
-
-       return ret;
-}
-
-u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
+u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
 {
        u8 data;
        int retval;
 
        brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
-       retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
+       retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+                                         false);
        brcmf_dbg(SDIO, "data:0x%02x\n", data);
 
        if (ret)
@@ -277,52 +376,62 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
        return data;
 }
 
-u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
+u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
 {
        u32 data;
        int retval;
 
        brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
-       retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
+       retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
+       if (retval)
+               goto done;
+       retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+                                         false);
        brcmf_dbg(SDIO, "data:0x%08x\n", data);
 
+done:
        if (ret)
                *ret = retval;
 
        return data;
 }
 
-void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
+void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
                      u8 data, int *ret)
 {
        int retval;
 
        brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
-       retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
-
+       retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+                                         true);
        if (ret)
                *ret = retval;
 }
 
-void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
+void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
                      u32 data, int *ret)
 {
        int retval;
 
        brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
-       retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
+       retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
+       if (retval)
+               goto done;
+       retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
+                                         true);
 
+done:
        if (ret)
                *ret = retval;
 }
 
-static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
+static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
                             bool write, u32 addr, struct sk_buff *pkt)
 {
        unsigned int req_sz;
 
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-       if (brcmf_pm_resume_error(sdiodev))
+       brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_sdiod_pm_resume_error(sdiodev))
                return -EIO;
 
        /* Single skb use the standard mmc interface */
@@ -345,7 +454,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
 }
 
 /**
- * brcmf_sdio_sglist_rw - SDIO interface function for block data access
+ * brcmf_sdiod_sglist_rw - SDIO interface function for block data access
  * @sdiodev: brcmfmac sdio device
  * @fn: SDIO function number
  * @write: direction flag
@@ -356,9 +465,9 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
  * stack for block data access. It assumes that the skb passed down by the
  * caller has already been padded and aligned.
  */
-static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
-                               bool write, u32 addr,
-                               struct sk_buff_head *pktlist)
+static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
+                                bool write, u32 addr,
+                                struct sk_buff_head *pktlist)
 {
        unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
        unsigned int max_req_sz, orig_offset, dst_offset;
@@ -376,8 +485,8 @@ static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
        if (!pktlist->qlen)
                return -EINVAL;
 
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
-       if (brcmf_pm_resume_error(sdiodev))
+       brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
+       if (brcmf_sdiod_pm_resume_error(sdiodev))
                return -EIO;
 
        target_list = pktlist;
@@ -524,9 +633,7 @@ exit:
        return ret;
 }
 
-int
-brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, u8 *buf, uint nbytes)
+int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
 {
        struct sk_buff *mypkt;
        int err;
@@ -538,7 +645,7 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                return -EIO;
        }
 
-       err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt);
+       err = brcmf_sdiod_recv_pkt(sdiodev, mypkt);
        if (!err)
                memcpy(buf, mypkt->data, nbytes);
 
@@ -546,50 +653,47 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
        return err;
 }
 
-int
-brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, struct sk_buff *pkt)
+int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
 {
-       uint width;
+       u32 addr = sdiodev->sbwad;
        int err = 0;
 
-       brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
-                 fn, addr, pkt->len);
+       brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
 
-       width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+       err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
        if (err)
                goto done;
 
-       err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt);
+       err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt);
 
 done:
        return err;
 }
 
-int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                           uint flags, struct sk_buff_head *pktq, uint totlen)
+int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
+                          struct sk_buff_head *pktq, uint totlen)
 {
        struct sk_buff *glom_skb;
        struct sk_buff *skb;
-       uint width;
+       u32 addr = sdiodev->sbwad;
        int err = 0;
 
-       brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
-                 fn, addr, pktq->qlen);
+       brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
+                 addr, pktq->qlen);
 
-       width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+       err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
        if (err)
                goto done;
 
        if (pktq->qlen == 1)
-               err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next);
+               err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
+                                        pktq->next);
        else if (!sdiodev->sg_support) {
                glom_skb = brcmu_pkt_buf_get_skb(totlen);
                if (!glom_skb)
                        return -ENOMEM;
-               err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb);
+               err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
+                                        glom_skb);
                if (err)
                        goto done;
 
@@ -598,18 +702,17 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
                        skb_pull(glom_skb, skb->len);
                }
        } else
-               err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq);
+               err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr,
+                                           pktq);
 
 done:
        return err;
 }
 
-int
-brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, u8 *buf, uint nbytes)
+int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
 {
        struct sk_buff *mypkt;
-       uint width;
+       u32 addr = sdiodev->sbwad;
        int err;
 
        mypkt = brcmu_pkt_buf_get_skb(nbytes);
@@ -621,48 +724,47 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
 
        memcpy(mypkt->data, buf, nbytes);
 
-       width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+       err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
 
        if (!err)
-               err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt);
+               err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr,
+                                        mypkt);
 
        brcmu_pkt_buf_free_skb(mypkt);
        return err;
 
 }
 
-int
-brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                     uint flags, struct sk_buff_head *pktq)
+int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
+                        struct sk_buff_head *pktq)
 {
        struct sk_buff *skb;
-       uint width;
+       u32 addr = sdiodev->sbwad;
        int err;
 
-       brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
-                 fn, addr, pktq->qlen);
+       brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
 
-       width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
-       err = brcmf_sdio_addrprep(sdiodev, width, &addr);
+       err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
        if (err)
                return err;
 
        if (pktq->qlen == 1 || !sdiodev->sg_support)
                skb_queue_walk(pktq, skb) {
-                       err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb);
+                       err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true,
+                                                addr, skb);
                        if (err)
                                break;
                }
        else
-               err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq);
+               err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr,
+                                           pktq);
 
        return err;
 }
 
 int
-brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
-                u8 *data, uint size)
+brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+                 u8 *data, uint size)
 {
        int bcmerror = 0;
        struct sk_buff *pkt;
@@ -689,7 +791,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        /* Do the transfer(s) */
        while (size) {
                /* Set the backplane window to include the start address */
-               bcmerror = brcmf_sdcard_set_sbaddr_window(sdiodev, address);
+               bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address);
                if (bcmerror)
                        break;
 
@@ -703,8 +805,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
                skb_put(pkt, dsize);
                if (write)
                        memcpy(pkt->data, data, dsize);
-               bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
-                                            sdaddr, pkt);
+               bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write,
+                                             sdaddr, pkt);
                if (bcmerror) {
                        brcmf_err("membytes transfer failed\n");
                        break;
@@ -726,7 +828,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        dev_kfree_skb(pkt);
 
        /* Return the window to backplane enumeration space for core access */
-       if (brcmf_sdcard_set_sbaddr_window(sdiodev, sdiodev->sbwad))
+       if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad))
                brcmf_err("FAILED to set window back to 0x%x\n",
                          sdiodev->sbwad);
 
@@ -735,65 +837,337 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
        return bcmerror;
 }
 
-int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
+int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
 {
        char t_func = (char)fn;
        brcmf_dbg(SDIO, "Enter\n");
 
        /* issue abort cmd52 command through F0 */
-       brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
-                                SDIO_CCCR_ABORT, &t_func);
+       brcmf_sdiod_request_data(sdiodev, SDIO_FUNC_0, SDIO_CCCR_ABORT,
+                                sizeof(t_func), &t_func, true);
 
        brcmf_dbg(SDIO, "Exit\n");
        return 0;
 }
 
-int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
+static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
 {
-       u32 regs = 0;
+       sdiodev->bus_if->state = BRCMF_BUS_DOWN;
+
+       if (sdiodev->bus) {
+               brcmf_sdio_remove(sdiodev->bus);
+               sdiodev->bus = NULL;
+       }
+
+       /* Disable Function 2 */
+       sdio_claim_host(sdiodev->func[2]);
+       sdio_disable_func(sdiodev->func[2]);
+       sdio_release_host(sdiodev->func[2]);
+
+       /* Disable Function 1 */
+       sdio_claim_host(sdiodev->func[1]);
+       sdio_disable_func(sdiodev->func[1]);
+       sdio_release_host(sdiodev->func[1]);
+
+       sdiodev->sbwad = 0;
+
+       return 0;
+}
+
+static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
+{
+       struct sdio_func *func;
+       struct mmc_host *host;
+       uint max_blocks;
        int ret = 0;
 
-       ret = brcmf_sdioh_attach(sdiodev);
-       if (ret)
+       sdiodev->num_funcs = 2;
+
+       sdio_claim_host(sdiodev->func[1]);
+
+       ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
+       if (ret) {
+               brcmf_err("Failed to set F1 blocksize\n");
+               sdio_release_host(sdiodev->func[1]);
+               goto out;
+       }
+       ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
+       if (ret) {
+               brcmf_err("Failed to set F2 blocksize\n");
+               sdio_release_host(sdiodev->func[1]);
+               goto out;
+       }
+
+       /* increase F2 timeout */
+       sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY;
+
+       /* Enable Function 1 */
+       ret = sdio_enable_func(sdiodev->func[1]);
+       sdio_release_host(sdiodev->func[1]);
+       if (ret) {
+               brcmf_err("Failed to enable F1: err=%d\n", ret);
                goto out;
+       }
 
-       regs = SI_ENUM_BASE;
+       /*
+        * determine host related variables after brcmf_sdiod_probe()
+        * as func->cur_blksize is properly set and F2 init has been
+        * completed successfully.
+        */
+       func = sdiodev->func[2];
+       host = func->card->host;
+       sdiodev->sg_support = host->max_segs > 1;
+       max_blocks = min_t(uint, host->max_blk_count, 511u);
+       sdiodev->max_request_size = min_t(uint, host->max_req_size,
+                                         max_blocks * func->cur_blksize);
+       sdiodev->max_segment_count = min_t(uint, host->max_segs,
+                                          SG_MAX_SINGLE_ALLOC);
+       sdiodev->max_segment_size = host->max_seg_size;
 
        /* try to attach to the target device */
-       sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev);
+       sdiodev->bus = brcmf_sdio_probe(sdiodev);
        if (!sdiodev->bus) {
-               brcmf_err("device attach failed\n");
                ret = -ENODEV;
                goto out;
        }
 
 out:
        if (ret)
-               brcmf_sdio_remove(sdiodev);
+               brcmf_sdiod_remove(sdiodev);
 
        return ret;
 }
 
-int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev)
+/* devices we support, null terminated */
+static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)},
+       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
+                    SDIO_DEVICE_ID_BROADCOM_4335_4339)},
+       { /* end: all zeroes */ },
+};
+MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
+
+static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
+
+
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
+                               const struct sdio_device_id *id)
 {
-       sdiodev->bus_if->state = BRCMF_BUS_DOWN;
+       int err;
+       struct brcmf_sdio_dev *sdiodev;
+       struct brcmf_bus *bus_if;
 
-       if (sdiodev->bus) {
-               brcmf_sdbrcm_disconnect(sdiodev->bus);
-               sdiodev->bus = NULL;
+       brcmf_dbg(SDIO, "Enter\n");
+       brcmf_dbg(SDIO, "Class=%x\n", func->class);
+       brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+       brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+       brcmf_dbg(SDIO, "Function#: %d\n", func->num);
+
+       /* Consume func num 1 but dont do anything with it. */
+       if (func->num == 1)
+               return 0;
+
+       /* Ignore anything but func 2 */
+       if (func->num != 2)
+               return -ENODEV;
+
+       bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
+       if (!bus_if)
+               return -ENOMEM;
+       sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
+       if (!sdiodev) {
+               kfree(bus_if);
+               return -ENOMEM;
+       }
+
+       /* store refs to functions used. mmc_card does
+        * not hold the F0 function pointer.
+        */
+       sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL);
+       sdiodev->func[0]->num = 0;
+       sdiodev->func[1] = func->card->sdio_func[0];
+       sdiodev->func[2] = func;
+
+       sdiodev->bus_if = bus_if;
+       bus_if->bus_priv.sdio = sdiodev;
+       bus_if->proto_type = BRCMF_PROTO_BCDC;
+       dev_set_drvdata(&func->dev, bus_if);
+       dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
+       sdiodev->dev = &sdiodev->func[1]->dev;
+       sdiodev->pdata = brcmfmac_sdio_pdata;
+
+       atomic_set(&sdiodev->suspend, false);
+       init_waitqueue_head(&sdiodev->request_word_wait);
+       init_waitqueue_head(&sdiodev->request_buffer_wait);
+
+       brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
+       err = brcmf_sdiod_probe(sdiodev);
+       if (err) {
+               brcmf_err("F2 error, probe failed %d...\n", err);
+               goto fail;
        }
 
-       brcmf_sdioh_detach(sdiodev);
+       brcmf_dbg(SDIO, "F2 init completed...\n");
+       return 0;
 
-       sdiodev->sbwad = 0;
+fail:
+       dev_set_drvdata(&func->dev, NULL);
+       dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+       kfree(sdiodev->func[0]);
+       kfree(sdiodev);
+       kfree(bus_if);
+       return err;
+}
+
+static void brcmf_ops_sdio_remove(struct sdio_func *func)
+{
+       struct brcmf_bus *bus_if;
+       struct brcmf_sdio_dev *sdiodev;
+
+       brcmf_dbg(SDIO, "Enter\n");
+       brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
+       brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
+       brcmf_dbg(SDIO, "Function: %d\n", func->num);
+
+       if (func->num != 1 && func->num != 2)
+               return;
+
+       bus_if = dev_get_drvdata(&func->dev);
+       if (bus_if) {
+               sdiodev = bus_if->bus_priv.sdio;
+               brcmf_sdiod_remove(sdiodev);
+
+               dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
+               dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
+
+               kfree(bus_if);
+               kfree(sdiodev->func[0]);
+               kfree(sdiodev);
+       }
+
+       brcmf_dbg(SDIO, "Exit\n");
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int brcmf_ops_sdio_suspend(struct device *dev)
+{
+       mmc_pm_flag_t sdio_flags;
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+       int ret = 0;
+
+       brcmf_dbg(SDIO, "\n");
+
+       atomic_set(&sdiodev->suspend, true);
+
+       sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
+       if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+               brcmf_err("Host can't keep power while suspended\n");
+               return -EINVAL;
+       }
+
+       ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
+       if (ret) {
+               brcmf_err("Failed to set pm_flags\n");
+               return ret;
+       }
+
+       brcmf_sdio_wd_timer(sdiodev->bus, 0);
+
+       return ret;
+}
+
+static int brcmf_ops_sdio_resume(struct device *dev)
+{
+       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+
+       brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
+       atomic_set(&sdiodev->suspend, false);
+       return 0;
+}
+
+static const struct dev_pm_ops brcmf_sdio_pm_ops = {
+       .suspend        = brcmf_ops_sdio_suspend,
+       .resume         = brcmf_ops_sdio_resume,
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static struct sdio_driver brcmf_sdmmc_driver = {
+       .probe = brcmf_ops_sdio_probe,
+       .remove = brcmf_ops_sdio_remove,
+       .name = BRCMFMAC_SDIO_PDATA_NAME,
+       .id_table = brcmf_sdmmc_ids,
+#ifdef CONFIG_PM_SLEEP
+       .drv = {
+               .pm = &brcmf_sdio_pm_ops,
+       },
+#endif /* CONFIG_PM_SLEEP */
+};
+
+static int brcmf_sdio_pd_probe(struct platform_device *pdev)
+{
+       brcmf_dbg(SDIO, "Enter\n");
+
+       brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
+
+       if (brcmfmac_sdio_pdata->power_on)
+               brcmfmac_sdio_pdata->power_on();
 
        return 0;
 }
 
-void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable)
+static int brcmf_sdio_pd_remove(struct platform_device *pdev)
+{
+       brcmf_dbg(SDIO, "Enter\n");
+
+       if (brcmfmac_sdio_pdata->power_off)
+               brcmfmac_sdio_pdata->power_off();
+
+       sdio_unregister_driver(&brcmf_sdmmc_driver);
+
+       return 0;
+}
+
+static struct platform_driver brcmf_sdio_pd = {
+       .remove         = brcmf_sdio_pd_remove,
+       .driver         = {
+               .name   = BRCMFMAC_SDIO_PDATA_NAME,
+               .owner  = THIS_MODULE,
+       }
+};
+
+void brcmf_sdio_register(void)
+{
+       int ret;
+
+       ret = sdio_register_driver(&brcmf_sdmmc_driver);
+       if (ret)
+               brcmf_err("sdio_register_driver failed: %d\n", ret);
+}
+
+void brcmf_sdio_exit(void)
 {
-       if (enable)
-               brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
+       brcmf_dbg(SDIO, "Enter\n");
+
+       if (brcmfmac_sdio_pdata)
+               platform_driver_unregister(&brcmf_sdio_pd);
        else
-               brcmf_sdbrcm_wd_timer(sdiodev->bus, 0);
+               sdio_unregister_driver(&brcmf_sdmmc_driver);
+}
+
+void __init brcmf_sdio_init(void)
+{
+       int ret;
+
+       brcmf_dbg(SDIO, "Enter\n");
+
+       ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
+       if (ret == -ENODEV)
+               brcmf_dbg(SDIO, "No platform data available.\n");
 }
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
deleted file mode 100644 (file)
index a511c27..0000000
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * Copyright (c) 2010 Broadcom Corporation
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-#include <linux/mmc/sdio.h>
-#include <linux/mmc/core.h>
-#include <linux/mmc/sdio_func.h>
-#include <linux/mmc/sdio_ids.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
-#include <linux/suspend.h>
-#include <linux/errno.h>
-#include <linux/sched.h>       /* request_irq() */
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/brcmfmac-sdio.h>
-#include <net/cfg80211.h>
-
-#include <defs.h>
-#include <brcm_hw_ids.h>
-#include <brcmu_utils.h>
-#include <brcmu_wifi.h>
-#include "sdio_host.h"
-#include "sdio_chip.h"
-#include "dhd_dbg.h"
-#include "dhd_bus.h"
-
-#define SDIO_VENDOR_ID_BROADCOM                0x02d0
-
-#define DMA_ALIGN_MASK 0x03
-
-#define SDIO_FUNC1_BLOCKSIZE           64
-#define SDIO_FUNC2_BLOCKSIZE           512
-
-/* devices we support, null terminated */
-static const struct sdio_device_id brcmf_sdmmc_ids[] = {
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)},
-       {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM,
-                    SDIO_DEVICE_ID_BROADCOM_4335_4339)},
-       { /* end: all zeroes */ },
-};
-MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
-
-static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata;
-
-
-bool
-brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
-{
-       bool is_err = false;
-#ifdef CONFIG_PM_SLEEP
-       is_err = atomic_read(&sdiodev->suspend);
-#endif
-       return is_err;
-}
-
-void
-brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq)
-{
-#ifdef CONFIG_PM_SLEEP
-       int retry = 0;
-       while (atomic_read(&sdiodev->suspend) && retry++ != 30)
-               wait_event_timeout(*wq, false, HZ/100);
-#endif
-}
-
-static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev,
-                                           uint regaddr, u8 *byte)
-{
-       struct sdio_func *sdfunc = sdiodev->func[0];
-       int err_ret;
-
-       /*
-        * Can only directly write to some F0 registers.
-        * Handle F2 enable/disable and Abort command
-        * as a special case.
-        */
-       if (regaddr == SDIO_CCCR_IOEx) {
-               sdfunc = sdiodev->func[2];
-               if (sdfunc) {
-                       if (*byte & SDIO_FUNC_ENABLE_2) {
-                               /* Enable Function 2 */
-                               err_ret = sdio_enable_func(sdfunc);
-                               if (err_ret)
-                                       brcmf_err("enable F2 failed:%d\n",
-                                                 err_ret);
-                       } else {
-                               /* Disable Function 2 */
-                               err_ret = sdio_disable_func(sdfunc);
-                               if (err_ret)
-                                       brcmf_err("Disable F2 failed:%d\n",
-                                                 err_ret);
-                       }
-               } else {
-                       err_ret = -ENOENT;
-               }
-       } else if ((regaddr == SDIO_CCCR_ABORT) ||
-                  (regaddr == SDIO_CCCR_IENx)) {
-               sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func),
-                                GFP_KERNEL);
-               if (!sdfunc)
-                       return -ENOMEM;
-               sdfunc->num = 0;
-               sdio_writeb(sdfunc, *byte, regaddr, &err_ret);
-               kfree(sdfunc);
-       } else if (regaddr < 0xF0) {
-               brcmf_err("F0 Wr:0x%02x: write disallowed\n", regaddr);
-               err_ret = -EPERM;
-       } else {
-               sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret);
-       }
-
-       return err_ret;
-}
-
-int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func,
-                            uint regaddr, u8 *byte)
-{
-       int err_ret;
-
-       brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr);
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       if (rw && func == 0) {
-               /* handle F0 separately */
-               err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte);
-       } else {
-               if (rw) /* CMD52 Write */
-                       sdio_writeb(sdiodev->func[func], *byte, regaddr,
-                                   &err_ret);
-               else if (func == 0) {
-                       *byte = sdio_f0_readb(sdiodev->func[func], regaddr,
-                                             &err_ret);
-               } else {
-                       *byte = sdio_readb(sdiodev->func[func], regaddr,
-                                          &err_ret);
-               }
-       }
-
-       if (err_ret) {
-               /*
-                * SleepCSR register access can fail when
-                * waking up the device so reduce this noise
-                * in the logs.
-                */
-               if (regaddr != SBSDIO_FUNC1_SLEEPCSR)
-                       brcmf_err("Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
-                                 rw ? "write" : "read", func, regaddr, *byte,
-                                 err_ret);
-               else
-                       brcmf_dbg(SDIO, "Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
-                                 rw ? "write" : "read", func, regaddr, *byte,
-                                 err_ret);
-       }
-       return err_ret;
-}
-
-int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev,
-                            uint rw, uint func, uint addr, u32 *word,
-                            uint nbytes)
-{
-       int err_ret = -EIO;
-
-       if (func == 0) {
-               brcmf_err("Only CMD52 allowed to F0\n");
-               return -EINVAL;
-       }
-
-       brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
-                 rw, func, addr, nbytes);
-
-       brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
-       if (brcmf_pm_resume_error(sdiodev))
-               return -EIO;
-
-       if (rw) {               /* CMD52 Write */
-               if (nbytes == 4)
-                       sdio_writel(sdiodev->func[func], *word, addr,
-                                   &err_ret);
-               else if (nbytes == 2)
-                       sdio_writew(sdiodev->func[func], (*word & 0xFFFF),
-                                   addr, &err_ret);
-               else
-                       brcmf_err("Invalid nbytes: %d\n", nbytes);
-       } else {                /* CMD52 Read */
-               if (nbytes == 4)
-                       *word = sdio_readl(sdiodev->func[func], addr, &err_ret);
-               else if (nbytes == 2)
-                       *word = sdio_readw(sdiodev->func[func], addr,
-                                          &err_ret) & 0xFFFF;
-               else
-                       brcmf_err("Invalid nbytes: %d\n", nbytes);
-       }
-
-       if (err_ret)
-               brcmf_err("Failed to %s word, Err: 0x%08x\n",
-                         rw ? "write" : "read", err_ret);
-
-       return err_ret;
-}
-
-static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr)
-{
-       /* read 24 bits and return valid 17 bit addr */
-       int i, ret;
-       u32 scratch, regdata;
-       __le32 scratch_le;
-       u8 *ptr = (u8 *)&scratch_le;
-
-       for (i = 0; i < 3; i++) {
-               regdata = brcmf_sdio_regrl(sdiodev, regaddr, &ret);
-               if (ret != 0)
-                       brcmf_err("Can't read!\n");
-
-               *ptr++ = (u8) regdata;
-               regaddr++;
-       }
-
-       /* Only the lower 17-bits are valid */
-       scratch = le32_to_cpu(scratch_le);
-       scratch &= 0x0001FFFF;
-       return scratch;
-}
-
-static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev)
-{
-       int err_ret;
-       u32 fbraddr;
-       u8 func;
-
-       brcmf_dbg(SDIO, "\n");
-
-       /* Get the Card's common CIS address */
-       sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev,
-                                                          SDIO_CCCR_CIS);
-       brcmf_dbg(SDIO, "Card's Common CIS Ptr = 0x%x\n",
-                 sdiodev->func_cis_ptr[0]);
-
-       /* Get the Card's function CIS (for each function) */
-       for (fbraddr = SDIO_FBR_BASE(1), func = 1;
-            func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
-               sdiodev->func_cis_ptr[func] =
-                   brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr);
-               brcmf_dbg(SDIO, "Function %d CIS Ptr = 0x%x\n",
-                         func, sdiodev->func_cis_ptr[func]);
-       }
-
-       /* Enable Function 1 */
-       err_ret = sdio_enable_func(sdiodev->func[1]);
-       if (err_ret)
-               brcmf_err("Failed to enable F1 Err: 0x%08x\n", err_ret);
-
-       return false;
-}
-
-/*
- *     Public entry points & extern's
- */
-int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev)
-{
-       int err_ret = 0;
-       struct mmc_host *host;
-       struct sdio_func *func;
-       uint max_blocks;
-
-       brcmf_dbg(SDIO, "\n");
-
-       sdiodev->num_funcs = 2;
-
-       sdio_claim_host(sdiodev->func[1]);
-
-       err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
-       if (err_ret) {
-               brcmf_err("Failed to set F1 blocksize\n");
-               goto out;
-       }
-
-       err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
-       if (err_ret) {
-               brcmf_err("Failed to set F2 blocksize\n");
-               goto out;
-       }
-
-       brcmf_sdioh_enablefuncs(sdiodev);
-
-       /*
-        * determine host related variables after brcmf_sdio_probe()
-        * as func->cur_blksize is properly set and F2 init has been
-        * completed successfully.
-        */
-       func = sdiodev->func[2];
-       host = func->card->host;
-       sdiodev->sg_support = host->max_segs > 1;
-       max_blocks = min_t(uint, host->max_blk_count, 511u);
-       sdiodev->max_request_size = min_t(uint, host->max_req_size,
-                                         max_blocks * func->cur_blksize);
-       sdiodev->max_segment_count = min_t(uint, host->max_segs,
-                                          SG_MAX_SINGLE_ALLOC);
-       sdiodev->max_segment_size = host->max_seg_size;
-out:
-       sdio_release_host(sdiodev->func[1]);
-       brcmf_dbg(SDIO, "Done\n");
-       return err_ret;
-}
-
-void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev)
-{
-       brcmf_dbg(SDIO, "\n");
-
-       /* Disable Function 2 */
-       sdio_claim_host(sdiodev->func[2]);
-       sdio_disable_func(sdiodev->func[2]);
-       sdio_release_host(sdiodev->func[2]);
-
-       /* Disable Function 1 */
-       sdio_claim_host(sdiodev->func[1]);
-       sdio_disable_func(sdiodev->func[1]);
-       sdio_release_host(sdiodev->func[1]);
-
-}
-
-static int brcmf_ops_sdio_probe(struct sdio_func *func,
-                               const struct sdio_device_id *id)
-{
-       int err;
-       struct brcmf_sdio_dev *sdiodev;
-       struct brcmf_bus *bus_if;
-
-       brcmf_dbg(SDIO, "Enter\n");
-       brcmf_dbg(SDIO, "Class=%x\n", func->class);
-       brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
-       brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
-       brcmf_dbg(SDIO, "Function#: %d\n", func->num);
-
-       /* Consume func num 1 but dont do anything with it. */
-       if (func->num == 1)
-               return 0;
-
-       /* Ignore anything but func 2 */
-       if (func->num != 2)
-               return -ENODEV;
-
-       bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
-       if (!bus_if)
-               return -ENOMEM;
-       sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
-       if (!sdiodev) {
-               kfree(bus_if);
-               return -ENOMEM;
-       }
-
-       sdiodev->func[0] = func->card->sdio_func[0];
-       sdiodev->func[1] = func->card->sdio_func[0];
-       sdiodev->func[2] = func;
-
-       sdiodev->bus_if = bus_if;
-       bus_if->bus_priv.sdio = sdiodev;
-       dev_set_drvdata(&func->dev, bus_if);
-       dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
-       sdiodev->dev = &sdiodev->func[1]->dev;
-       sdiodev->pdata = brcmfmac_sdio_pdata;
-
-       atomic_set(&sdiodev->suspend, false);
-       init_waitqueue_head(&sdiodev->request_byte_wait);
-       init_waitqueue_head(&sdiodev->request_word_wait);
-       init_waitqueue_head(&sdiodev->request_buffer_wait);
-
-       brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n");
-       err = brcmf_sdio_probe(sdiodev);
-       if (err) {
-               brcmf_err("F2 error, probe failed %d...\n", err);
-               goto fail;
-       }
-
-       brcmf_dbg(SDIO, "F2 init completed...\n");
-       return 0;
-
-fail:
-       dev_set_drvdata(&func->dev, NULL);
-       dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
-       kfree(sdiodev);
-       kfree(bus_if);
-       return err;
-}
-
-static void brcmf_ops_sdio_remove(struct sdio_func *func)
-{
-       struct brcmf_bus *bus_if;
-       struct brcmf_sdio_dev *sdiodev;
-
-       brcmf_dbg(SDIO, "Enter\n");
-       brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
-       brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
-       brcmf_dbg(SDIO, "Function: %d\n", func->num);
-
-       if (func->num != 1 && func->num != 2)
-               return;
-
-       bus_if = dev_get_drvdata(&func->dev);
-       if (bus_if) {
-               sdiodev = bus_if->bus_priv.sdio;
-               brcmf_sdio_remove(sdiodev);
-
-               dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
-               dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
-
-               kfree(bus_if);
-               kfree(sdiodev);
-       }
-
-       brcmf_dbg(SDIO, "Exit\n");
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int brcmf_sdio_suspend(struct device *dev)
-{
-       mmc_pm_flag_t sdio_flags;
-       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-       int ret = 0;
-
-       brcmf_dbg(SDIO, "\n");
-
-       atomic_set(&sdiodev->suspend, true);
-
-       sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
-       if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
-               brcmf_err("Host can't keep power while suspended\n");
-               return -EINVAL;
-       }
-
-       ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
-       if (ret) {
-               brcmf_err("Failed to set pm_flags\n");
-               return ret;
-       }
-
-       brcmf_sdio_wdtmr_enable(sdiodev, false);
-
-       return ret;
-}
-
-static int brcmf_sdio_resume(struct device *dev)
-{
-       struct brcmf_bus *bus_if = dev_get_drvdata(dev);
-       struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
-
-       brcmf_sdio_wdtmr_enable(sdiodev, true);
-       atomic_set(&sdiodev->suspend, false);
-       return 0;
-}
-
-static const struct dev_pm_ops brcmf_sdio_pm_ops = {
-       .suspend        = brcmf_sdio_suspend,
-       .resume         = brcmf_sdio_resume,
-};
-#endif /* CONFIG_PM_SLEEP */
-
-static struct sdio_driver brcmf_sdmmc_driver = {
-       .probe = brcmf_ops_sdio_probe,
-       .remove = brcmf_ops_sdio_remove,
-       .name = BRCMFMAC_SDIO_PDATA_NAME,
-       .id_table = brcmf_sdmmc_ids,
-#ifdef CONFIG_PM_SLEEP
-       .drv = {
-               .pm = &brcmf_sdio_pm_ops,
-       },
-#endif /* CONFIG_PM_SLEEP */
-};
-
-static int brcmf_sdio_pd_probe(struct platform_device *pdev)
-{
-       brcmf_dbg(SDIO, "Enter\n");
-
-       brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev);
-
-       if (brcmfmac_sdio_pdata->power_on)
-               brcmfmac_sdio_pdata->power_on();
-
-       return 0;
-}
-
-static int brcmf_sdio_pd_remove(struct platform_device *pdev)
-{
-       brcmf_dbg(SDIO, "Enter\n");
-
-       if (brcmfmac_sdio_pdata->power_off)
-               brcmfmac_sdio_pdata->power_off();
-
-       sdio_unregister_driver(&brcmf_sdmmc_driver);
-
-       return 0;
-}
-
-static struct platform_driver brcmf_sdio_pd = {
-       .remove         = brcmf_sdio_pd_remove,
-       .driver         = {
-               .name   = BRCMFMAC_SDIO_PDATA_NAME,
-               .owner  = THIS_MODULE,
-       }
-};
-
-void brcmf_sdio_register(void)
-{
-       int ret;
-
-       ret = sdio_register_driver(&brcmf_sdmmc_driver);
-       if (ret)
-               brcmf_err("sdio_register_driver failed: %d\n", ret);
-}
-
-void brcmf_sdio_exit(void)
-{
-       brcmf_dbg(SDIO, "Enter\n");
-
-       if (brcmfmac_sdio_pdata)
-               platform_driver_unregister(&brcmf_sdio_pd);
-       else
-               sdio_unregister_driver(&brcmf_sdmmc_driver);
-}
-
-void __init brcmf_sdio_init(void)
-{
-       int ret;
-
-       brcmf_dbg(SDIO, "Enter\n");
-
-       ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe);
-       if (ret == -ENODEV)
-               brcmf_dbg(SDIO, "No platform data available.\n");
-}
index 252024b..939d6b1 100644 (file)
@@ -21,8 +21,6 @@
 #ifndef _BRCMF_H_
 #define _BRCMF_H_
 
-#define BRCMF_VERSION_STR              "4.218.248.5"
-
 #include "fweh.h"
 
 #define TOE_TX_CSUM_OL         0x00000001
 #define BRCMF_DCMD_MEDLEN      1536
 #define BRCMF_DCMD_MAXLEN      8192
 
+/* IOCTL from host to device are limited in lenght. A device can only handle
+ * ethernet frame size. This limitation is to be applied by protocol layer.
+ */
+#define BRCMF_TX_IOCTL_MAX_MSG_SIZE    (ETH_FRAME_LEN+ETH_FCS_LEN)
+
 #define BRCMF_AMPDU_RX_REORDER_MAXFLOWS                256
 
 /* Length of firmware version string stored for
index 6a54905..5c12a07 100644 (file)
@@ -24,6 +24,12 @@ enum brcmf_bus_state {
        BRCMF_BUS_DATA          /* Ready for frame transfers */
 };
 
+/* The level of bus communication with the dongle */
+enum brcmf_bus_protocol_type {
+       BRCMF_PROTO_BCDC,
+       BRCMF_PROTO_MSGBUF
+};
+
 struct brcmf_bus_dcmd {
        char *name;
        char *param;
@@ -65,6 +71,7 @@ struct brcmf_bus_ops {
  * struct brcmf_bus - interface structure between common and bus layer
  *
  * @bus_priv: pointer to private bus device.
+ * @proto_type: protocol type, bcdc or msgbuf
  * @dev: device pointer of bus device.
  * @drvr: public driver information.
  * @state: operational state of the bus interface.
@@ -80,6 +87,7 @@ struct brcmf_bus {
                struct brcmf_sdio_dev *sdio;
                struct brcmf_usbdev *usb;
        } bus_priv;
+       enum brcmf_bus_protocol_type proto_type;
        struct device *dev;
        struct brcmf_pub *drvr;
        enum brcmf_bus_state state;
index 548dbb5..6a8983a 100644 (file)
 #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME        40
 #define BRCMF_DEFAULT_PACKET_FILTER    "100 0 0 0 0x01 0x00"
 
-#ifdef DEBUG
-static const char brcmf_version[] =
-       "Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on "
-       __DATE__ " at " __TIME__;
-#else
-static const char brcmf_version[] =
-       "Dongle Host Driver, version " BRCMF_VERSION_STR;
-#endif
-
 
 bool brcmf_c_prec_enq(struct device *dev, struct pktq *q,
                      struct sk_buff *pkt, int prec)
index bce0b8e..af39eda 100644 (file)
@@ -702,7 +702,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked)
 
        brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);
 
-       ndev->destructor = free_netdev;
+       ndev->destructor = brcmf_cfg80211_free_netdev;
        return 0;
 
 fail:
@@ -859,8 +859,6 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
                }
                /* unregister will take care of freeing it */
                unregister_netdev(ifp->ndev);
-               if (bssidx == 0)
-                       brcmf_cfg80211_detach(drvr->config);
        } else {
                kfree(ifp);
        }
@@ -963,8 +961,7 @@ int brcmf_bus_start(struct device *dev)
 fail:
        if (ret < 0) {
                brcmf_err("failed: %d\n", ret);
-               if (drvr->config)
-                       brcmf_cfg80211_detach(drvr->config);
+               brcmf_cfg80211_detach(drvr->config);
                if (drvr->fws) {
                        brcmf_fws_del_interface(ifp);
                        brcmf_fws_deinit(drvr);
@@ -1039,6 +1036,8 @@ void brcmf_detach(struct device *dev)
                        brcmf_del_if(drvr, i);
                }
 
+       brcmf_cfg80211_detach(drvr->config);
+
        brcmf_bus_detach(drvr);
 
        brcmf_proto_detach(drvr);
index 0f95f3e..9c7f08a 100644 (file)
@@ -260,9 +260,6 @@ struct rte_console {
 #define MAX_HDR_READ   (1 << 6)
 #define MAX_RX_DATASZ  2048
 
-/* Maximum milliseconds to wait for F2 to come up */
-#define BRCMF_WAIT_F2RDY       3000
-
 /* Bump up limit on waiting for HT to account for first startup;
  * if the image is doing a CRC calculation before programming the PMU
  * for HT availability, it could take a couple hundred ms more, so
@@ -512,6 +509,8 @@ enum brcmf_sdio_frmtype {
 #define BCM4334_NVRAM_NAME             "brcm/brcmfmac4334-sdio.txt"
 #define BCM4335_FIRMWARE_NAME          "brcm/brcmfmac4335-sdio.bin"
 #define BCM4335_NVRAM_NAME             "brcm/brcmfmac4335-sdio.txt"
+#define BCM43362_FIRMWARE_NAME         "brcm/brcmfmac43362-sdio.bin"
+#define BCM43362_NVRAM_NAME            "brcm/brcmfmac43362-sdio.txt"
 #define BCM4339_FIRMWARE_NAME          "brcm/brcmfmac4339-sdio.bin"
 #define BCM4339_NVRAM_NAME             "brcm/brcmfmac4339-sdio.txt"
 
@@ -529,6 +528,8 @@ MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4334_NVRAM_NAME);
 MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4335_NVRAM_NAME);
+MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME);
+MODULE_FIRMWARE(BCM43362_NVRAM_NAME);
 MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME);
 MODULE_FIRMWARE(BCM4339_NVRAM_NAME);
 
@@ -555,11 +556,12 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = {
        { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) },
        { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) },
        { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) },
+       { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) },
        { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }
 };
 
 
-static const struct firmware *brcmf_sdbrcm_get_fw(struct brcmf_sdio *bus,
+static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus,
                                                  enum brcmf_firmware_type type)
 {
        const struct firmware *fw;
@@ -624,8 +626,8 @@ r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)
        u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
        int ret;
 
-       *regvar = brcmf_sdio_regrl(bus->sdiodev,
-                                  bus->ci->c_inf[idx].base + offset, &ret);
+       *regvar = brcmf_sdiod_regrl(bus->sdiodev,
+                                   bus->ci->c_inf[idx].base + offset, &ret);
 
        return ret;
 }
@@ -636,15 +638,15 @@ w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)
        u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
        int ret;
 
-       brcmf_sdio_regwl(bus->sdiodev,
-                        bus->ci->c_inf[idx].base + reg_offset,
-                        regval, &ret);
+       brcmf_sdiod_regwl(bus->sdiodev,
+                         bus->ci->c_inf[idx].base + reg_offset,
+                         regval, &ret);
 
        return ret;
 }
 
 static int
-brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on)
+brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)
 {
        u8 wr_val = 0, rd_val, cmp_val, bmask;
        int err = 0;
@@ -654,8 +656,8 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on)
 
        wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
        /* 1st KSO write goes to AOS wake up core if device is asleep  */
-       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
-                        wr_val, &err);
+       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+                         wr_val, &err);
        if (err) {
                brcmf_err("SDIO_AOS KSO write error: %d\n", err);
                return err;
@@ -685,15 +687,15 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on)
                 * just one write attempt may fail,
                 * read it back until it matches written value
                 */
-               rd_val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
-                                         &err);
+               rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+                                          &err);
                if (((rd_val & bmask) == cmp_val) && !err)
                        break;
                brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n",
                          try_cnt, MAX_KSO_ATTEMPTS, err);
                udelay(KSO_WAIT_US);
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
-                                wr_val, &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+                                 wr_val, &err);
        } while (try_cnt++ < MAX_KSO_ATTEMPTS);
 
        return err;
@@ -704,7 +706,7 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on)
 #define HOSTINTMASK            (I_HMB_SW_MASK | I_CHIPACTIVE)
 
 /* Turn backplane clock on or off */
-static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
+static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 {
        int err;
        u8 clkctl, clkreq, devctl;
@@ -724,16 +726,16 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
                clkreq =
                    bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
 
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                clkreq, &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 clkreq, &err);
                if (err) {
                        brcmf_err("HT Avail request error: %d\n", err);
                        return -EBADE;
                }
 
                /* Check current status */
-               clkctl = brcmf_sdio_regrb(bus->sdiodev,
-                                         SBSDIO_FUNC1_CHIPCLKCSR, &err);
+               clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                          SBSDIO_FUNC1_CHIPCLKCSR, &err);
                if (err) {
                        brcmf_err("HT Avail read error: %d\n", err);
                        return -EBADE;
@@ -742,8 +744,8 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
                /* Go to pending and await interrupt if appropriate */
                if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
                        /* Allow only clock-available interrupt */
-                       devctl = brcmf_sdio_regrb(bus->sdiodev,
-                                                 SBSDIO_DEVICE_CTL, &err);
+                       devctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                                  SBSDIO_DEVICE_CTL, &err);
                        if (err) {
                                brcmf_err("Devctl error setting CA: %d\n",
                                          err);
@@ -751,28 +753,28 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
                        }
 
                        devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
-                       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
-                                        devctl, &err);
+                       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+                                         devctl, &err);
                        brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");
                        bus->clkstate = CLK_PENDING;
 
                        return 0;
                } else if (bus->clkstate == CLK_PENDING) {
                        /* Cancel CA-only interrupt filter */
-                       devctl = brcmf_sdio_regrb(bus->sdiodev,
-                                                 SBSDIO_DEVICE_CTL, &err);
+                       devctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                                  SBSDIO_DEVICE_CTL, &err);
                        devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
-                       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
-                                        devctl, &err);
+                       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+                                         devctl, &err);
                }
 
                /* Otherwise, wait here (polling) for HT Avail */
                timeout = jiffies +
                          msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
                while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
-                       clkctl = brcmf_sdio_regrb(bus->sdiodev,
-                                                 SBSDIO_FUNC1_CHIPCLKCSR,
-                                                 &err);
+                       clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                                  SBSDIO_FUNC1_CHIPCLKCSR,
+                                                  &err);
                        if (time_after(jiffies, timeout))
                                break;
                        else
@@ -805,16 +807,16 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 
                if (bus->clkstate == CLK_PENDING) {
                        /* Cancel CA-only interrupt filter */
-                       devctl = brcmf_sdio_regrb(bus->sdiodev,
-                                                 SBSDIO_DEVICE_CTL, &err);
+                       devctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                                  SBSDIO_DEVICE_CTL, &err);
                        devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
-                       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
-                                        devctl, &err);
+                       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+                                         devctl, &err);
                }
 
                bus->clkstate = CLK_SDONLY;
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                clkreq, &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 clkreq, &err);
                brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");
                if (err) {
                        brcmf_err("Failed access turning clock off: %d\n",
@@ -826,7 +828,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
 }
 
 /* Change idle/active SD state */
-static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on)
+static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)
 {
        brcmf_dbg(SDIO, "Enter\n");
 
@@ -839,7 +841,7 @@ static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on)
 }
 
 /* Transition SD and backplane clock readiness */
-static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
+static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
 {
 #ifdef DEBUG
        uint oldstate = bus->clkstate;
@@ -850,7 +852,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
        /* Early exit if we're already there */
        if (bus->clkstate == target) {
                if (target == CLK_AVAIL) {
-                       brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+                       brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
                        bus->activity = true;
                }
                return 0;
@@ -860,32 +862,32 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
        case CLK_AVAIL:
                /* Make sure SD clock is available */
                if (bus->clkstate == CLK_NONE)
-                       brcmf_sdbrcm_sdclk(bus, true);
+                       brcmf_sdio_sdclk(bus, true);
                /* Now request HT Avail on the backplane */
-               brcmf_sdbrcm_htclk(bus, true, pendok);
-               brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+               brcmf_sdio_htclk(bus, true, pendok);
+               brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
                bus->activity = true;
                break;
 
        case CLK_SDONLY:
                /* Remove HT request, or bring up SD clock */
                if (bus->clkstate == CLK_NONE)
-                       brcmf_sdbrcm_sdclk(bus, true);
+                       brcmf_sdio_sdclk(bus, true);
                else if (bus->clkstate == CLK_AVAIL)
-                       brcmf_sdbrcm_htclk(bus, false, false);
+                       brcmf_sdio_htclk(bus, false, false);
                else
                        brcmf_err("request for %d -> %d\n",
                                  bus->clkstate, target);
-               brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+               brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
                break;
 
        case CLK_NONE:
                /* Make sure to remove HT request */
                if (bus->clkstate == CLK_AVAIL)
-                       brcmf_sdbrcm_htclk(bus, false, false);
+                       brcmf_sdio_htclk(bus, false, false);
                /* Now remove the SD clock */
-               brcmf_sdbrcm_sdclk(bus, false);
-               brcmf_sdbrcm_wd_timer(bus, 0);
+               brcmf_sdio_sdclk(bus, false);
+               brcmf_sdio_wd_timer(bus, 0);
                break;
        }
 #ifdef DEBUG
@@ -896,7 +898,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
 }
 
 static int
-brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
+brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
 {
        int err = 0;
        brcmf_dbg(TRACE, "Enter\n");
@@ -919,13 +921,13 @@ brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)
                            brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
                            data_ok(bus)))
                                 return -EBUSY;
-                       err = brcmf_sdbrcm_kso_control(bus, false);
+                       err = brcmf_sdio_kso_control(bus, false);
                        /* disable watchdog */
                        if (!err)
-                               brcmf_sdbrcm_wd_timer(bus, 0);
+                               brcmf_sdio_wd_timer(bus, 0);
                } else {
                        bus->idlecount = 0;
-                       err = brcmf_sdbrcm_kso_control(bus, true);
+                       err = brcmf_sdio_kso_control(bus, true);
                }
                if (!err) {
                        /* Change state */
@@ -943,16 +945,16 @@ end:
        /* control clocks */
        if (sleep) {
                if (!bus->sr_enabled)
-                       brcmf_sdbrcm_clkctl(bus, CLK_NONE, pendok);
+                       brcmf_sdio_clkctl(bus, CLK_NONE, pendok);
        } else {
-               brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, pendok);
+               brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);
        }
 
        return err;
 
 }
 
-static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
+static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
 {
        u32 intstatus = 0;
        u32 hmb_data;
@@ -1028,7 +1030,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
        return intstatus;
 }
 
-static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
+static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
 {
        uint retries = 0;
        u16 lastrbc;
@@ -1040,18 +1042,18 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
                  rtx ? ", send NAK" : "");
 
        if (abort)
-               brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+               brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
 
-       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
-                        SFC_RF_TERM, &err);
+       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
+                         SFC_RF_TERM, &err);
        bus->sdcnt.f1regdata++;
 
        /* Wait until the packet has been flushed (device/FIFO stable) */
        for (lastrbc = retries = 0xffff; retries > 0; retries--) {
-               hi = brcmf_sdio_regrb(bus->sdiodev,
-                                     SBSDIO_FUNC1_RFRAMEBCHI, &err);
-               lo = brcmf_sdio_regrb(bus->sdiodev,
-                                     SBSDIO_FUNC1_RFRAMEBCLO, &err);
+               hi = brcmf_sdiod_regrb(bus->sdiodev,
+                                      SBSDIO_FUNC1_RFRAMEBCHI, &err);
+               lo = brcmf_sdiod_regrb(bus->sdiodev,
+                                      SBSDIO_FUNC1_RFRAMEBCLO, &err);
                bus->sdcnt.f1regdata += 2;
 
                if ((hi == 0) && (lo == 0))
@@ -1088,7 +1090,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
 }
 
 /* return total length of buffer chain */
-static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)
+static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)
 {
        struct sk_buff *p;
        uint total;
@@ -1099,7 +1101,7 @@ static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)
        return total;
 }
 
-static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)
+static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)
 {
        struct sk_buff *cur, *next;
 
@@ -1187,7 +1189,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
        if ((u16)(~(len ^ checksum))) {
                brcmf_err("HW header checksum error\n");
                bus->sdcnt.rx_badhdr++;
-               brcmf_sdbrcm_rxfail(bus, false, false);
+               brcmf_sdio_rxfail(bus, false, false);
                return -EIO;
        }
        if (len < SDPCM_HDRLEN) {
@@ -1219,7 +1221,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
            type != BRCMF_SDIO_FT_SUPER) {
                brcmf_err("HW header length too long\n");
                bus->sdcnt.rx_toolong++;
-               brcmf_sdbrcm_rxfail(bus, false, false);
+               brcmf_sdio_rxfail(bus, false, false);
                rd->len = 0;
                return -EPROTO;
        }
@@ -1238,7 +1240,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
        if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
                brcmf_err("seq %d: bad data offset\n", rx_seq);
                bus->sdcnt.rx_badhdr++;
-               brcmf_sdbrcm_rxfail(bus, false, false);
+               brcmf_sdio_rxfail(bus, false, false);
                rd->len = 0;
                return -ENXIO;
        }
@@ -1311,7 +1313,7 @@ static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,
        trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);
 }
 
-static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
+static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)
 {
        u16 dlen, totlen;
        u8 *dptr, num = 0;
@@ -1391,7 +1393,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        }
                        pfirst = pnext = NULL;
                } else {
-                       brcmf_sdbrcm_free_glom(bus);
+                       brcmf_sdio_free_glom(bus);
                        num = 0;
                }
 
@@ -1414,16 +1416,15 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                }
 
                pfirst = skb_peek(&bus->glom);
-               dlen = (u16) brcmf_sdbrcm_glom_len(bus);
+               dlen = (u16) brcmf_sdio_glom_len(bus);
 
                /* Do an SDIO read for the superframe.  Configurable iovar to
                 * read directly into the chained packet, or allocate a large
                 * packet and and copy into the chain.
                 */
                sdio_claim_host(bus->sdiodev->func[1]);
-               errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
-                               bus->sdiodev->sbwad,
-                               SDIO_FUNC_2, F2SYNC, &bus->glom, dlen);
+               errcode = brcmf_sdiod_recv_chain(bus->sdiodev,
+                                                &bus->glom, dlen);
                sdio_release_host(bus->sdiodev->func[1]);
                bus->sdcnt.f2rxdata++;
 
@@ -1434,12 +1435,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
 
                        sdio_claim_host(bus->sdiodev->func[1]);
                        if (bus->glomerr++ < 3) {
-                               brcmf_sdbrcm_rxfail(bus, true, true);
+                               brcmf_sdio_rxfail(bus, true, true);
                        } else {
                                bus->glomerr = 0;
-                               brcmf_sdbrcm_rxfail(bus, true, false);
+                               brcmf_sdio_rxfail(bus, true, false);
                                bus->sdcnt.rxglomfail++;
-                               brcmf_sdbrcm_free_glom(bus);
+                               brcmf_sdio_free_glom(bus);
                        }
                        sdio_release_host(bus->sdiodev->func[1]);
                        return 0;
@@ -1487,12 +1488,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
                        if (bus->glomerr++ < 3) {
                                /* Restore superframe header space */
                                skb_push(pfirst, sfdoff);
-                               brcmf_sdbrcm_rxfail(bus, true, true);
+                               brcmf_sdio_rxfail(bus, true, true);
                        } else {
                                bus->glomerr = 0;
-                               brcmf_sdbrcm_rxfail(bus, true, false);
+                               brcmf_sdio_rxfail(bus, true, false);
                                bus->sdcnt.rxglomfail++;
-                               brcmf_sdbrcm_free_glom(bus);
+                               brcmf_sdio_free_glom(bus);
                        }
                        sdio_release_host(bus->sdiodev->func[1]);
                        bus->cur_read.len = 0;
@@ -1536,8 +1537,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
        return num;
 }
 
-static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
-                                       bool *pending)
+static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
+                                    bool *pending)
 {
        DECLARE_WAITQUEUE(wait, current);
        int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT);
@@ -1558,7 +1559,7 @@ static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
        return timeout;
 }
 
-static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus)
+static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)
 {
        if (waitqueue_active(&bus->dcmd_resp_wait))
                wake_up_interruptible(&bus->dcmd_resp_wait);
@@ -1566,7 +1567,7 @@ static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus)
        return 0;
 }
 static void
-brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
+brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
 {
        uint rdlen, pad;
        u8 *buf = NULL, *rbuf;
@@ -1604,7 +1605,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
        if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
                brcmf_err("%d-byte control read exceeds %d-byte buffer\n",
                          rdlen, bus->sdiodev->bus_if->maxctl);
-               brcmf_sdbrcm_rxfail(bus, false, false);
+               brcmf_sdio_rxfail(bus, false, false);
                goto done;
        }
 
@@ -1612,15 +1613,12 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
                brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
                          len, len - doff, bus->sdiodev->bus_if->maxctl);
                bus->sdcnt.rx_toolong++;
-               brcmf_sdbrcm_rxfail(bus, false, false);
+               brcmf_sdio_rxfail(bus, false, false);
                goto done;
        }
 
        /* Read remain of frame body */
-       sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
-                               bus->sdiodev->sbwad,
-                               SDIO_FUNC_2,
-                               F2SYNC, rbuf, rdlen);
+       sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);
        bus->sdcnt.f2rxdata++;
 
        /* Control frame failures need retransmission */
@@ -1628,7 +1626,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
                brcmf_err("read %d control bytes failed: %d\n",
                          rdlen, sdret);
                bus->sdcnt.rxc_errors++;
-               brcmf_sdbrcm_rxfail(bus, true, true);
+               brcmf_sdio_rxfail(bus, true, true);
                goto done;
        } else
                memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen);
@@ -1653,11 +1651,11 @@ gotpkt:
 
 done:
        /* Awake any waiters */
-       brcmf_sdbrcm_dcmd_resp_wake(bus);
+       brcmf_sdio_dcmd_resp_wake(bus);
 }
 
 /* Pad read to blocksize for efficiency */
-static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
+static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
 {
        if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
                *pad = bus->blocksize - (*rdlen % bus->blocksize);
@@ -1694,7 +1692,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                        u8 cnt;
                        brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
                                  bus->glomd, skb_peek(&bus->glom));
-                       cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num);
+                       cnt = brcmf_sdio_rxglom(bus, rd->seq_num);
                        brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
                        rd->seq_num += cnt - 1;
                        rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
@@ -1705,17 +1703,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                /* read header first for unknow frame length */
                sdio_claim_host(bus->sdiodev->func[1]);
                if (!rd->len) {
-                       ret = brcmf_sdcard_recv_buf(bus->sdiodev,
-                                                     bus->sdiodev->sbwad,
-                                                     SDIO_FUNC_2, F2SYNC,
-                                                     bus->rxhdr,
-                                                     BRCMF_FIRSTREAD);
+                       ret = brcmf_sdiod_recv_buf(bus->sdiodev,
+                                                  bus->rxhdr, BRCMF_FIRSTREAD);
                        bus->sdcnt.f2rxhdrs++;
                        if (ret < 0) {
                                brcmf_err("RXHEADER FAILED: %d\n",
                                          ret);
                                bus->sdcnt.rx_hdrfail++;
-                               brcmf_sdbrcm_rxfail(bus, true, true);
+                               brcmf_sdio_rxfail(bus, true, true);
                                sdio_release_host(bus->sdiodev->func[1]);
                                continue;
                        }
@@ -1734,9 +1729,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                        }
 
                        if (rd->channel == SDPCM_CONTROL_CHANNEL) {
-                               brcmf_sdbrcm_read_control(bus, bus->rxhdr,
-                                                         rd->len,
-                                                         rd->dat_offset);
+                               brcmf_sdio_read_control(bus, bus->rxhdr,
+                                                       rd->len,
+                                                       rd->dat_offset);
                                /* prepare the descriptor for the next read */
                                rd->len = rd->len_nxtfrm << 4;
                                rd->len_nxtfrm = 0;
@@ -1750,14 +1745,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                        head_read = BRCMF_FIRSTREAD;
                }
 
-               brcmf_pad(bus, &pad, &rd->len_left);
+               brcmf_sdio_pad(bus, &pad, &rd->len_left);
 
                pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read +
                                            bus->head_align);
                if (!pkt) {
                        /* Give up on data, request rtx of events */
                        brcmf_err("brcmu_pkt_buf_get_skb failed\n");
-                       brcmf_sdbrcm_rxfail(bus, false,
+                       brcmf_sdio_rxfail(bus, false,
                                            RETRYCHAN(rd->channel));
                        sdio_release_host(bus->sdiodev->func[1]);
                        continue;
@@ -1765,8 +1760,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                skb_pull(pkt, head_read);
                pkt_align(pkt, rd->len_left, bus->head_align);
 
-               ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
-                                             SDIO_FUNC_2, F2SYNC, pkt);
+               ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);
                bus->sdcnt.f2rxdata++;
                sdio_release_host(bus->sdiodev->func[1]);
 
@@ -1775,7 +1769,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                                  rd->len, rd->channel, ret);
                        brcmu_pkt_buf_free_skb(pkt);
                        sdio_claim_host(bus->sdiodev->func[1]);
-                       brcmf_sdbrcm_rxfail(bus, true,
+                       brcmf_sdio_rxfail(bus, true,
                                            RETRYCHAN(rd->channel));
                        sdio_release_host(bus->sdiodev->func[1]);
                        continue;
@@ -1800,7 +1794,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                                          rd->len,
                                          roundup(rd_new.len, 16) >> 4);
                                rd->len = 0;
-                               brcmf_sdbrcm_rxfail(bus, true, true);
+                               brcmf_sdio_rxfail(bus, true, true);
                                sdio_release_host(bus->sdiodev->func[1]);
                                brcmu_pkt_buf_free_skb(pkt);
                                continue;
@@ -1822,7 +1816,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                                /* Force retry w/normal header read */
                                rd->len = 0;
                                sdio_claim_host(bus->sdiodev->func[1]);
-                               brcmf_sdbrcm_rxfail(bus, false, true);
+                               brcmf_sdio_rxfail(bus, false, true);
                                sdio_release_host(bus->sdiodev->func[1]);
                                brcmu_pkt_buf_free_skb(pkt);
                                continue;
@@ -1847,7 +1841,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
                                brcmf_err("%s: glom superframe w/o "
                                          "descriptor!\n", __func__);
                                sdio_claim_host(bus->sdiodev->func[1]);
-                               brcmf_sdbrcm_rxfail(bus, false, false);
+                               brcmf_sdio_rxfail(bus, false, false);
                                sdio_release_host(bus->sdiodev->func[1]);
                        }
                        /* prepare the descriptor for the next read */
@@ -1891,7 +1885,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
 }
 
 static void
-brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
+brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)
 {
        if (waitqueue_active(&bus->ctrl_wait))
                wake_up_interruptible(&bus->ctrl_wait);
@@ -2107,8 +2101,8 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)
 
 /* 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_head *pktq,
-                             uint chan)
+static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
+                           uint chan)
 {
        int ret;
        int i;
@@ -2121,8 +2115,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
                goto done;
 
        sdio_claim_host(bus->sdiodev->func[1]);
-       ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad,
-                                   SDIO_FUNC_2, F2SYNC, pktq);
+       ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);
        bus->sdcnt.f2txdata++;
 
        if (ret < 0) {
@@ -2131,17 +2124,17 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq,
                          ret);
                bus->sdcnt.tx_sderrs++;
 
-               brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
-                                SFC_WF_TERM, NULL);
+               brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
+                                 SFC_WF_TERM, NULL);
                bus->sdcnt.f1regdata++;
 
                for (i = 0; i < 3; i++) {
                        u8 hi, lo;
-                       hi = brcmf_sdio_regrb(bus->sdiodev,
-                                             SBSDIO_FUNC1_WFRAMEBCHI, NULL);
-                       lo = brcmf_sdio_regrb(bus->sdiodev,
-                                             SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+                       hi = brcmf_sdiod_regrb(bus->sdiodev,
+                                              SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+                       lo = brcmf_sdiod_regrb(bus->sdiodev,
+                                              SBSDIO_FUNC1_WFRAMEBCLO, NULL);
                        bus->sdcnt.f1regdata += 2;
                        if ((hi == 0) && (lo == 0))
                                break;
@@ -2160,7 +2153,7 @@ done:
        return ret;
 }
 
-static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
+static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)
 {
        struct sk_buff *pkt;
        struct sk_buff_head pktq;
@@ -2194,7 +2187,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
                if (i == 0)
                        break;
 
-               ret = brcmf_sdbrcm_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
+               ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL);
                cnt += i;
 
                /* In poll mode, need to check for other events */
@@ -2223,7 +2216,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
        return cnt;
 }
 
-static void brcmf_sdbrcm_bus_stop(struct device *dev)
+static void brcmf_sdio_bus_stop(struct device *dev)
 {
        u32 local_hostintmask;
        u8 saveclk;
@@ -2243,7 +2236,7 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
        sdio_claim_host(bus->sdiodev->func[1]);
 
        /* Enable clock for device interrupts */
-       brcmf_sdbrcm_bus_sleep(bus, false, false);
+       brcmf_sdio_bus_sleep(bus, false, false);
 
        /* Disable and clear interrupts at the chip level also */
        w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask));
@@ -2254,26 +2247,25 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
        bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
 
        /* Force clocks on backplane to be sure F2 interrupt propagates */
-       saveclk = brcmf_sdio_regrb(bus->sdiodev,
-                                  SBSDIO_FUNC1_CHIPCLKCSR, &err);
+       saveclk = brcmf_sdiod_regrb(bus->sdiodev,
+                                   SBSDIO_FUNC1_CHIPCLKCSR, &err);
        if (!err) {
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                (saveclk | SBSDIO_FORCE_HT), &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 (saveclk | SBSDIO_FORCE_HT), &err);
        }
        if (err)
                brcmf_err("Failed to force clock for F2: err %d\n", err);
 
        /* Turn off the bus (F2), free any pending packets */
        brcmf_dbg(INTR, "disable SDIO interrupts\n");
-       brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1,
-                        NULL);
+       sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
 
        /* Clear any pending interrupts now that F2 is disabled */
        w_sdreg32(bus, local_hostintmask,
                  offsetof(struct sdpcmd_regs, intstatus));
 
        /* Turn off the backplane clock (only) */
-       brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+       brcmf_sdio_clkctl(bus, CLK_SDONLY, false);
        sdio_release_host(bus->sdiodev->func[1]);
 
        /* Clear the data packet queues */
@@ -2282,20 +2274,20 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)
        /* Clear any held glomming stuff */
        if (bus->glomd)
                brcmu_pkt_buf_free_skb(bus->glomd);
-       brcmf_sdbrcm_free_glom(bus);
+       brcmf_sdio_free_glom(bus);
 
        /* Clear rx control and wake any waiters */
        spin_lock_bh(&bus->rxctl_lock);
        bus->rxlen = 0;
        spin_unlock_bh(&bus->rxctl_lock);
-       brcmf_sdbrcm_dcmd_resp_wake(bus);
+       brcmf_sdio_dcmd_resp_wake(bus);
 
        /* Reset some F2 state stuff */
        bus->rxskip = false;
        bus->tx_seq = bus->rx_seq = 0;
 }
 
-static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)
+static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)
 {
        unsigned long flags;
 
@@ -2320,7 +2312,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
        addr = bus->ci->c_inf[idx].base +
               offsetof(struct sdpcmd_regs, intstatus);
 
-       ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false);
+       val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);
        bus->sdcnt.f1regdata++;
        if (ret != 0)
                val = 0;
@@ -2330,7 +2322,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
 
        /* Clear interrupts */
        if (val) {
-               ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true);
+               brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);
                bus->sdcnt.f1regdata++;
        }
 
@@ -2344,7 +2336,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)
        return ret;
 }
 
-static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
+static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
 {
        u32 newstatus = 0;
        unsigned long intstatus;
@@ -2363,8 +2355,8 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
 
 #ifdef DEBUG
                /* Check for inconsistent device control */
-               devctl = brcmf_sdio_regrb(bus->sdiodev,
-                                         SBSDIO_DEVICE_CTL, &err);
+               devctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                          SBSDIO_DEVICE_CTL, &err);
                if (err) {
                        brcmf_err("error reading DEVCTL: %d\n", err);
                        bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
@@ -2372,8 +2364,8 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
 #endif                         /* DEBUG */
 
                /* Read CSR, if clock on switch to AVAIL, else ignore */
-               clkctl = brcmf_sdio_regrb(bus->sdiodev,
-                                         SBSDIO_FUNC1_CHIPCLKCSR, &err);
+               clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                          SBSDIO_FUNC1_CHIPCLKCSR, &err);
                if (err) {
                        brcmf_err("error reading CSR: %d\n",
                                  err);
@@ -2384,16 +2376,16 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                          devctl, clkctl);
 
                if (SBSDIO_HTAV(clkctl)) {
-                       devctl = brcmf_sdio_regrb(bus->sdiodev,
-                                                 SBSDIO_DEVICE_CTL, &err);
+                       devctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                                  SBSDIO_DEVICE_CTL, &err);
                        if (err) {
                                brcmf_err("error reading DEVCTL: %d\n",
                                          err);
                                bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
                        }
                        devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
-                       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
-                                        devctl, &err);
+                       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL,
+                                         devctl, &err);
                        if (err) {
                                brcmf_err("error writing DEVCTL: %d\n",
                                          err);
@@ -2404,7 +2396,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
        }
 
        /* Make sure backplane clock is on */
-       brcmf_sdbrcm_bus_sleep(bus, false, true);
+       brcmf_sdio_bus_sleep(bus, false, true);
 
        /* Pending interrupt indicates new device status */
        if (atomic_read(&bus->ipend) > 0) {
@@ -2435,7 +2427,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
        /* Handle host mailbox indication */
        if (intstatus & I_HMB_HOST_INT) {
                intstatus &= ~I_HMB_HOST_INT;
-               intstatus |= brcmf_sdbrcm_hostmail(bus);
+               intstatus |= brcmf_sdio_hostmail(bus);
        }
 
        sdio_release_host(bus->sdiodev->func[1]);
@@ -2480,16 +2472,15 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                        set_bit(n, (unsigned long *)&bus->intstatus.counter);
        }
 
-       brcmf_sdbrcm_clrintr(bus);
+       brcmf_sdio_clrintr(bus);
 
        if (data_ok(bus) && bus->ctrl_frame_stat &&
                (bus->clkstate == CLK_AVAIL)) {
                int i;
 
                sdio_claim_host(bus->sdiodev->func[1]);
-               err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad,
-                       SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf,
-                       (u32) bus->ctrl_frame_len);
+               err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf,
+                                          (u32)bus->ctrl_frame_len);
 
                if (err < 0) {
                        /* On failure, abort the command and
@@ -2498,20 +2489,20 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                                  err);
                        bus->sdcnt.tx_sderrs++;
 
-                       brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+                       brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
 
-                       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
-                                        SFC_WF_TERM, &err);
+                       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
+                                         SFC_WF_TERM, &err);
                        bus->sdcnt.f1regdata++;
 
                        for (i = 0; i < 3; i++) {
                                u8 hi, lo;
-                               hi = brcmf_sdio_regrb(bus->sdiodev,
-                                                     SBSDIO_FUNC1_WFRAMEBCHI,
-                                                     &err);
-                               lo = brcmf_sdio_regrb(bus->sdiodev,
-                                                     SBSDIO_FUNC1_WFRAMEBCLO,
-                                                     &err);
+                               hi = brcmf_sdiod_regrb(bus->sdiodev,
+                                                      SBSDIO_FUNC1_WFRAMEBCHI,
+                                                      &err);
+                               lo = brcmf_sdiod_regrb(bus->sdiodev,
+                                                      SBSDIO_FUNC1_WFRAMEBCLO,
+                                                      &err);
                                bus->sdcnt.f1regdata += 2;
                                if ((hi == 0) && (lo == 0))
                                        break;
@@ -2522,7 +2513,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                }
                sdio_release_host(bus->sdiodev->func[1]);
                bus->ctrl_frame_stat = false;
-               brcmf_sdbrcm_wait_event_wakeup(bus);
+               brcmf_sdio_wait_event_wakeup(bus);
        }
        /* Send queued frames (limit 1 if rx may still be pending) */
        else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) &&
@@ -2530,7 +2521,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                 && data_ok(bus)) {
                framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :
                                            txlimit;
-               framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt);
+               framecnt = brcmf_sdio_sendfromq(bus, framecnt);
                txlimit -= framecnt;
        }
 
@@ -2552,12 +2543,12 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
                bus->activity = false;
                brcmf_dbg(SDIO, "idle state\n");
                sdio_claim_host(bus->sdiodev->func[1]);
-               brcmf_sdbrcm_bus_sleep(bus, true, false);
+               brcmf_sdio_bus_sleep(bus, true, false);
                sdio_release_host(bus->sdiodev->func[1]);
        }
 }
 
-static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev)
+static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
@@ -2566,7 +2557,7 @@ static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev)
        return &bus->txq;
 }
 
-static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
+static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
 {
        int ret = -EBADE;
        uint datalen, prec;
@@ -2622,7 +2613,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
 #ifdef DEBUG
 #define CONSOLE_LINE_MAX       192
 
-static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)
+static int brcmf_sdio_readconsole(struct brcmf_sdio *bus)
 {
        struct brcmf_console *c = &bus->console;
        u8 line[CONSOLE_LINE_MAX], ch;
@@ -2635,8 +2626,8 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)
 
        /* Read console log struct */
        addr = bus->console_addr + offsetof(struct rte_console, log_le);
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
-                             sizeof(c->log_le));
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le,
+                              sizeof(c->log_le));
        if (rv < 0)
                return rv;
 
@@ -2661,7 +2652,7 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)
 
        /* Read the console buffer */
        addr = le32_to_cpu(c->log_le.buf);
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);
        if (rv < 0)
                return rv;
 
@@ -2699,14 +2690,13 @@ break2:
 }
 #endif                         /* DEBUG */
 
-static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
+static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
 {
        int i;
        int ret;
 
        bus->ctrl_frame_stat = false;
-       ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad,
-                                   SDIO_FUNC_2, F2SYNC, frame, len);
+       ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len);
 
        if (ret < 0) {
                /* On failure, abort the command and terminate the frame */
@@ -2714,18 +2704,18 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
                          ret);
                bus->sdcnt.tx_sderrs++;
 
-               brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
+               brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2);
 
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
-                                SFC_WF_TERM, NULL);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL,
+                                 SFC_WF_TERM, NULL);
                bus->sdcnt.f1regdata++;
 
                for (i = 0; i < 3; i++) {
                        u8 hi, lo;
-                       hi = brcmf_sdio_regrb(bus->sdiodev,
-                                             SBSDIO_FUNC1_WFRAMEBCHI, NULL);
-                       lo = brcmf_sdio_regrb(bus->sdiodev,
-                                             SBSDIO_FUNC1_WFRAMEBCLO, NULL);
+                       hi = brcmf_sdiod_regrb(bus->sdiodev,
+                                              SBSDIO_FUNC1_WFRAMEBCHI, NULL);
+                       lo = brcmf_sdiod_regrb(bus->sdiodev,
+                                              SBSDIO_FUNC1_WFRAMEBCLO, NULL);
                        bus->sdcnt.f1regdata += 2;
                        if (hi == 0 && lo == 0)
                                break;
@@ -2739,7 +2729,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
 }
 
 static int
-brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
+brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 {
        u8 *frame;
        u16 len, pad;
@@ -2783,7 +2773,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 
        /* Make sure backplane clock is on */
        sdio_claim_host(bus->sdiodev->func[1]);
-       brcmf_sdbrcm_bus_sleep(bus, false, false);
+       brcmf_sdio_bus_sleep(bus, false, false);
        sdio_release_host(bus->sdiodev->func[1]);
 
        hd_info.len = (u16)msglen;
@@ -2827,7 +2817,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
 
                do {
                        sdio_claim_host(bus->sdiodev->func[1]);
-                       ret = brcmf_tx_frame(bus, frame, len);
+                       ret = brcmf_sdio_tx_frame(bus, frame, len);
                        sdio_release_host(bus->sdiodev->func[1]);
                } while (ret < 0 && retries++ < TXRETRIES);
        }
@@ -2837,7 +2827,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
                bus->activity = false;
                sdio_claim_host(bus->sdiodev->func[1]);
                brcmf_dbg(INFO, "idle\n");
-               brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+               brcmf_sdio_clkctl(bus, CLK_NONE, true);
                sdio_release_host(bus->sdiodev->func[1]);
        }
 
@@ -2871,8 +2861,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
         * address of sdpcm_shared structure
         */
        sdio_claim_host(bus->sdiodev->func[1]);
-       brcmf_sdbrcm_bus_sleep(bus, false, false);
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
+       brcmf_sdio_bus_sleep(bus, false, false);
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4);
        sdio_release_host(bus->sdiodev->func[1]);
        if (rv < 0)
                return rv;
@@ -2892,8 +2882,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus,
        }
 
        /* Read hndrte_shared structure */
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
-                             sizeof(struct sdpcm_shared_le));
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le,
+                              sizeof(struct sdpcm_shared_le));
        if (rv < 0)
                return rv;
 
@@ -2929,22 +2919,22 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
 
        /* obtain console information from device memory */
        addr = sh->console_addr + offsetof(struct rte_console, log_le);
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr,
-                             (u8 *)&sh_val, sizeof(u32));
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+                              (u8 *)&sh_val, sizeof(u32));
        if (rv < 0)
                return rv;
        console_ptr = le32_to_cpu(sh_val);
 
        addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size);
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr,
-                             (u8 *)&sh_val, sizeof(u32));
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+                              (u8 *)&sh_val, sizeof(u32));
        if (rv < 0)
                return rv;
        console_size = le32_to_cpu(sh_val);
 
        addr = sh->console_addr + offsetof(struct rte_console, log_le.idx);
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr,
-                             (u8 *)&sh_val, sizeof(u32));
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr,
+                              (u8 *)&sh_val, sizeof(u32));
        if (rv < 0)
                return rv;
        console_index = le32_to_cpu(sh_val);
@@ -2958,8 +2948,8 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,
 
        /* obtain the console data from device */
        conbuf[console_size] = '\0';
-       rv = brcmf_sdio_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
-                             console_size);
+       rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf,
+                              console_size);
        if (rv < 0)
                goto done;
 
@@ -2996,8 +2986,8 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,
                return 0;
        }
 
-       error = brcmf_sdio_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
-                                sizeof(struct brcmf_trap_info));
+       error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr,
+                                 sizeof(struct brcmf_trap_info));
        if (error < 0)
                return error;
 
@@ -3040,14 +3030,14 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
 
        sdio_claim_host(bus->sdiodev->func[1]);
        if (sh->assert_file_addr != 0) {
-               error = brcmf_sdio_ramrw(bus->sdiodev, false,
-                                        sh->assert_file_addr, (u8 *)file, 80);
+               error = brcmf_sdiod_ramrw(bus->sdiodev, false,
+                                         sh->assert_file_addr, (u8 *)file, 80);
                if (error < 0)
                        return error;
        }
        if (sh->assert_exp_addr != 0) {
-               error = brcmf_sdio_ramrw(bus->sdiodev, false,
-                                        sh->assert_exp_addr, (u8 *)expr, 80);
+               error = brcmf_sdiod_ramrw(bus->sdiodev, false,
+                                         sh->assert_exp_addr, (u8 *)expr, 80);
                if (error < 0)
                        return error;
        }
@@ -3059,7 +3049,7 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,
        return simple_read_from_buffer(data, count, &pos, buf, res);
 }
 
-static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
+static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
 {
        int error;
        struct sdpcm_shared sh;
@@ -3080,8 +3070,8 @@ static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
        return 0;
 }
 
-static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data,
-                                 size_t count, loff_t *ppos)
+static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data,
+                               size_t count, loff_t *ppos)
 {
        int error = 0;
        struct sdpcm_shared sh;
@@ -3122,7 +3112,7 @@ static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,
        struct brcmf_sdio *bus = f->private_data;
        int res;
 
-       res = brcmf_sdbrcm_died_dump(bus, data, count, ppos);
+       res = brcmf_sdio_died_dump(bus, data, count, ppos);
        if (res > 0)
                *ppos += res;
        return (ssize_t)res;
@@ -3147,7 +3137,7 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
        brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt);
 }
 #else
-static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)
+static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)
 {
        return 0;
 }
@@ -3158,7 +3148,7 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)
 #endif /* DEBUG */
 
 static int
-brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
+brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
 {
        int timeleft;
        uint rxlen = 0;
@@ -3171,7 +3161,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
        brcmf_dbg(TRACE, "Enter\n");
 
        /* Wait until control frame is available */
-       timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending);
+       timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);
 
        spin_lock_bh(&bus->rxctl_lock);
        rxlen = bus->rxlen;
@@ -3188,13 +3178,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
                          rxlen, msglen);
        } else if (timeleft == 0) {
                brcmf_err("resumed on timeout\n");
-               brcmf_sdbrcm_checkdied(bus);
+               brcmf_sdio_checkdied(bus);
        } else if (pending) {
                brcmf_dbg(CTL, "cancelled\n");
                return -ERESTARTSYS;
        } else {
                brcmf_dbg(CTL, "resumed for unknown reason?\n");
-               brcmf_sdbrcm_checkdied(bus);
+               brcmf_sdio_checkdied(bus);
        }
 
        if (rxlen)
@@ -3205,7 +3195,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
        return rxlen ? (int)rxlen : -ETIMEDOUT;
 }
 
-static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
+static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter)
 {
        struct chip_info *ci = bus->ci;
 
@@ -3230,7 +3220,7 @@ static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
        return true;
 }
 
-static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
+static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus)
 {
        const struct firmware *fw;
        int err;
@@ -3238,7 +3228,7 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
        int address;
        int len;
 
-       fw = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_BIN);
+       fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
        if (fw == NULL)
                return -ENOENT;
 
@@ -3252,8 +3242,8 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
        while (offset < fw->size) {
                len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK :
                      fw->size - offset;
-               err = brcmf_sdio_ramrw(bus->sdiodev, true, address,
-                                      (u8 *)&fw->data[offset], len);
+               err = brcmf_sdiod_ramrw(bus->sdiodev, true, address,
+                                       (u8 *)&fw->data[offset], len);
                if (err) {
                        brcmf_err("error %d on writing %d membytes at 0x%08x\n",
                                  err, len, address);
@@ -3278,8 +3268,8 @@ failure:
  * by two NULs.
 */
 
-static int brcmf_process_nvram_vars(struct brcmf_sdio *bus,
-                                   const struct firmware *nv)
+static int brcmf_sdio_strip_nvram(struct brcmf_sdio *bus,
+                                 const struct firmware *nv)
 {
        char *varbuf;
        char *dp;
@@ -3343,44 +3333,48 @@ err:
        return ret;
 }
 
-static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus)
+static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus)
 {
        const struct firmware *nv;
        int ret;
 
-       nv = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
+       nv = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
        if (nv == NULL)
                return -ENOENT;
 
-       ret = brcmf_process_nvram_vars(bus, nv);
+       ret = brcmf_sdio_strip_nvram(bus, nv);
 
        release_firmware(nv);
 
        return ret;
 }
 
-static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
+static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
 {
-       int bcmerror = -1;
+       int bcmerror = -EFAULT;
+
+
+       sdio_claim_host(bus->sdiodev->func[1]);
+       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
 
        /* Keep arm in reset */
-       if (!brcmf_sdbrcm_download_state(bus, true)) {
+       if (!brcmf_sdio_download_state(bus, true)) {
                brcmf_err("error placing ARM core in reset\n");
                goto err;
        }
 
-       if (brcmf_sdbrcm_download_code_file(bus)) {
+       if (brcmf_sdio_download_code_file(bus)) {
                brcmf_err("dongle image file download failed\n");
                goto err;
        }
 
-       if (brcmf_sdbrcm_download_nvram(bus)) {
+       if (brcmf_sdio_download_nvram(bus)) {
                brcmf_err("dongle nvram file download failed\n");
                goto err;
        }
 
        /* Take arm out of reset */
-       if (!brcmf_sdbrcm_download_state(bus, false)) {
+       if (!brcmf_sdio_download_state(bus, false)) {
                brcmf_err("error getting out of ARM core reset\n");
                goto err;
        }
@@ -3388,12 +3382,15 @@ static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
        bcmerror = 0;
 
 err:
+       brcmf_sdio_clkctl(bus, CLK_SDONLY, false);
+       sdio_release_host(bus->sdiodev->func[1]);
        return bcmerror;
 }
 
-static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus)
+static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus)
 {
-       u32 addr, reg;
+       u32 addr, reg, pmu_cc3_mask = ~0;
+       int err;
 
        brcmf_dbg(TRACE, "Enter\n");
 
@@ -3401,49 +3398,61 @@ static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus)
        if (bus->ci->pmurev < 17)
                return false;
 
-       /* read PMU chipcontrol register 3*/
-       addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr);
-       brcmf_sdio_regwl(bus->sdiodev, addr, 3, NULL);
-       addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data);
-       reg = brcmf_sdio_regrl(bus->sdiodev, addr, NULL);
+       switch (bus->ci->chip) {
+       case BCM43241_CHIP_ID:
+       case BCM4335_CHIP_ID:
+       case BCM4339_CHIP_ID:
+               /* read PMU chipcontrol register 3 */
+               addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr);
+               brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL);
+               addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data);
+               reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL);
+               return (reg & pmu_cc3_mask) != 0;
+       default:
+               addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucapabilities_ext);
+               reg = brcmf_sdiod_regrl(bus->sdiodev, addr, &err);
+               if ((reg & PCAPEXT_SR_SUPPORTED_MASK) == 0)
+                       return false;
 
-       return (bool)reg;
+               addr = CORE_CC_REG(bus->ci->c_inf[0].base, retention_ctl);
+               reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL);
+               return (reg & (PMU_RCTL_MACPHY_DISABLE_MASK |
+                              PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
+       }
 }
 
-static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus)
+static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
 {
        int err = 0;
        u8 val;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL,
-                              &err);
+       val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);
        if (err) {
                brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");
                return;
        }
 
        val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT;
-       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL,
-                        val, &err);
+       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);
        if (err) {
                brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");
                return;
        }
 
        /* Add CMD14 Support */
-       brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
-                        (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
-                         SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
-                        &err);
+       brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP,
+                         (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT |
+                          SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT),
+                         &err);
        if (err) {
                brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");
                return;
        }
 
-       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                        SBSDIO_FORCE_HT, &err);
+       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                         SBSDIO_FORCE_HT, &err);
        if (err) {
                brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");
                return;
@@ -3455,7 +3464,7 @@ static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus)
 }
 
 /* enable KSO bit */
-static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)
+static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)
 {
        u8 val;
        int err = 0;
@@ -3466,8 +3475,7 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)
        if (bus->ci->c_inf[1].rev < 12)
                return 0;
 
-       val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
-                              &err);
+       val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);
        if (err) {
                brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");
                return err;
@@ -3476,8 +3484,8 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)
        if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {
                val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<
                        SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
-                                val, &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR,
+                                 val, &err);
                if (err) {
                        brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");
                        return err;
@@ -3488,25 +3496,7 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)
 }
 
 
-static bool
-brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
-{
-       bool ret;
-
-       sdio_claim_host(bus->sdiodev->func[1]);
-
-       brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
-
-       ret = _brcmf_sdbrcm_download_firmware(bus) == 0;
-
-       brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
-
-       sdio_release_host(bus->sdiodev->func[1]);
-
-       return ret;
-}
-
-static int brcmf_sdbrcm_bus_preinit(struct device *dev)
+static int brcmf_sdio_bus_preinit(struct device *dev)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
@@ -3565,13 +3555,11 @@ done:
        return err;
 }
 
-static int brcmf_sdbrcm_bus_init(struct device *dev)
+static int brcmf_sdio_bus_init(struct device *dev)
 {
        struct brcmf_bus *bus_if = dev_get_drvdata(dev);
        struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
        struct brcmf_sdio *bus = sdiodev->bus;
-       unsigned long timeout;
-       u8 ready, enable;
        int err, ret = 0;
        u8 saveclk;
 
@@ -3579,8 +3567,9 @@ static int brcmf_sdbrcm_bus_init(struct device *dev)
 
        /* try to download image and nvram to the dongle */
        if (bus_if->state == BRCMF_BUS_DOWN) {
-               if (!(brcmf_sdbrcm_download_firmware(bus)))
-                       return -1;
+               err = brcmf_sdio_download_firmware(bus);
+               if (err)
+                       return err;
        }
 
        if (!bus->sdiodev->bus_if->drvr)
@@ -3588,21 +3577,21 @@ static int brcmf_sdbrcm_bus_init(struct device *dev)
 
        /* Start the watchdog timer */
        bus->sdcnt.tickcnt = 0;
-       brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+       brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
 
        sdio_claim_host(bus->sdiodev->func[1]);
 
        /* Make sure backplane clock is on, needed to generate F2 interrupt */
-       brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
        if (bus->clkstate != CLK_AVAIL)
                goto exit;
 
        /* Force clocks on backplane to be sure F2 interrupt propagates */
-       saveclk = brcmf_sdio_regrb(bus->sdiodev,
-                                  SBSDIO_FUNC1_CHIPCLKCSR, &err);
+       saveclk = brcmf_sdiod_regrb(bus->sdiodev,
+                                   SBSDIO_FUNC1_CHIPCLKCSR, &err);
        if (!err) {
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                (saveclk | SBSDIO_FORCE_HT), &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 (saveclk | SBSDIO_FORCE_HT), &err);
        }
        if (err) {
                brcmf_err("Failed to force clock for F2: err %d\n", err);
@@ -3612,56 +3601,42 @@ static int brcmf_sdbrcm_bus_init(struct device *dev)
        /* Enable function 2 (frame transfers) */
        w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
                  offsetof(struct sdpcmd_regs, tosbmailboxdata));
-       enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
-
-       brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL);
+       err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]);
 
-       timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY);
-       ready = 0;
-       while (enable != ready) {
-               ready = brcmf_sdio_regrb(bus->sdiodev,
-                                        SDIO_CCCR_IORx, NULL);
-               if (time_after(jiffies, timeout))
-                       break;
-               else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50))
-                       /* prevent busy waiting if it takes too long */
-                       msleep_interruptible(20);
-       }
 
-       brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready);
+       brcmf_dbg(INFO, "enable F2: err=%d\n", err);
 
        /* If F2 successfully enabled, set core and enable interrupts */
-       if (ready == enable) {
+       if (!err) {
                /* Set up the interrupt mask and enable interrupts */
                bus->hostintmask = HOSTINTMASK;
                w_sdreg32(bus, bus->hostintmask,
                          offsetof(struct sdpcmd_regs, hostintmask));
 
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err);
        } else {
                /* Disable F2 again */
-               enable = SDIO_FUNC_ENABLE_1;
-               brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL);
+               sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
                ret = -ENODEV;
        }
 
-       if (brcmf_sdbrcm_sr_capable(bus)) {
-               brcmf_sdbrcm_sr_init(bus);
+       if (brcmf_sdio_sr_capable(bus)) {
+               brcmf_sdio_sr_init(bus);
        } else {
                /* Restore previous clock setting */
-               brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                                saveclk, &err);
+               brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                                 saveclk, &err);
        }
 
        if (ret == 0) {
-               ret = brcmf_sdio_intr_register(bus->sdiodev);
+               ret = brcmf_sdiod_intr_register(bus->sdiodev);
                if (ret != 0)
                        brcmf_err("intr register failed:%d\n", ret);
        }
 
        /* If we didn't come up, turn off backplane clock */
-       if (bus_if->state != BRCMF_BUS_DATA)
-               brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+       if (ret != 0)
+               brcmf_sdio_clkctl(bus, CLK_NONE, false);
 
 exit:
        sdio_release_host(bus->sdiodev->func[1]);
@@ -3669,10 +3644,8 @@ exit:
        return ret;
 }
 
-void brcmf_sdbrcm_isr(void *arg)
+void brcmf_sdio_isr(struct brcmf_sdio *bus)
 {
-       struct brcmf_sdio *bus = (struct brcmf_sdio *) arg;
-
        brcmf_dbg(TRACE, "Enter\n");
 
        if (!bus) {
@@ -3702,7 +3675,7 @@ void brcmf_sdbrcm_isr(void *arg)
        queue_work(bus->brcmf_wq, &bus->datawork);
 }
 
-static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
+static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
 {
 #ifdef DEBUG
        struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
@@ -3726,9 +3699,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                                u8 devpend;
 
                                sdio_claim_host(bus->sdiodev->func[1]);
-                               devpend = brcmf_sdio_regrb(bus->sdiodev,
-                                                          SDIO_CCCR_INTx,
-                                                          NULL);
+                               devpend = brcmf_sdiod_regrb(bus->sdiodev,
+                                                           SDIO_CCCR_INTx,
+                                                           NULL);
                                sdio_release_host(bus->sdiodev->func[1]);
                                intstatus =
                                    devpend & (INTR_STATUS_FUNC1 |
@@ -3758,8 +3731,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                        bus->console.count -= bus->console_interval;
                        sdio_claim_host(bus->sdiodev->func[1]);
                        /* Make sure backplane clock is on */
-                       brcmf_sdbrcm_bus_sleep(bus, false, false);
-                       if (brcmf_sdbrcm_readconsole(bus) < 0)
+                       brcmf_sdio_bus_sleep(bus, false, false);
+                       if (brcmf_sdio_readconsole(bus) < 0)
                                /* stop on error */
                                bus->console_interval = 0;
                        sdio_release_host(bus->sdiodev->func[1]);
@@ -3773,11 +3746,11 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
                        bus->idlecount = 0;
                        if (bus->activity) {
                                bus->activity = false;
-                               brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
+                               brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);
                        } else {
                                brcmf_dbg(SDIO, "idle\n");
                                sdio_claim_host(bus->sdiodev->func[1]);
-                               brcmf_sdbrcm_bus_sleep(bus, true, false);
+                               brcmf_sdio_bus_sleep(bus, true, false);
                                sdio_release_host(bus->sdiodev->func[1]);
                        }
                }
@@ -3792,38 +3765,13 @@ static void brcmf_sdio_dataworker(struct work_struct *work)
                                              datawork);
 
        while (atomic_read(&bus->dpc_tskcnt)) {
-               brcmf_sdbrcm_dpc(bus);
+               brcmf_sdio_dpc(bus);
                atomic_dec(&bus->dpc_tskcnt);
        }
 }
 
-static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
-{
-       brcmf_dbg(TRACE, "Enter\n");
-
-       kfree(bus->rxbuf);
-       bus->rxctl = bus->rxbuf = NULL;
-       bus->rxlen = 0;
-}
-
-static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
-{
-       brcmf_dbg(TRACE, "Enter\n");
-
-       if (bus->sdiodev->bus_if->maxctl) {
-               bus->rxblen =
-                   roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
-                           ALIGNMENT) + bus->head_align;
-               bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
-               if (!(bus->rxbuf))
-                       return false;
-       }
-
-       return true;
-}
-
 static bool
-brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
+brcmf_sdio_probe_attach(struct brcmf_sdio *bus)
 {
        u8 clkctl = 0;
        int err = 0;
@@ -3836,18 +3784,18 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
        sdio_claim_host(bus->sdiodev->func[1]);
 
        pr_debug("F1 signature read @0x18000000=0x%4x\n",
-                brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
+                brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));
 
        /*
         * Force PLL off until brcmf_sdio_chip_attach()
         * programs PLL control regs
         */
 
-       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
-                        BRCMF_INIT_CLKCTL1, &err);
+       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
+                         BRCMF_INIT_CLKCTL1, &err);
        if (!err)
-               clkctl = brcmf_sdio_regrb(bus->sdiodev,
-                                         SBSDIO_FUNC1_CHIPCLKCSR, &err);
+               clkctl = brcmf_sdiod_regrb(bus->sdiodev,
+                                          SBSDIO_FUNC1_CHIPCLKCSR, &err);
 
        if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
                brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
@@ -3855,12 +3803,12 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
                goto fail;
        }
 
-       if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) {
+       if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) {
                brcmf_err("brcmf_sdio_chip_attach failed!\n");
                goto fail;
        }
 
-       if (brcmf_sdbrcm_kso_init(bus)) {
+       if (brcmf_sdio_kso_init(bus)) {
                brcmf_err("error enabling KSO\n");
                goto fail;
        }
@@ -3879,33 +3827,33 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
        }
 
        /* Set card control so an SDIO card reset does a WLAN backplane reset */
-       reg_val = brcmf_sdio_regrb(bus->sdiodev,
-                                  SDIO_CCCR_BRCM_CARDCTRL, &err);
+       reg_val = brcmf_sdiod_regrb(bus->sdiodev,
+                                   SDIO_CCCR_BRCM_CARDCTRL, &err);
        if (err)
                goto fail;
 
        reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET;
 
-       brcmf_sdio_regwb(bus->sdiodev,
-                        SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
+       brcmf_sdiod_regwb(bus->sdiodev,
+                         SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);
        if (err)
                goto fail;
 
        /* set PMUControl so a backplane reset does PMU state reload */
        reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base,
                               pmucontrol);
-       reg_val = brcmf_sdio_regrl(bus->sdiodev,
-                                  reg_addr,
-                                  &err);
+       reg_val = brcmf_sdiod_regrl(bus->sdiodev,
+                                   reg_addr,
+                                   &err);
        if (err)
                goto fail;
 
        reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT);
 
-       brcmf_sdio_regwl(bus->sdiodev,
-                        reg_addr,
-                        reg_val,
-                        &err);
+       brcmf_sdiod_regwl(bus->sdiodev,
+                         reg_addr,
+                         reg_val,
+                         &err);
        if (err)
                goto fail;
 
@@ -3935,42 +3883,8 @@ fail:
        return false;
 }
 
-static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus)
-{
-       brcmf_dbg(TRACE, "Enter\n");
-
-       sdio_claim_host(bus->sdiodev->func[1]);
-
-       /* Disable F2 to clear any intermediate frame state on the dongle */
-       brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx,
-                        SDIO_FUNC_ENABLE_1, NULL);
-
-       bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
-       bus->rxflow = false;
-
-       /* Done with backplane-dependent accesses, can drop clock... */
-       brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
-
-       sdio_release_host(bus->sdiodev->func[1]);
-
-       /* ...and initialize clock/power states */
-       bus->clkstate = CLK_SDONLY;
-       bus->idletime = BRCMF_IDLE_INTERVAL;
-       bus->idleclock = BRCMF_IDLE_ACTIVE;
-
-       /* Query the F2 block size, set roundup accordingly */
-       bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
-       bus->roundup = min(max_roundup, bus->blocksize);
-
-       /* SR state */
-       bus->sleeping = false;
-       bus->sr_enabled = false;
-
-       return true;
-}
-
 static int
-brcmf_sdbrcm_watchdog_thread(void *data)
+brcmf_sdio_watchdog_thread(void *data)
 {
        struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
 
@@ -3980,7 +3894,7 @@ brcmf_sdbrcm_watchdog_thread(void *data)
                if (kthread_should_stop())
                        break;
                if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
-                       brcmf_sdbrcm_bus_watchdog(bus);
+                       brcmf_sdio_bus_watchdog(bus);
                        /* Count the tick for reference */
                        bus->sdcnt.tickcnt++;
                } else
@@ -3990,7 +3904,7 @@ brcmf_sdbrcm_watchdog_thread(void *data)
 }
 
 static void
-brcmf_sdbrcm_watchdog(unsigned long data)
+brcmf_sdio_watchdog(unsigned long data)
 {
        struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
 
@@ -4003,71 +3917,23 @@ brcmf_sdbrcm_watchdog(unsigned long data)
        }
 }
 
-static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus)
-{
-       brcmf_dbg(TRACE, "Enter\n");
-
-       if (bus->ci) {
-               sdio_claim_host(bus->sdiodev->func[1]);
-               brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
-               brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
-               sdio_release_host(bus->sdiodev->func[1]);
-               brcmf_sdio_chip_detach(&bus->ci);
-               if (bus->vars && bus->varsz)
-                       kfree(bus->vars);
-               bus->vars = NULL;
-       }
-
-       brcmf_dbg(TRACE, "Disconnected\n");
-}
-
-/* Detach and free everything */
-static void brcmf_sdbrcm_release(struct brcmf_sdio *bus)
-{
-       brcmf_dbg(TRACE, "Enter\n");
-
-       if (bus) {
-               /* De-register interrupt handler */
-               brcmf_sdio_intr_unregister(bus->sdiodev);
-
-               cancel_work_sync(&bus->datawork);
-               if (bus->brcmf_wq)
-                       destroy_workqueue(bus->brcmf_wq);
-
-               if (bus->sdiodev->bus_if->drvr) {
-                       brcmf_detach(bus->sdiodev->dev);
-                       brcmf_sdbrcm_release_dongle(bus);
-               }
-
-               brcmu_pkt_buf_free_skb(bus->txglom_sgpad);
-               brcmf_sdbrcm_release_malloc(bus);
-               kfree(bus->hdrbuf);
-               kfree(bus);
-       }
-
-       brcmf_dbg(TRACE, "Disconnected\n");
-}
-
 static struct brcmf_bus_ops brcmf_sdio_bus_ops = {
-       .stop = brcmf_sdbrcm_bus_stop,
-       .preinit = brcmf_sdbrcm_bus_preinit,
-       .init = brcmf_sdbrcm_bus_init,
-       .txdata = brcmf_sdbrcm_bus_txdata,
-       .txctl = brcmf_sdbrcm_bus_txctl,
-       .rxctl = brcmf_sdbrcm_bus_rxctl,
-       .gettxq = brcmf_sdbrcm_bus_gettxq,
+       .stop = brcmf_sdio_bus_stop,
+       .preinit = brcmf_sdio_bus_preinit,
+       .init = brcmf_sdio_bus_init,
+       .txdata = brcmf_sdio_bus_txdata,
+       .txctl = brcmf_sdio_bus_txctl,
+       .rxctl = brcmf_sdio_bus_rxctl,
+       .gettxq = brcmf_sdio_bus_gettxq,
 };
 
-void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
+struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
 {
        int ret;
        struct brcmf_sdio *bus;
 
        brcmf_dbg(TRACE, "Enter\n");
 
-       /* We make an assumption about address window mappings:
-        * regsva == SI_ENUM_BASE*/
-
        /* Allocate private bus interface state */
        bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);
        if (!bus)
@@ -4101,8 +3967,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        }
 
        /* attempt to attach to the dongle */
-       if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) {
-               brcmf_err("brcmf_sdbrcm_probe_attach failed\n");
+       if (!(brcmf_sdio_probe_attach(bus))) {
+               brcmf_err("brcmf_sdio_probe_attach failed\n");
                goto fail;
        }
 
@@ -4114,11 +3980,11 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        /* Set up the watchdog timer */
        init_timer(&bus->timer);
        bus->timer.data = (unsigned long)bus;
-       bus->timer.function = brcmf_sdbrcm_watchdog;
+       bus->timer.function = brcmf_sdio_watchdog;
 
        /* Initialize watchdog thread */
        init_completion(&bus->watchdog_wait);
-       bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread,
+       bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,
                                        bus, "brcmf_watchdog");
        if (IS_ERR(bus->watchdog_tsk)) {
                pr_warn("brcmf_watchdog thread failed to start\n");
@@ -4144,15 +4010,42 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        }
 
        /* Allocate buffers */
-       if (!(brcmf_sdbrcm_probe_malloc(bus))) {
-               brcmf_err("brcmf_sdbrcm_probe_malloc failed\n");
-               goto fail;
+       if (bus->sdiodev->bus_if->maxctl) {
+               bus->rxblen =
+                   roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
+                           ALIGNMENT) + bus->head_align;
+               bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
+               if (!(bus->rxbuf)) {
+                       brcmf_err("rxbuf allocation failed\n");
+                       goto fail;
+               }
        }
 
-       if (!(brcmf_sdbrcm_probe_init(bus))) {
-               brcmf_err("brcmf_sdbrcm_probe_init failed\n");
-               goto fail;
-       }
+       sdio_claim_host(bus->sdiodev->func[1]);
+
+       /* Disable F2 to clear any intermediate frame state on the dongle */
+       sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]);
+
+       bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
+       bus->rxflow = false;
+
+       /* Done with backplane-dependent accesses, can drop clock... */
+       brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+       sdio_release_host(bus->sdiodev->func[1]);
+
+       /* ...and initialize clock/power states */
+       bus->clkstate = CLK_SDONLY;
+       bus->idletime = BRCMF_IDLE_INTERVAL;
+       bus->idleclock = BRCMF_IDLE_ACTIVE;
+
+       /* Query the F2 block size, set roundup accordingly */
+       bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
+       bus->roundup = min(max_roundup, bus->blocksize);
+
+       /* SR state */
+       bus->sleeping = false;
+       bus->sr_enabled = false;
 
        brcmf_sdio_debugfs_create(bus);
        brcmf_dbg(INFO, "completed!!\n");
@@ -4167,24 +4060,46 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
        return bus;
 
 fail:
-       brcmf_sdbrcm_release(bus);
+       brcmf_sdio_remove(bus);
        return NULL;
 }
 
-void brcmf_sdbrcm_disconnect(void *ptr)
+/* Detach and free everything */
+void brcmf_sdio_remove(struct brcmf_sdio *bus)
 {
-       struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr;
-
        brcmf_dbg(TRACE, "Enter\n");
 
-       if (bus)
-               brcmf_sdbrcm_release(bus);
+       if (bus) {
+               /* De-register interrupt handler */
+               brcmf_sdiod_intr_unregister(bus->sdiodev);
+
+               cancel_work_sync(&bus->datawork);
+               if (bus->brcmf_wq)
+                       destroy_workqueue(bus->brcmf_wq);
+
+               if (bus->sdiodev->bus_if->drvr) {
+                       brcmf_detach(bus->sdiodev->dev);
+               }
+
+               if (bus->ci) {
+                       sdio_claim_host(bus->sdiodev->func[1]);
+                       brcmf_sdio_clkctl(bus, CLK_AVAIL, false);
+                       brcmf_sdio_clkctl(bus, CLK_NONE, false);
+                       sdio_release_host(bus->sdiodev->func[1]);
+                       brcmf_sdio_chip_detach(&bus->ci);
+               }
+
+               brcmu_pkt_buf_free_skb(bus->txglom_sgpad);
+               kfree(bus->rxbuf);
+               kfree(bus->hdrbuf);
+               kfree(bus->vars);
+               kfree(bus);
+       }
 
        brcmf_dbg(TRACE, "Disconnected\n");
 }
 
-void
-brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick)
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)
 {
        /* Totally stop the timer */
        if (!wdtick && bus->wd_timer_valid) {
@@ -4195,7 +4110,7 @@ brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick)
        }
 
        /* don't start the wd until fw is loaded */
-       if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN)
+       if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA)
                return;
 
        if (wdtick) {
index b72d339..22adbe3 100644 (file)
@@ -68,7 +68,7 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 
        brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
-                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data");
+                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
        err = brcmf_fil_cmd_data(ifp, cmd, data, len, true);
        mutex_unlock(&ifp->drvr->proto_block);
@@ -86,7 +86,7 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len)
 
        brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
-                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data");
+                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
        mutex_unlock(&ifp->drvr->proto_block);
 
@@ -155,7 +155,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data,
 
        brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
-                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data");
+                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
        buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf,
                                    sizeof(drvr->proto_buf));
@@ -195,7 +195,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data,
 
        brcmf_dbg(FIL, "name=%s, len=%d\n", name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
-                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data");
+                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
        mutex_unlock(&drvr->proto_block);
        return err;
@@ -278,7 +278,7 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name,
 
        brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
-                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data");
+                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
        buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len,
                                     drvr->proto_buf, sizeof(drvr->proto_buf));
@@ -317,7 +317,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name,
        }
        brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len);
        brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data,
-                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data");
+                          min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n");
 
        mutex_unlock(&drvr->proto_block);
        return err;
index 4f80e12..c3e7d76 100644 (file)
@@ -838,7 +838,7 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx)
        brcmf_fws_hanger_cleanup(fws, matchfn, ifidx);
 }
 
-static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
+static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
 {
        struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac;
        u8 *wlh;
@@ -887,9 +887,7 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb)
        if (fillers)
                memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers);
 
-       brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX),
-                           data_offset >> 2, skb);
-       return 0;
+       return (u8)(data_offset >> 2);
 }
 
 static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
@@ -897,10 +895,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
                                 int fifo, bool send_immediately)
 {
        struct sk_buff *skb;
-       struct brcmf_bus *bus;
        struct brcmf_skbuff_cb *skcb;
        s32 err;
        u32 len;
+       u8 data_offset;
+       int ifidx;
 
        /* check delayedQ and suppressQ in one call using bitmap */
        if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0)
@@ -928,13 +927,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws,
                skcb->state = BRCMF_FWS_SKBSTATE_TIM;
                skcb->htod = 0;
                skcb->htod_seq = 0;
-               bus = fws->drvr->bus_if;
-               err = brcmf_fws_hdrpush(fws, skb);
-               if (err == 0) {
-                       brcmf_fws_unlock(fws);
-                       err = brcmf_bus_txdata(bus, skb);
-                       brcmf_fws_lock(fws);
-               }
+               data_offset = brcmf_fws_hdrpush(fws, skb);
+               ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
+               brcmf_fws_unlock(fws);
+               err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
+               brcmf_fws_lock(fws);
                if (err)
                        brcmu_pkt_buf_free_skb(skb);
                return true;
@@ -1393,7 +1390,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
        entry->generation = genbit;
 
        ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb);
-       if (ret == 0)
+       if (ret == 0) {
                brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit);
                brcmf_skbcb(skb)->htod_seq = seq;
                if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) {
@@ -1404,6 +1401,8 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo,
                }
                ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo,
                                    skb);
+       }
+
        if (ret != 0) {
                /* suppress q is full or hdrpull failed, drop this packet */
                brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb,
@@ -1717,7 +1716,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len,
        return 0;
 }
 
-static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
+static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                                   struct sk_buff *p)
 {
        struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p);
@@ -1735,7 +1734,7 @@ static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo,
                flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED;
        }
        brcmf_skb_htod_tag_set_field(p, FLAGS, flags);
-       brcmf_fws_hdrpush(fws, p);
+       return brcmf_fws_hdrpush(fws, p);
 }
 
 static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
@@ -1803,20 +1802,21 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo,
 {
        struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb);
        struct brcmf_fws_mac_descriptor *entry;
-       struct brcmf_bus *bus = fws->drvr->bus_if;
        int rc;
        u8 ifidx;
+       u8 data_offset;
 
        entry = skcb->mac;
        if (IS_ERR(entry))
                return PTR_ERR(entry);
 
-       brcmf_fws_precommit_skb(fws, fifo, skb);
+       data_offset = brcmf_fws_precommit_skb(fws, fifo, skb);
        entry->transit_count++;
        if (entry->suppressed)
                entry->suppr_transit_count++;
+       ifidx = brcmf_skb_if_flags_get_field(skb, INDEX);
        brcmf_fws_unlock(fws);
-       rc = brcmf_bus_txdata(bus, skb);
+       rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb);
        brcmf_fws_lock(fws);
        brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name,
                  skcb->if_flags, skcb->htod, rc);
@@ -1977,10 +1977,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
                                                        &skb, true);
                                ifidx = brcmf_skb_if_flags_get_field(skb,
                                                                     INDEX);
-                               brcmf_proto_hdrpush(drvr, ifidx, 0, skb);
-                               /* Use bus module to send data frame */
+                               /* Use proto layer to send data frame */
                                brcmf_fws_unlock(fws);
-                               ret = brcmf_bus_txdata(drvr->bus_if, skb);
+                               ret = brcmf_proto_txdata(drvr, ifidx, 0, skb);
                                brcmf_fws_lock(fws);
                                if (ret < 0)
                                        brcmf_txfinalize(drvr, skb, false);
index d318036..e23c869 100644 (file)
@@ -1956,21 +1956,21 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
                err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
                if (err < 0) {
                        brcmf_err("set p2p_disc error\n");
-                       brcmf_free_vif(cfg, p2p_vif);
+                       brcmf_free_vif(p2p_vif);
                        goto exit;
                }
                /* obtain bsscfg index for P2P discovery */
                err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
                if (err < 0) {
                        brcmf_err("retrieving discover bsscfg index failed\n");
-                       brcmf_free_vif(cfg, p2p_vif);
+                       brcmf_free_vif(p2p_vif);
                        goto exit;
                }
                /* Verify that firmware uses same bssidx as driver !! */
                if (p2p_ifp->bssidx != bssidx) {
                        brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
                                  bssidx, p2p_ifp->bssidx);
-                       brcmf_free_vif(cfg, p2p_vif);
+                       brcmf_free_vif(p2p_vif);
                        goto exit;
                }
 
@@ -1998,7 +1998,7 @@ void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
                brcmf_p2p_cancel_remain_on_channel(vif->ifp);
                brcmf_p2p_deinit_discovery(p2p);
                /* remove discovery interface */
-               brcmf_free_vif(p2p->cfg, vif);
+               brcmf_free_vif(vif);
                p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
        }
        /* just set it all to zero */
@@ -2223,7 +2223,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p,
        return &p2p_vif->wdev;
 
 fail:
-       brcmf_free_vif(p2p->cfg, p2p_vif);
+       brcmf_free_vif(p2p_vif);
        return ERR_PTR(err);
 }
 
@@ -2232,31 +2232,12 @@ fail:
  *
  * @vif: virtual interface object to delete.
  */
-static void brcmf_p2p_delete_p2pdev(struct brcmf_cfg80211_info *cfg,
+static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p,
                                    struct brcmf_cfg80211_vif *vif)
 {
        cfg80211_unregister_wdev(&vif->wdev);
-       cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
-       brcmf_free_vif(cfg, vif);
-}
-
-/**
- * brcmf_p2p_free_p2p_if() - free up net device related data.
- *
- * @ndev: net device that needs to be freed.
- */
-static void brcmf_p2p_free_p2p_if(struct net_device *ndev)
-{
-       struct brcmf_cfg80211_info *cfg;
-       struct brcmf_cfg80211_vif *vif;
-       struct brcmf_if *ifp;
-
-       ifp = netdev_priv(ndev);
-       cfg = ifp->drvr->config;
-       vif = ifp->vif;
-
-       brcmf_free_vif(cfg, vif);
-       free_netdev(ifp->ndev);
+       p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
+       brcmf_free_vif(vif);
 }
 
 /**
@@ -2336,8 +2317,6 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
                brcmf_err("Registering netdevice failed\n");
                goto fail;
        }
-       /* override destructor */
-       ifp->ndev->destructor = brcmf_p2p_free_p2p_if;
 
        cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif;
        /* Disable firmware roaming for P2P interface  */
@@ -2350,7 +2329,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
        return &ifp->vif->wdev;
 
 fail:
-       brcmf_free_vif(cfg, vif);
+       brcmf_free_vif(vif);
        return ERR_PTR(err);
 }
 
@@ -2359,8 +2338,6 @@ fail:
  *
  * @wiphy: wiphy device of interface.
  * @wdev: wireless device of interface.
- *
- * TODO: not yet supported.
  */
 int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
 {
@@ -2386,7 +2363,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
                break;
 
        case NL80211_IFTYPE_P2P_DEVICE:
-               brcmf_p2p_delete_p2pdev(cfg, vif);
+               brcmf_p2p_delete_p2pdev(p2p, vif);
                return 0;
        default:
                return -ENOTSUPP;
index 87eb2bd..b6b4641 100644 (file)
@@ -39,7 +39,7 @@ int brcmf_proto_attach(struct brcmf_pub *drvr)
        if (brcmf_proto_bcdc_attach(drvr))
                goto fail;
 
-       if ((proto->hdrpush == NULL) || (proto->hdrpull == NULL) ||
+       if ((proto->txdata == NULL) || (proto->hdrpull == NULL) ||
            (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL)) {
                brcmf_err("Not all proto handlers have been installed\n");
                goto fail;
index 8de1b3b..482fb0b 100644 (file)
 #define BRCMFMAC_PROTO_H
 
 struct brcmf_proto {
-       void (*hdrpush)(struct brcmf_pub *drvr, int ifidx, u8 offset,
-                       struct sk_buff *skb);
        int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx,
                       struct sk_buff *skb);
        int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd,
                          void *buf, uint len);
        int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf,
                        uint len);
+       int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset,
+                     struct sk_buff *skb);
        void *pd;
 };
 
@@ -32,11 +32,6 @@ struct brcmf_proto {
 int brcmf_proto_attach(struct brcmf_pub *drvr);
 void brcmf_proto_detach(struct brcmf_pub *drvr);
 
-static inline void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
-                                      u8 offset, struct sk_buff *skb)
-{
-       drvr->proto->hdrpush(drvr, ifidx, offset, skb);
-}
 static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
                                      u8 *ifidx, struct sk_buff *skb)
 {
@@ -52,6 +47,11 @@ static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx,
 {
        return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len);
 }
+static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx,
+                                      u8 offset, struct sk_buff *skb)
+{
+       return drvr->proto->txdata(drvr, ifidx, offset, skb);
+}
 
 
 #endif /* BRCMFMAC_PROTO_H */
index 2096a14..9fd4067 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/netdevice.h>
 #include <linux/mmc/card.h>
 #include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
 #include <linux/ssb/ssb_regs.h>
 #include <linux/bcma/bcma.h>
 
@@ -83,6 +84,24 @@ static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = {
        {0, 0x1}
 };
 
+/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = {
+        {6, 0x7},
+        {5, 0x6},
+        {4, 0x5},
+        {3, 0x4},
+        {2, 0x2},
+        {1, 0x1},
+        {0, 0x0}
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */
+static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = {
+       {3, 0x3},
+       {2, 0x2},
+       {1, 0x1},
+       {0, 0x0} };
+
 /* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */
 static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = {
        {16, 0x7},
@@ -112,9 +131,9 @@ brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev,
 
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
 
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbidhigh),
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbidhigh),
+                                   NULL);
        return SBCOREREV(regdata);
 }
 
@@ -140,9 +159,9 @@ brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev,
        if (idx == BRCMF_MAX_CORENUM)
                return false;
 
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                                   NULL);
        regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT |
                    SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK);
        return (SSB_TMSLOW_CLOCK == regdata);
@@ -160,13 +179,13 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev,
        if (idx == BRCMF_MAX_CORENUM)
                return false;
 
-       regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                                   NULL);
        ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK;
 
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+                                   NULL);
        ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0);
 
        return ret;
@@ -182,79 +201,79 @@ brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev,
        idx = brcmf_sdio_chip_getinfidx(ci, coreid);
        base = ci->c_inf[idx].base;
 
-       regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
        if (regdata & SSB_TMSLOW_RESET)
                return;
 
-       regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL);
        if ((regdata & SSB_TMSLOW_CLOCK) != 0) {
                /*
                 * set target reject and spin until busy is clear
                 * (preserve core-specific bits)
                 */
-               regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                          NULL);
-               brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                regdata | SSB_TMSLOW_REJECT, NULL);
+               regdata = brcmf_sdiod_regrl(sdiodev,
+                                           CORE_SB(base, sbtmstatelow), NULL);
+               brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
+                                 regdata | SSB_TMSLOW_REJECT, NULL);
 
-               regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                          NULL);
+               regdata = brcmf_sdiod_regrl(sdiodev,
+                                           CORE_SB(base, sbtmstatelow), NULL);
                udelay(1);
-               SPINWAIT((brcmf_sdio_regrl(sdiodev,
-                                          CORE_SB(base, sbtmstatehigh),
-                                          NULL) &
-                       SSB_TMSHIGH_BUSY), 100000);
-
-               regdata = brcmf_sdio_regrl(sdiodev,
-                                          CORE_SB(base, sbtmstatehigh),
-                                          NULL);
+               SPINWAIT((brcmf_sdiod_regrl(sdiodev,
+                                           CORE_SB(base, sbtmstatehigh),
+                                           NULL) &
+                         SSB_TMSHIGH_BUSY), 100000);
+
+               regdata = brcmf_sdiod_regrl(sdiodev,
+                                           CORE_SB(base, sbtmstatehigh),
+                                           NULL);
                if (regdata & SSB_TMSHIGH_BUSY)
                        brcmf_err("core state still busy\n");
 
-               regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
-                                          NULL);
+               regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow),
+                                           NULL);
                if (regdata & SSB_IDLOW_INITIATOR) {
-                       regdata = brcmf_sdio_regrl(sdiodev,
-                                                  CORE_SB(base, sbimstate),
-                                                  NULL);
+                       regdata = brcmf_sdiod_regrl(sdiodev,
+                                                   CORE_SB(base, sbimstate),
+                                                   NULL);
                        regdata |= SSB_IMSTATE_REJECT;
-                       brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
-                                        regdata, NULL);
-                       regdata = brcmf_sdio_regrl(sdiodev,
-                                                  CORE_SB(base, sbimstate),
-                                                  NULL);
+                       brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate),
+                                         regdata, NULL);
+                       regdata = brcmf_sdiod_regrl(sdiodev,
+                                                   CORE_SB(base, sbimstate),
+                                                   NULL);
                        udelay(1);
-                       SPINWAIT((brcmf_sdio_regrl(sdiodev,
-                                                  CORE_SB(base, sbimstate),
-                                                  NULL) &
-                               SSB_IMSTATE_BUSY), 100000);
+                       SPINWAIT((brcmf_sdiod_regrl(sdiodev,
+                                                   CORE_SB(base, sbimstate),
+                                                   NULL) &
+                                 SSB_IMSTATE_BUSY), 100000);
                }
 
                /* set reset and reject while enabling the clocks */
                regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK |
                          SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET;
-               brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                regdata, NULL);
-               regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow),
-                                          NULL);
+               brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
+                                 regdata, NULL);
+               regdata = brcmf_sdiod_regrl(sdiodev,
+                                           CORE_SB(base, sbtmstatelow), NULL);
                udelay(10);
 
                /* clear the initiator reject bit */
-               regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow),
-                                          NULL);
+               regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow),
+                                           NULL);
                if (regdata & SSB_IDLOW_INITIATOR) {
-                       regdata = brcmf_sdio_regrl(sdiodev,
-                                                  CORE_SB(base, sbimstate),
-                                                  NULL);
+                       regdata = brcmf_sdiod_regrl(sdiodev,
+                                                   CORE_SB(base, sbimstate),
+                                                   NULL);
                        regdata &= ~SSB_IMSTATE_REJECT;
-                       brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate),
-                                        regdata, NULL);
+                       brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate),
+                                         regdata, NULL);
                }
        }
 
        /* leave reset and reject asserted */
-       brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
-                        (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
+       brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow),
+                         (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL);
        udelay(1);
 }
 
@@ -270,9 +289,9 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
                return;
 
        /* if core is already in reset, just return */
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+                                   NULL);
        if ((regdata & BCMA_RESET_CTL_RESET) != 0)
                return;
 
@@ -281,24 +300,24 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev,
         * extra 10ms is taken into account for firmware load stage
         * after 10300us carry on disabling the core anyway
         */
-       SPINWAIT(brcmf_sdio_regrl(sdiodev,
-                                 ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
-                                 NULL), 10300);
-       regdata = brcmf_sdio_regrl(sdiodev,
+       SPINWAIT(brcmf_sdiod_regrl(sdiodev,
                                   ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
-                                  NULL);
+                                  NULL), 10300);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   ci->c_inf[idx].wrapbase+BCMA_RESET_ST,
+                                   NULL);
        if (regdata)
                brcmf_err("disabling core 0x%x with reset status %x\n",
                          coreid, regdata);
 
-       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
-                        BCMA_RESET_CTL_RESET, NULL);
+       brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+                         BCMA_RESET_CTL_RESET, NULL);
        udelay(1);
 
-       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                        core_bits, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                                  NULL);
+       brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                         core_bits, NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                                   NULL);
        usleep_range(10, 20);
 
 }
@@ -325,47 +344,47 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev,
         * set reset while enabling the clock and
         * forcing them on throughout the core
         */
-       brcmf_sdio_regwl(sdiodev,
-                        CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                        SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET,
-                        NULL);
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                  NULL);
+       brcmf_sdiod_regwl(sdiodev,
+                         CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                         SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET,
+                         NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                                   NULL);
        udelay(1);
 
        /* clear any serror */
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
+                                   NULL);
        if (regdata & SSB_TMSHIGH_SERR)
-               brcmf_sdio_regwl(sdiodev,
-                                CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
-                                0, NULL);
+               brcmf_sdiod_regwl(sdiodev,
+                                 CORE_SB(ci->c_inf[idx].base, sbtmstatehigh),
+                                 0, NULL);
 
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbimstate),
-                                  NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbimstate),
+                                   NULL);
        if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO))
-               brcmf_sdio_regwl(sdiodev,
-                                CORE_SB(ci->c_inf[idx].base, sbimstate),
-                                regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
-                                NULL);
+               brcmf_sdiod_regwl(sdiodev,
+                                 CORE_SB(ci->c_inf[idx].base, sbimstate),
+                                 regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO),
+                                 NULL);
 
        /* clear reset and allow it to propagate throughout the core */
-       brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                        SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                  NULL);
+       brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                         SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                                   NULL);
        udelay(1);
 
        /* leave clock enabled */
-       brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                        SSB_TMSLOW_CLOCK, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
-                                  NULL);
+       brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                         SSB_TMSLOW_CLOCK, NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_SB(ci->c_inf[idx].base, sbtmstatelow),
+                                   NULL);
        udelay(1);
 }
 
@@ -384,21 +403,21 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev,
        brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits);
 
        /* now do initialization sequence */
-       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                        core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                                  NULL);
-       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
-                        0, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
-                                  NULL);
+       brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                         core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                                   NULL);
+       brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+                         0, NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   ci->c_inf[idx].wrapbase+BCMA_RESET_CTL,
+                                   NULL);
        udelay(1);
 
-       brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                        core_bits | BCMA_IOCTL_CLK, NULL);
-       regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
-                                  NULL);
+       brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                         core_bits | BCMA_IOCTL_CLK, NULL);
+       regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL,
+                                   NULL);
        udelay(1);
 }
 
@@ -438,7 +457,7 @@ static inline int brcmf_sdio_chip_cichk(struct chip_info *ci)
 #endif
 
 static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
-                                      struct chip_info *ci, u32 regs)
+                                      struct chip_info *ci)
 {
        u32 regdata;
        int ret;
@@ -449,10 +468,10 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
         * other ways of recognition should be added here.
         */
        ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON;
-       ci->c_inf[0].base = regs;
-       regdata = brcmf_sdio_regrl(sdiodev,
-                                  CORE_CC_REG(ci->c_inf[0].base, chipid),
-                                  NULL);
+       ci->c_inf[0].base = SI_ENUM_BASE;
+       regdata = brcmf_sdiod_regrl(sdiodev,
+                                   CORE_CC_REG(ci->c_inf[0].base, chipid),
+                                   NULL);
        ci->chip = regdata & CID_ID_MASK;
        ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
        if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 &&
@@ -569,6 +588,23 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev,
                ci->ramsize = 0xc0000;
                ci->rambase = 0x180000;
                break;
+       case BCM43362_CHIP_ID:
+               ci->c_inf[0].wrapbase = 0x18100000;
+               ci->c_inf[0].cib = 0x27004211;
+               ci->c_inf[1].id = BCMA_CORE_SDIO_DEV;
+               ci->c_inf[1].base = 0x18002000;
+               ci->c_inf[1].wrapbase = 0x18102000;
+               ci->c_inf[1].cib = 0x0a004211;
+               ci->c_inf[2].id = BCMA_CORE_INTERNAL_MEM;
+               ci->c_inf[2].base = 0x18004000;
+               ci->c_inf[2].wrapbase = 0x18104000;
+               ci->c_inf[2].cib = 0x08080401;
+               ci->c_inf[3].id = BCMA_CORE_ARM_CM3;
+               ci->c_inf[3].base = 0x18003000;
+               ci->c_inf[3].wrapbase = 0x18103000;
+               ci->c_inf[3].cib = 0x03004211;
+               ci->ramsize = 0x3C000;
+               break;
        default:
                brcmf_err("chipid 0x%x is not supported\n", ci->chip);
                return -ENODEV;
@@ -607,7 +643,7 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
 
        /* Try forcing SDIO core to do ALPAvail request only */
        clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
-       brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
        if (err) {
                brcmf_err("error writing for HT off\n");
                return err;
@@ -615,8 +651,8 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
 
        /* If register supported, wait for ALPAvail and then force ALP */
        /* This may take up to 15 milliseconds */
-       clkval = brcmf_sdio_regrb(sdiodev,
-                                 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+       clkval = brcmf_sdiod_regrb(sdiodev,
+                                  SBSDIO_FUNC1_CHIPCLKCSR, NULL);
 
        if ((clkval & ~SBSDIO_AVBITS) != clkset) {
                brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
@@ -624,8 +660,8 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
                return -EACCES;
        }
 
-       SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev,
-                                            SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
+       SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev,
+                                             SBSDIO_FUNC1_CHIPCLKCSR, NULL)),
                        !SBSDIO_ALPAV(clkval)),
                        PMU_MAX_TRANSITION_DLY);
        if (!SBSDIO_ALPAV(clkval)) {
@@ -635,11 +671,11 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev)
        }
 
        clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
-       brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
        udelay(65);
 
        /* Also, disable the extra SDIO pull-ups */
-       brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+       brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
 
        return 0;
 }
@@ -654,16 +690,16 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
        ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id);
 
        /* get chipcommon capabilites */
-       ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev,
-                                            CORE_CC_REG(base, capabilities),
-                                            NULL);
+       ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev,
+                                             CORE_CC_REG(base, capabilities),
+                                             NULL);
 
        /* get pmu caps & rev */
        if (ci->c_inf[0].caps & CC_CAP_PMU) {
                ci->pmucaps =
-                       brcmf_sdio_regrl(sdiodev,
-                                        CORE_CC_REG(base, pmucapabilities),
-                                        NULL);
+                       brcmf_sdiod_regrl(sdiodev,
+                                         CORE_CC_REG(base, pmucapabilities),
+                                         NULL);
                ci->pmurev = ci->pmucaps & PCAP_REV_MASK;
        }
 
@@ -681,7 +717,7 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev,
 }
 
 int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
-                          struct chip_info **ci_ptr, u32 regs)
+                          struct chip_info **ci_ptr)
 {
        int ret;
        struct chip_info *ci;
@@ -697,16 +733,16 @@ int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
        if (ret != 0)
                goto err;
 
-       ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs);
+       ret = brcmf_sdio_chip_recognition(sdiodev, ci);
        if (ret != 0)
                goto err;
 
        brcmf_sdio_chip_buscoresetup(sdiodev, ci);
 
-       brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
-                        0, NULL);
-       brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
-                        0, NULL);
+       brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup),
+                         0, NULL);
+       brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown),
+                         0, NULL);
 
        *ci_ptr = ci;
        return 0;
@@ -757,6 +793,11 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                str_mask = 0x00003800;
                str_shift = 11;
                break;
+       case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17):
+               str_tab = sdiod_drvstr_tab6_1v8;
+               str_mask = 0x00001800;
+               str_shift = 11;
+               break;
        case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17):
                /* note: 43143 does not support tristate */
                i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1;
@@ -769,6 +810,11 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                                  brcmf_sdio_chip_name(ci->chip, chn, 8),
                                  drivestrength);
                break;
+       case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13):
+               str_tab = sdiod_drive_strength_tab5_1v8;
+               str_mask = 0x00003800;
+               str_shift = 11;
+               break;
        default:
                brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n",
                          brcmf_sdio_chip_name(ci->chip, chn, 8),
@@ -784,12 +830,12 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                        }
                }
                addr = CORE_CC_REG(base, chipcontrol_addr);
-               brcmf_sdio_regwl(sdiodev, addr, 1, NULL);
-               cc_data_temp = brcmf_sdio_regrl(sdiodev, addr, NULL);
+               brcmf_sdiod_regwl(sdiodev, addr, 1, NULL);
+               cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL);
                cc_data_temp &= ~str_mask;
                drivestrength_sel <<= str_shift;
                cc_data_temp |= drivestrength_sel;
-               brcmf_sdio_regwl(sdiodev, addr, cc_data_temp, NULL);
+               brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL);
 
                brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n",
                          str_tab[i].strength, drivestrength, cc_data_temp);
@@ -816,8 +862,8 @@ brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr,
        memset(nvram_ularray, 0xaa, nvram_sz);
 
        /* Read the vars list to temp buffer for comparison */
-       err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray,
-                              nvram_sz);
+       err = brcmf_sdiod_ramrw(sdiodev, false, nvram_addr, nvram_ularray,
+                               nvram_sz);
        if (err) {
                brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n",
                          err, nvram_sz, nvram_addr);
@@ -850,7 +896,7 @@ static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
        nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase;
 
        /* Write the vars list */
-       err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
+       err = brcmf_sdiod_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz);
        if (err) {
                brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
                          err, nvram_sz, nvram_addr);
@@ -874,8 +920,8 @@ static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev,
                  nvram_addr, nvram_sz, token);
 
        /* Write the length token to the last word */
-       if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase),
-                            (u8 *)&token_le, 4))
+       if (brcmf_sdiod_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase),
+                             (u8 *)&token_le, 4))
                return false;
 
        return true;
@@ -891,7 +937,7 @@ brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev,
        ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0);
 
        /* clear length token */
-       brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
+       brcmf_sdiod_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4);
 }
 
 static bool
@@ -913,7 +959,7 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
        core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
        reg_addr = ci->c_inf[core_idx].base;
        reg_addr += offsetof(struct sdpcmd_regs, intstatus);
-       brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+       brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
 
        ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0);
 
@@ -942,11 +988,11 @@ brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci,
        core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV);
        reg_addr = ci->c_inf[core_idx].base;
        reg_addr += offsetof(struct sdpcmd_regs, intstatus);
-       brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
+       brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL);
 
        /* Write reset vector to address 0 */
-       brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec,
-                        sizeof(ci->rst_vec));
+       brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec,
+                         sizeof(ci->rst_vec));
 
        /* restore ARM */
        ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0);
index 507c61c..7ea424e 100644 (file)
 
 #define BRCMF_MAX_CORENUM      6
 
-/* SDIO device ID */
-#define SDIO_DEVICE_ID_BROADCOM_43143          43143
-#define SDIO_DEVICE_ID_BROADCOM_43241          0x4324
-#define SDIO_DEVICE_ID_BROADCOM_4329           0x4329
-#define SDIO_DEVICE_ID_BROADCOM_4330           0x4330
-#define SDIO_DEVICE_ID_BROADCOM_4334           0x4334
-#define SDIO_DEVICE_ID_BROADCOM_4335_4339      0x4335
-
 struct chip_core_info {
        u16 id;
        u16 rev;
@@ -224,7 +216,7 @@ struct sdpcmd_regs {
 };
 
 int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev,
-                          struct chip_info **ci_ptr, u32 regs);
+                          struct chip_info **ci_ptr);
 void brcmf_sdio_chip_detach(struct chip_info **ci_ptr);
 void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev,
                                       struct chip_info *ci, u32 drivestrength);
index fc0d4f0..092e9c8 100644 (file)
@@ -164,11 +164,9 @@ struct brcmf_sdio;
 struct brcmf_sdio_dev {
        struct sdio_func *func[SDIO_MAX_FUNCS];
        u8 num_funcs;                   /* Supported funcs on client */
-       u32 func_cis_ptr[SDIOD_MAX_IOFUNCS];
        u32 sbwad;                      /* Save backplane window address */
-       void *bus;
+       struct brcmf_sdio *bus;
        atomic_t suspend;               /* suspend flag */
-       wait_queue_head_t request_byte_wait;
        wait_queue_head_t request_word_wait;
        wait_queue_head_t request_buffer_wait;
        struct device *dev;
@@ -185,22 +183,19 @@ struct brcmf_sdio_dev {
 };
 
 /* Register/deregister interrupt handler. */
-int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev);
-int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev);
+int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev);
 
 /* sdio device register access interface */
-u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
-u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
-void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
-                     int *ret);
-void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
-                     int *ret);
-int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
-                           void *data, bool write);
+u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
+void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data,
+                      int *ret);
+void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data,
+                      int *ret);
 
 /* Buffer transfer to/from device (client) core via cmd53.
  *   fn:       function number
- *   addr:     backplane address (i.e. >= regsva from attach)
  *   flags:    backplane width, address increment, sync/async
  *   buf:      pointer to memory data buffer
  *   nbytes:   number of bytes to transfer to/from buf
@@ -210,17 +205,14 @@ int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
  * Returns 0 or error code.
  * NOTE: Async operation is not currently supported.
  */
-int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                         uint flags, struct sk_buff_head *pktq);
-int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                         uint flags, u8 *buf, uint nbytes);
-
-int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                         uint flags, struct sk_buff *pkt);
-int brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                         uint flags, u8 *buf, uint nbytes);
-int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
-                           uint flags, struct sk_buff_head *pktq, uint totlen);
+int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
+                        struct sk_buff_head *pktq);
+int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
+
+int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt);
+int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes);
+int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
+                          struct sk_buff_head *pktq, uint totlen);
 
 /* Flags bits */
 
@@ -236,43 +228,16 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
  *   nbytes:   number of bytes to transfer to/from buf
  * Returns 0 or error code.
  */
-int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr,
-                       u8 *buf, uint nbytes);
-int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
-                    u8 *data, uint size);
+int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
+                     u8 *data, uint size);
 
 /* Issue an abort to the specified function */
-int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
-
-/* platform specific/high level functions */
-int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
-int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev);
-
-/* attach, return handler on success, NULL if failed.
- *  The handler shall be provided by all subsequent calls. No local cache
- *  cfghdl points to the starting address of pci device mapped memory
- */
-int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev);
-void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev);
-
-/* read or write one byte using cmd52 */
-int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc,
-                            uint addr, u8 *byte);
-
-/* read or write 2/4 bytes using cmd53 */
-int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc,
-                            uint addr, u32 *word, uint nbyte);
-
-/* Watchdog timer interface for pm ops */
-void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable);
+int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn);
 
-void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev);
-void brcmf_sdbrcm_disconnect(void *ptr);
-void brcmf_sdbrcm_isr(void *arg);
+struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev);
+void brcmf_sdio_remove(struct brcmf_sdio *bus);
+void brcmf_sdio_isr(struct brcmf_sdio *bus);
 
-void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick);
+void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
 
-void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev,
-                         wait_queue_head_t *wq);
-bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev);
 #endif                         /* _BRCM_SDH_H_ */
index 51c4de0..c345c32 100644 (file)
@@ -1253,6 +1253,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
        bus->ops = &brcmf_usb_bus_ops;
        bus->chip = bus_pub->devid;
        bus->chiprev = bus_pub->chiprev;
+       bus->proto_type = BRCMF_PROTO_BCDC;
 
        /* Attach to the common driver interface */
        ret = brcmf_attach(dev);
index 3966fe0..aad83ae 100644 (file)
@@ -1095,10 +1095,10 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
                                             BRCMF_C_DISASSOC, NULL, 0);
                if (err) {
                        brcmf_err("WLC_DISASSOC failed (%d)\n", err);
-                       cfg80211_disconnected(vif->wdev.netdev, 0,
-                                             NULL, 0, GFP_KERNEL);
                }
                clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
+               cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL);
+
        }
        clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
        clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
@@ -1758,6 +1758,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
                return -EIO;
 
        clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
+       cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
 
        memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
        scbval.val = cpu_to_le32(reason_code);
@@ -4359,9 +4360,6 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
 {
        struct brcmf_cfg80211_vif *vif;
 
-       if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
-               return ERR_PTR(-ENOSPC);
-
        brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
                  sizeof(*vif));
        vif = kzalloc(sizeof(*vif), GFP_KERNEL);
@@ -4378,21 +4376,25 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
        brcmf_init_prof(&vif->profile);
 
        list_add_tail(&vif->list, &cfg->vif_list);
-       cfg->vif_cnt++;
        return vif;
 }
 
-void brcmf_free_vif(struct brcmf_cfg80211_info *cfg,
-                   struct brcmf_cfg80211_vif *vif)
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
 {
        list_del(&vif->list);
-       cfg->vif_cnt--;
-
        kfree(vif);
-       if (!cfg->vif_cnt) {
-               wiphy_unregister(cfg->wiphy);
-               wiphy_free(cfg->wiphy);
-       }
+}
+
+void brcmf_cfg80211_free_netdev(struct net_device *ndev)
+{
+       struct brcmf_cfg80211_vif *vif;
+       struct brcmf_if *ifp;
+
+       ifp = netdev_priv(ndev);
+       vif = ifp->vif;
+
+       brcmf_free_vif(vif);
+       free_netdev(ndev);
 }
 
 static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
@@ -4979,20 +4981,20 @@ cfg80211_p2p_attach_out:
        wl_deinit_priv(cfg);
 
 cfg80211_attach_out:
-       brcmf_free_vif(cfg, vif);
+       brcmf_free_vif(vif);
        return NULL;
 }
 
 void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
 {
-       struct brcmf_cfg80211_vif *vif;
-       struct brcmf_cfg80211_vif *tmp;
+       if (!cfg)
+               return;
 
-       wl_deinit_priv(cfg);
+       WARN_ON(!list_empty(&cfg->vif_list));
+       wiphy_unregister(cfg->wiphy);
        brcmf_btcoex_detach(cfg);
-       list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
-               brcmf_free_vif(cfg, vif);
-       }
+       wl_deinit_priv(cfg);
+       wiphy_free(cfg->wiphy);
 }
 
 static s32
@@ -5087,7 +5089,8 @@ dongle_scantime_out:
 }
 
 
-static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
+static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
+                                  u32 bw_cap[])
 {
        struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
        struct ieee80211_channel *band_chan_arr;
@@ -5100,7 +5103,6 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
        enum ieee80211_band band;
        u32 channel;
        u32 *n_cnt;
-       bool ht40_allowed;
        u32 index;
        u32 ht40_flag;
        bool update;
@@ -5133,18 +5135,17 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
                        array_size = ARRAY_SIZE(__wl_2ghz_channels);
                        n_cnt = &__wl_band_2ghz.n_channels;
                        band = IEEE80211_BAND_2GHZ;
-                       ht40_allowed = (bw_cap == WLC_N_BW_40ALL);
                } else if (ch.band == BRCMU_CHAN_BAND_5G) {
                        band_chan_arr = __wl_5ghz_a_channels;
                        array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
                        n_cnt = &__wl_band_5ghz_a.n_channels;
                        band = IEEE80211_BAND_5GHZ;
-                       ht40_allowed = !(bw_cap == WLC_N_BW_20ALL);
                } else {
-                       brcmf_err("Invalid channel Sepc. 0x%x.\n", ch.chspec);
+                       brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
                        continue;
                }
-               if (!ht40_allowed && ch.bw == BRCMU_CHAN_BW_40)
+               if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
+                   ch.bw == BRCMU_CHAN_BW_40)
                        continue;
                update = false;
                for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
@@ -5162,7 +5163,10 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
                                ieee80211_channel_to_frequency(ch.chnum, band);
                        band_chan_arr[index].hw_value = ch.chnum;
 
-                       if (ch.bw == BRCMU_CHAN_BW_40 && ht40_allowed) {
+                       brcmf_err("channel %d: f=%d bw=%d sb=%d\n",
+                                 ch.chnum, band_chan_arr[index].center_freq,
+                                 ch.bw, ch.sb);
+                       if (ch.bw == BRCMU_CHAN_BW_40) {
                                /* assuming the order is HT20, HT40 Upper,
                                 * HT40 lower from chanspecs
                                 */
@@ -5213,6 +5217,46 @@ exit:
        return err;
 }
 
+static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
+{
+       u32 band, mimo_bwcap;
+       int err;
+
+       band = WLC_BAND_2G;
+       err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
+       if (!err) {
+               bw_cap[IEEE80211_BAND_2GHZ] = band;
+               band = WLC_BAND_5G;
+               err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
+               if (!err) {
+                       bw_cap[IEEE80211_BAND_5GHZ] = band;
+                       return;
+               }
+               WARN_ON(1);
+               return;
+       }
+       brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
+       mimo_bwcap = 0;
+       err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
+       if (err)
+               /* assume 20MHz if firmware does not give a clue */
+               mimo_bwcap = WLC_N_BW_20ALL;
+
+       switch (mimo_bwcap) {
+       case WLC_N_BW_40ALL:
+               bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
+               /* fall-thru */
+       case WLC_N_BW_20IN2G_40IN5G:
+               bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
+               /* fall-thru */
+       case WLC_N_BW_20ALL:
+               bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
+               bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
+               break;
+       default:
+               brcmf_err("invalid mimo_bw_cap value\n");
+       }
+}
 
 static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
 {
@@ -5221,13 +5265,13 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        s32 phy_list;
        u32 band_list[3];
        u32 nmode;
-       u32 bw_cap = 0;
+       u32 bw_cap[2] = { 0, 0 };
        s8 phy;
        s32 err;
        u32 nband;
        s32 i;
-       struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
-       s32 index;
+       struct ieee80211_supported_band *bands[2] = { NULL, NULL };
+       struct ieee80211_supported_band *band;
 
        err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
                                     &phy_list, sizeof(phy_list));
@@ -5253,11 +5297,10 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        if (err) {
                brcmf_err("nmode error (%d)\n", err);
        } else {
-               err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &bw_cap);
-               if (err)
-                       brcmf_err("mimo_bw_cap error (%d)\n", err);
+               brcmf_get_bwcap(ifp, bw_cap);
        }
-       brcmf_dbg(INFO, "nmode=%d, mimo_bw_cap=%d\n", nmode, bw_cap);
+       brcmf_dbg(INFO, "nmode=%d, bw_cap=(%d, %d)\n", nmode,
+                 bw_cap[IEEE80211_BAND_2GHZ], bw_cap[IEEE80211_BAND_5GHZ]);
 
        err = brcmf_construct_reginfo(cfg, bw_cap);
        if (err) {
@@ -5266,40 +5309,33 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
        }
 
        nband = band_list[0];
-       memset(bands, 0, sizeof(bands));
 
        for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
-               index = -1;
+               band = NULL;
                if ((band_list[i] == WLC_BAND_5G) &&
-                   (__wl_band_5ghz_a.n_channels > 0)) {
-                       index = IEEE80211_BAND_5GHZ;
-                       bands[index] = &__wl_band_5ghz_a;
-                       if ((bw_cap == WLC_N_BW_40ALL) ||
-                           (bw_cap == WLC_N_BW_20IN2G_40IN5G))
-                               bands[index]->ht_cap.cap |=
-                                                       IEEE80211_HT_CAP_SGI_40;
-               } else if ((band_list[i] == WLC_BAND_2G) &&
-                          (__wl_band_2ghz.n_channels > 0)) {
-                       index = IEEE80211_BAND_2GHZ;
-                       bands[index] = &__wl_band_2ghz;
-                       if (bw_cap == WLC_N_BW_40ALL)
-                               bands[index]->ht_cap.cap |=
-                                                       IEEE80211_HT_CAP_SGI_40;
-               }
+                   (__wl_band_5ghz_a.n_channels > 0))
+                       band = &__wl_band_5ghz_a;
+               else if ((band_list[i] == WLC_BAND_2G) &&
+                        (__wl_band_2ghz.n_channels > 0))
+                       band = &__wl_band_2ghz;
+               else
+                       continue;
 
-               if ((index >= 0) && nmode) {
-                       bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
-                       bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
-                       bands[index]->ht_cap.ht_supported = true;
-                       bands[index]->ht_cap.ampdu_factor =
-                                               IEEE80211_HT_MAX_AMPDU_64K;
-                       bands[index]->ht_cap.ampdu_density =
-                                               IEEE80211_HT_MPDU_DENSITY_16;
-                       /* An HT shall support all EQM rates for one spatial
-                        * stream
-                        */
-                       bands[index]->ht_cap.mcs.rx_mask[0] = 0xff;
+               if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
+                       band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
+                       band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
                }
+               band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
+               band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
+               band->ht_cap.ht_supported = true;
+               band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+               band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
+               /* An HT shall support all EQM rates for one spatial
+                * stream
+                */
+               band->ht_cap.mcs.rx_mask[0] = 0xff;
+               band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
+               bands[band->band] = band;
        }
 
        wiphy = cfg_to_wiphy(cfg);
index d9bdaf9..2dc6a07 100644 (file)
@@ -412,7 +412,6 @@ struct brcmf_cfg80211_info {
        struct work_struct escan_timeout_work;
        u8 *escan_ioctl_buf;
        struct list_head vif_list;
-       u8 vif_cnt;
        struct brcmf_cfg80211_vif_event vif_event;
        struct completion vif_disabled;
        struct brcmu_d11inf d11inf;
@@ -487,8 +486,7 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp);
 struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
                                           enum nl80211_iftype type,
                                           bool pm_block);
-void brcmf_free_vif(struct brcmf_cfg80211_info *cfg,
-                   struct brcmf_cfg80211_vif *vif);
+void brcmf_free_vif(struct brcmf_cfg80211_vif *vif);
 
 s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
                          const u8 *vndr_ie_buf, u32 vndr_ie_len);
@@ -507,5 +505,6 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
                                bool fw_abort);
 void brcmf_set_mpc(struct brcmf_if *ndev, int mpc);
 void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg);
+void brcmf_cfg80211_free_netdev(struct net_device *ndev);
 
 #endif                         /* _wl_cfg80211_h_ */
index 84113ea..6fa5d48 100644 (file)
@@ -41,6 +41,7 @@
 #define BCM4331_CHIP_ID                0x4331
 #define BCM4334_CHIP_ID                0x4334
 #define BCM4335_CHIP_ID                0x4335
+#define BCM43362_CHIP_ID       43362
 #define BCM4339_CHIP_ID                0x4339
 
 #endif                         /* _BRCM_HW_IDS_H_ */
index 0505cc0..7ca2aa1 100644 (file)
 #define WLC_N_BW_40ALL                 1
 #define WLC_N_BW_20IN2G_40IN5G         2
 
+#define WLC_BW_20MHZ_BIT               BIT(0)
+#define WLC_BW_40MHZ_BIT               BIT(1)
+#define WLC_BW_80MHZ_BIT               BIT(2)
+#define WLC_BW_160MHZ_BIT              BIT(3)
+
+/* Bandwidth capabilities */
+#define WLC_BW_CAP_20MHZ               (WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_40MHZ               (WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_80MHZ               (WLC_BW_80MHZ_BIT|WLC_BW_40MHZ_BIT| \
+                                        WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_160MHZ              (WLC_BW_160MHZ_BIT|WLC_BW_80MHZ_BIT| \
+                                        WLC_BW_40MHZ_BIT|WLC_BW_20MHZ_BIT)
+#define WLC_BW_CAP_UNRESTRICTED                0xFF
+
 /* band types */
 #define        WLC_BAND_AUTO                   0       /* auto-select */
 #define        WLC_BAND_5G                     1       /* 5 Ghz */
index acdff0f..5a9ffd3 100644 (file)
@@ -14,7 +14,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/vmalloc.h>
 #include <linux/sched.h>
 #include <linux/firmware.h>
index 1be819d..3e78cc3 100644 (file)
@@ -21,7 +21,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
index b37abb9..6907c8f 100644 (file)
@@ -225,7 +225,7 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
                cw1200_set_pm(priv, &priv->powersave_mode);
                if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
                                                     !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
-                       goto revert3;
+                       goto revert4;
                }
        }
 
@@ -254,11 +254,11 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        /* Stop serving thread */
        if (cw1200_bh_suspend(priv))
-               goto revert4;
+               goto revert5;
 
        ret = timer_pending(&priv->mcast_timeout);
        if (ret)
-               goto revert5;
+               goto revert6;
 
        /* Store suspend state */
        pm_state->suspend_state = state;
@@ -280,9 +280,9 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 
        return 0;
 
-revert5:
+revert6:
        WARN_ON(cw1200_bh_resume(priv));
-revert4:
+revert5:
        cw1200_resume_work(priv, &priv->bss_loss_work,
                           state->bss_loss_tmo);
        cw1200_resume_work(priv, &priv->join_timeout,
@@ -291,6 +291,7 @@ revert4:
                           state->direct_probe);
        cw1200_resume_work(priv, &priv->link_id_gc_work,
                           state->link_id_gc);
+revert4:
        kfree(state);
 revert3:
        wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
index 56cd01c..9f825f2 100644 (file)
@@ -1,7 +1,6 @@
 #define PRISM2_PCCARD
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/if.h>
 #include <linux/slab.h>
 #include <linux/wait.h>
index e509030..63e350a 100644 (file)
@@ -2567,7 +2567,7 @@ static int prism2_ioctl_priv_prism2_param(struct net_device *dev,
                local->passive_scan_interval = value;
                if (timer_pending(&local->passive_scan_timer))
                        del_timer(&local->passive_scan_timer);
-               if (value > 0) {
+               if (value > 0 && value < INT_MAX / HZ) {
                        local->passive_scan_timer.expires = jiffies +
                                local->passive_scan_interval * HZ;
                        add_timer(&local->passive_scan_timer);
index 05ca340..91158e2 100644 (file)
@@ -5,7 +5,6 @@
  * Andy Warner <andyw@pobox.com> */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/if.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
index c3d067e..3bf530d 100644 (file)
@@ -8,7 +8,6 @@
 
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/if.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
index 570d6fb..aa301d1 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/mutex.h>
 
index 9ffe659..ce27859 100644 (file)
@@ -1468,7 +1468,7 @@ static inline int is_same_network(struct libipw_network *src,
         * as one network */
        return ((src->ssid_len == dst->ssid_len) &&
                (src->channel == dst->channel) &&
-               ether_addr_equal(src->bssid, dst->bssid) &&
+               ether_addr_equal_64bits(src->bssid, dst->bssid) &&
                !memcmp(src->ssid, dst->ssid, src->ssid_len));
 }
 
index f767dd1..c1b4441 100644 (file)
@@ -48,7 +48,7 @@ il3945_stats_flag(struct il_priv *il, char *buf, int bufsz)
        return p;
 }
 
-ssize_t
+static ssize_t
 il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -313,7 +313,7 @@ il3945_ucode_rx_stats_read(struct file *file, char __user *user_buf,
        return ret;
 }
 
-ssize_t
+static ssize_t
 il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -403,7 +403,7 @@ il3945_ucode_tx_stats_read(struct file *file, char __user *user_buf,
        return ret;
 }
 
-ssize_t
+static ssize_t
 il3945_ucode_general_stats_read(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
index aea667b..9a45f6f 100644 (file)
@@ -25,7 +25,6 @@
  *****************************************************************************/
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <net/mac80211.h>
index f09e257..d37a6fd 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
@@ -466,10 +465,10 @@ il3945_is_network_packet(struct il_priv *il, struct ieee80211_hdr *header)
        switch (il->iw_mode) {
        case NL80211_IFTYPE_ADHOC:      /* Header: Dest. | Source    | BSSID */
                /* packets to our IBSS update information */
-               return ether_addr_equal(header->addr3, il->bssid);
+               return ether_addr_equal_64bits(header->addr3, il->bssid);
        case NL80211_IFTYPE_STATION:    /* Header: Dest. | AP{BSSID} | Source */
                /* packets to our IBSS update information */
-               return ether_addr_equal(header->addr2, il->bssid);
+               return ether_addr_equal_64bits(header->addr2, il->bssid);
        default:
                return 1;
        }
index c8153fc..e0597bf 100644 (file)
@@ -55,7 +55,7 @@ il4965_stats_flag(struct il_priv *il, char *buf, int bufsz)
        return p;
 }
 
-ssize_t
+static ssize_t
 il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -467,7 +467,7 @@ il4965_ucode_rx_stats_read(struct file *file, char __user *user_buf,
        return ret;
 }
 
-ssize_t
+static ssize_t
 il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf,
                           size_t count, loff_t *ppos)
 {
@@ -633,7 +633,7 @@ il4965_ucode_tx_stats_read(struct file *file, char __user *user_buf,
        return ret;
 }
 
-ssize_t
+static ssize_t
 il4965_ucode_general_stats_read(struct file *file, char __user *user_buf,
                                size_t count, loff_t *ppos)
 {
index 3ccbaf7..4d5e332 100644 (file)
@@ -24,7 +24,6 @@
  *
  *****************************************************************************/
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <net/mac80211.h>
index 777a578..fe47db9 100644 (file)
@@ -26,7 +26,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
index a27b14c..02e8233 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/lockdep.h>
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
@@ -3746,10 +3745,10 @@ il_full_rxon_required(struct il_priv *il)
 
        /* These items are only settable from the full RXON command */
        CHK(!il_is_associated(il));
-       CHK(!ether_addr_equal(staging->bssid_addr, active->bssid_addr));
-       CHK(!ether_addr_equal(staging->node_addr, active->node_addr));
-       CHK(!ether_addr_equal(staging->wlap_bssid_addr,
-                             active->wlap_bssid_addr));
+       CHK(!ether_addr_equal_64bits(staging->bssid_addr, active->bssid_addr));
+       CHK(!ether_addr_equal_64bits(staging->node_addr, active->node_addr));
+       CHK(!ether_addr_equal_64bits(staging->wlap_bssid_addr,
+                                    active->wlap_bssid_addr));
        CHK_NEQ(staging->dev_type, active->dev_type);
        CHK_NEQ(staging->channel, active->channel);
        CHK_NEQ(staging->air_propagation, active->air_propagation);
index 3a487a3..3440101 100644 (file)
@@ -31,7 +31,7 @@
 
 #include "common.h"
 
-void
+static void
 il_clear_traffic_stats(struct il_priv *il)
 {
        memset(&il->tx_stats, 0, sizeof(struct traffic_stats));
index 23d5f02..562772d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 1b0f0d5..be1086c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index cfddde1..aeae4e8 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index ebdac90..751ae1d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index f69301e..d2fe259 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index 7434d9e..3441f70 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 352c6cb..7b140e4 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 33c7e15..ca4d669 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -27,7 +27,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
index 8749dcf..6a0817d 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 3d5bdc4..576f7ee 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -29,7 +29,6 @@
 #include <linux/etherdevice.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/sched.h>
 #include <net/mac80211.h>
 
index 217f1ca..40eb5e6 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -28,7 +28,6 @@
  *****************************************************************************/
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 #include <linux/delay.h>
@@ -322,12 +321,6 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw)
 
        flush_workqueue(priv->workqueue);
 
-       /* User space software may expect getting rfkill changes
-        * even if interface is down, trans->down will leave the RF
-        * kill interrupt enabled
-        */
-       iwl_trans_stop_hw(priv->trans, false);
-
        IWL_DEBUG_MAC80211(priv, "leave\n");
 }
 
index 7aad766..ba1b1ea 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -1313,7 +1313,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans,
        }
 
        /* Reset chip to save power until we load uCode during "up". */
-       iwl_trans_stop_hw(priv->trans, false);
+       iwl_trans_stop_device(priv->trans);
 
        priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg,
                                                  priv->eeprom_blob,
@@ -1458,7 +1458,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode)
 
        dev_kfree_skb(priv->beacon_skb);
 
-       iwl_trans_stop_hw(priv->trans, true);
+       iwl_trans_op_mode_leave(priv->trans);
        ieee80211_free_hw(priv->hw);
 }
 
index 77cb597..b4e6141 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -30,7 +30,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <net/mac80211.h>
 #include "iwl-io.h"
 #include "iwl-debug.h"
index 7b03e13..570d3a5 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index b647e50..0977d93 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -24,7 +24,6 @@
  *
  *****************************************************************************/
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <net/mac80211.h>
index 41988f4..bdd5644 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index d71776d..b68bb2f 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portionhelp of the ieee80211 subsystem header files.
index d7ce2f1..503a81e 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 928f864..be98b91 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
index c3c13ce..c0d070c 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index fbeee08..058c589 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -30,7 +30,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <net/mac80211.h>
 #include "iwl-io.h"
 #include "iwl-modparams.h"
index 9356c4b..5077265 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
index e12b1a6..a6839df 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -29,7 +29,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/ieee80211.h>
 #include "iwl-io.h"
index 6363794..f59709a 100644 (file)
@@ -2,7 +2,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -28,7 +28,6 @@
  *****************************************************************************/
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 
 #include "iwl-io.h"
 #include "iwl-agn-hw.h"
index 0d2afe0..854ba84 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index c727ec7..3e63323 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index ecc01e1..6674f2c 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 8ac305b..8048de9 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 3c34a72..2a59da2 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -108,7 +108,7 @@ static const struct iwl_base_params iwl7000_base_params = {
 };
 
 static const struct iwl_ht_params iwl7000_ht_params = {
-       .use_rts_for_aggregation = true, /* use rts/cts protection */
+       .stbc = true,
        .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ),
 };
 
index 6d73f94..7f37fb8 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 0b91624..1ced525 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -162,12 +162,14 @@ struct iwl_base_params {
 };
 
 /*
+ * @stbc: support Tx STBC and 1*SS Rx STBC
  * @use_rts_for_aggregation: use rts/cts protection for HT traffic
  * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40
  */
 struct iwl_ht_params {
        enum ieee80211_smps_mode smps_mode;
        const bool ht_greenfield_support; /* if used set to true */
+       const bool stbc;
        bool use_rts_for_aggregation;
        u8 ht40_bands;
 };
index da4eca8..9d32551 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
                                 CSR_INT_BIT_RF_KILL | \
                                 CSR_INT_BIT_SW_RX   | \
                                 CSR_INT_BIT_WAKEUP  | \
-                                CSR_INT_BIT_ALIVE)
+                                CSR_INT_BIT_ALIVE   | \
+                                CSR_INT_BIT_RX_PERIODIC)
 
 /* interrupt flags in FH (flow handler) (PCI busmaster DMA) */
 #define CSR_FH_INT_BIT_ERR       (1 << 31) /* Error */
index b2bb32a..a75aac9 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
index 8f61c71..23e7351 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index 684c416..78bd41b 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2009 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
index ff57002..c372816 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -322,6 +322,41 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces,
        pieces->img[type].sec[sec].offset = offset;
 }
 
+static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len)
+{
+       int i, j;
+       struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data;
+       struct iwl_fw_cipher_scheme *fwcs;
+       struct ieee80211_cipher_scheme *cs;
+       u32 cipher;
+
+       if (len < sizeof(*l) ||
+           len < sizeof(l->size) + l->size * sizeof(l->cs[0]))
+               return -EINVAL;
+
+       for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) {
+               fwcs = &l->cs[j];
+               cipher = le32_to_cpu(fwcs->cipher);
+
+               /* we skip schemes with zero cipher suite selector */
+               if (!cipher)
+                       continue;
+
+               cs = &fw->cs[j++];
+               cs->cipher = cipher;
+               cs->iftype = BIT(NL80211_IFTYPE_STATION);
+               cs->hdr_len = fwcs->hdr_len;
+               cs->pn_len = fwcs->pn_len;
+               cs->pn_off = fwcs->pn_off;
+               cs->key_idx_off = fwcs->key_idx_off;
+               cs->key_idx_mask = fwcs->key_idx_mask;
+               cs->key_idx_shift = fwcs->key_idx_shift;
+               cs->mic_len = fwcs->mic_len;
+       }
+
+       return 0;
+}
+
 /*
  * Gets uCode section from tlv.
  */
@@ -729,6 +764,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                                return -EINVAL;
                        }
                        break;
+               case IWL_UCODE_TLV_CSCHEME:
+                       if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
+                               goto invalid_tlv_len;
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;
index 429337a..592c01e 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -67,7 +67,7 @@
 /* for all modules */
 #define DRV_NAME        "iwlwifi"
 #define IWLWIFI_VERSION "in-tree:"
-#define DRV_COPYRIGHT  "Copyright(c) 2003-2013 Intel Corporation"
+#define DRV_COPYRIGHT  "Copyright(c) 2003- 2014 Intel Corporation"
 #define DRV_AUTHOR     "<ilw@linux.intel.com>"
 
 
index f4a6d31..c44cf11 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -751,6 +751,13 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
        ht_info->ht_supported = true;
        ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40;
 
+       if (cfg->ht_params->stbc) {
+               ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
+
+               if (tx_chains > 1)
+                       ht_info->cap |= IEEE80211_HT_CAP_TX_STBC;
+       }
+
        if (iwlwifi_mod_params.amsdu_size_8K)
                ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU;
 
index d73304a..e3c7dea 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index e5f2e36..25d0105 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 8e941f8..a6d3bdf 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 484d318..9564ae1 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 6c6c35c..88e2d6e 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -125,6 +125,7 @@ enum iwl_ucode_tlv_type {
        IWL_UCODE_TLV_SECURE_SEC_INIT   = 25,
        IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26,
        IWL_UCODE_TLV_NUM_OF_CPU        = 27,
+       IWL_UCODE_TLV_CSCHEME           = 28,
 };
 
 struct iwl_ucode_tlv {
index 75db087..5f1493c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -92,6 +92,9 @@
  * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
  * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command
  *     containing CAM (Continuous Active Mode) indication.
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a
+ *     single bound interface).
+ * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
  */
 enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_PAN                 = BIT(0),
@@ -113,7 +116,9 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
        IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
        IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD       = BIT(20),
+       IWL_UCODE_TLV_FLAGS_P2P_PS              = BIT(21),
        IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT       = BIT(24),
+       IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD        = BIT(26),
 };
 
 /* The default calibrate table size if not specified by firmware file */
@@ -209,6 +214,44 @@ enum iwl_fw_phy_cfg {
        FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS,
 };
 
+#define IWL_UCODE_MAX_CS               1
+
+/**
+ * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW.
+ * @cipher: a cipher suite selector
+ * @flags: cipher scheme flags (currently reserved for a future use)
+ * @hdr_len: a size of MPDU security header
+ * @pn_len: a size of PN
+ * @pn_off: an offset of pn from the beginning of the security header
+ * @key_idx_off: an offset of key index byte in the security header
+ * @key_idx_mask: a bit mask of key_idx bits
+ * @key_idx_shift: bit shift needed to get key_idx
+ * @mic_len: mic length in bytes
+ * @hw_cipher: a HW cipher index used in host commands
+ */
+struct iwl_fw_cipher_scheme {
+       __le32 cipher;
+       u8 flags;
+       u8 hdr_len;
+       u8 pn_len;
+       u8 pn_off;
+       u8 key_idx_off;
+       u8 key_idx_mask;
+       u8 key_idx_shift;
+       u8 mic_len;
+       u8 hw_cipher;
+} __packed;
+
+/**
+ * struct iwl_fw_cscheme_list - a cipher scheme list
+ * @size: a number of entries
+ * @cs: cipher scheme entries
+ */
+struct iwl_fw_cscheme_list {
+       u8 size;
+       struct iwl_fw_cipher_scheme cs[];
+} __packed;
+
 /**
  * struct iwl_fw - variables associated with the firmware
  *
@@ -224,6 +267,7 @@ enum iwl_fw_phy_cfg {
  * @inst_evtlog_size: event log size for runtime ucode.
  * @inst_errlog_ptr: error log offfset for runtime ucode.
  * @mvm_fw: indicates this is MVM firmware
+ * @cipher_scheme: optional external cipher scheme.
  */
 struct iwl_fw {
        u32 ucode_ver;
@@ -243,6 +287,8 @@ struct iwl_fw {
        u32 phy_config;
 
        bool mvm_fw;
+
+       struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
 };
 
 static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw)
index ad8e19a..f98175a 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
index 63d10ec..c339c1b 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project.
  *
index a1f580c..0a84ade 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 940b8a9..b5bc959 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 2e2f1c8..95af97a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 94aef22..1b61cb5 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -263,13 +263,20 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
                                  struct iwl_nvm_data *data,
                                  struct ieee80211_sta_vht_cap *vht_cap)
 {
+       int num_ants = num_of_ant(data->valid_rx_ant);
+       int bf_sts_cap = num_ants - 1;
+
        vht_cap->vht_supported = true;
 
        vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 |
                       IEEE80211_VHT_CAP_RXSTBC_1 |
                       IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                      bf_sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT |
                       7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
 
+       if (num_ants > 1)
+               vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
+
        if (iwlwifi_mod_params.amsdu_size_8K)
                vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
 
@@ -283,16 +290,22 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg,
                            IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 |
                            IEEE80211_VHT_MCS_NOT_SUPPORTED << 14);
 
-       if (num_of_ant(data->valid_rx_ant) == 1 ||
+       /* Max rate for Long GI NSS=2 80Mhz is 780Mbps */
+       vht_cap->vht_mcs.rx_highest = cpu_to_le16(780);
+
+       if (num_ants == 1 ||
            cfg->rx_with_siso_diversity) {
                vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
                                IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN;
                /* this works because NOT_SUPPORTED == 3 */
                vht_cap->vht_mcs.rx_mcs_map |=
                        cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2);
+               /* Max rate for Long GI NSS=1 80Mhz is 390Mbps */
+               vht_cap->vht_mcs.rx_highest = cpu_to_le16(390);
        }
 
        vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map;
+       vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest;
 }
 
 static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
index 3325059..0c4399a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2008 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 976448a..b5be51f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -155,14 +155,12 @@ void iwl_opmode_deregister(const char *name);
 
 /**
  * struct iwl_op_mode - operational mode
+ * @ops - pointer to its own ops
  *
  * This holds an implementation of the mac80211 / fw API.
- *
- * @ops - pointer to its own ops
  */
 struct iwl_op_mode {
        const struct iwl_op_mode_ops *ops;
-       const struct iwl_trans *trans;
 
        char op_mode_specific[0] __aligned(sizeof(void *));
 };
index 1a405ae..fa77d63 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index ce983af..9ee18d0 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index f6412da..d69b0fb 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 143292b..8d1b5ed 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -70,6 +70,7 @@
 #include "iwl-debug.h"
 #include "iwl-config.h"
 #include "iwl-fw.h"
+#include "iwl-op-mode.h"
 
 /**
  * DOC: Transport layer - what is it ?
  *        start_fw
  *
  *     5) Then when finished (or reset):
- *        stop_fw (a.k.a. stop device for the moment)
- *        stop_hw
+ *        stop_device
  *
  *     6) Eventually, the free function will be called.
  */
@@ -318,6 +318,24 @@ enum iwl_d3_status {
 };
 
 /**
+ * enum iwl_trans_status: transport status flags
+ * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed
+ * @STATUS_DEVICE_ENABLED: APM is enabled
+ * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
+ * @STATUS_INT_ENABLED: interrupts are enabled
+ * @STATUS_RFKILL: the HW RFkill switch is in KILL position
+ * @STATUS_FW_ERROR: the fw is in error state
+ */
+enum iwl_trans_status {
+       STATUS_SYNC_HCMD_ACTIVE,
+       STATUS_DEVICE_ENABLED,
+       STATUS_TPOWER_PMI,
+       STATUS_INT_ENABLED,
+       STATUS_RFKILL,
+       STATUS_FW_ERROR,
+};
+
+/**
  * struct iwl_trans_config - transport configuration
  *
  * @op_mode: pointer to the upper layer.
@@ -361,9 +379,7 @@ struct iwl_trans;
  *
  * @start_hw: starts the HW- from that point on, the HW can send interrupts
  *     May sleep
- * @stop_hw: stops the HW- from that point on, the HW will be in low power but
- *     will still issue interrupt if the HW RF kill is triggered unless
- *     op_mode_leaving is true.
+ * @op_mode_leave: Turn off the HW RF kill indication if on
  *     May sleep
  * @start_fw: allocates and inits all the resources for the transport
  *     layer. Also kick a fw image.
@@ -371,8 +387,11 @@ struct iwl_trans;
  * @fw_alive: called when the fw sends alive notification. If the fw provides
  *     the SCD base address in SRAM, then provide it here, or 0 otherwise.
  *     May sleep
- * @stop_device:stops the whole device (embedded CPU put to reset)
- *     May sleep
+ * @stop_device: stops the whole device (embedded CPU put to reset) and stops
+ *     the HW. From that point on, the HW will be in low power but will still
+ *     issue interrupt if the HW RF kill is triggered. This callback must do
+ *     the right thing and not crash even if start_hw() was called but not
+ *     start_fw(). May sleep
  * @d3_suspend: put the device into the correct mode for WoWLAN during
  *     suspend. This is optional, if not implemented WoWLAN will not be
  *     supported. This callback may sleep.
@@ -418,7 +437,7 @@ struct iwl_trans;
 struct iwl_trans_ops {
 
        int (*start_hw)(struct iwl_trans *iwl_trans);
-       void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving);
+       void (*op_mode_leave)(struct iwl_trans *iwl_trans);
        int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw,
                        bool run_in_rfkill);
        void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
@@ -479,6 +498,7 @@ enum iwl_trans_state {
  * @ops - pointer to iwl_trans_ops
  * @op_mode - pointer to the op_mode
  * @cfg - pointer to the configuration
+ * @status: a bit-mask of transport status flags
  * @dev - pointer to struct device * that represents the device
  * @hw_id: a u32 with the ID of the device / subdevice.
  *     Set during transport allocation.
@@ -499,6 +519,7 @@ struct iwl_trans {
        struct iwl_op_mode *op_mode;
        const struct iwl_cfg *cfg;
        enum iwl_trans_state state;
+       unsigned long status;
 
        struct device *dev;
        u32 hw_rev;
@@ -540,15 +561,14 @@ static inline int iwl_trans_start_hw(struct iwl_trans *trans)
        return trans->ops->start_hw(trans);
 }
 
-static inline void iwl_trans_stop_hw(struct iwl_trans *trans,
-                                    bool op_mode_leaving)
+static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans)
 {
        might_sleep();
 
-       trans->ops->stop_hw(trans, op_mode_leaving);
+       if (trans->ops->op_mode_leave)
+               trans->ops->op_mode_leave(trans);
 
-       if (op_mode_leaving)
-               trans->op_mode = NULL;
+       trans->op_mode = NULL;
 
        trans->state = IWL_TRANS_NO_FW;
 }
@@ -570,6 +590,7 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans,
 
        WARN_ON_ONCE(!trans->rx_mpdu_cmd);
 
+       clear_bit(STATUS_FW_ERROR, &trans->status);
        return trans->ops->start_fw(trans, fw, run_in_rfkill);
 }
 
@@ -601,6 +622,13 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans,
 {
        int ret;
 
+       if (unlikely(!(cmd->flags & CMD_SEND_IN_RFKILL) &&
+                    test_bit(STATUS_RFKILL, &trans->status)))
+               return -ERFKILL;
+
+       if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+               return -EIO;
+
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) {
                IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
                return -EIO;
@@ -640,6 +668,9 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans,
 static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb,
                               struct iwl_device_cmd *dev_cmd, int queue)
 {
+       if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status)))
+               return -EIO;
+
        if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
                IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
 
@@ -657,9 +688,6 @@ static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue,
 
 static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue)
 {
-       if (unlikely(trans->state != IWL_TRANS_FW_ALIVE))
-               IWL_ERR(trans, "%s bad state = %d", __func__, trans->state);
-
        trans->ops->txq_disable(trans, queue);
 }
 
@@ -760,7 +788,8 @@ static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr,
 
 static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state)
 {
-       trans->ops->set_pmi(trans, state);
+       if (trans->ops->set_pmi)
+               trans->ops->set_pmi(trans, state);
 }
 
 static inline void
@@ -780,6 +809,16 @@ iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags)
        __release(nic_access);
 }
 
+static inline void iwl_trans_fw_error(struct iwl_trans *trans)
+{
+       if (WARN_ON_ONCE(!trans->op_mode))
+               return;
+
+       /* prevent double restarts due to the same erroneous FW */
+       if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status))
+               iwl_op_mode_nic_error(trans->op_mode);
+}
+
 /*****************************************************
 * driver (transport) register/unregister functions
 ******************************************************/
index 285d8c7..f98ec2b 100644 (file)
@@ -1,10 +1,9 @@
 obj-$(CONFIG_IWLMVM)   += iwlmvm.o
 iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
-iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o
+iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
 iwlmvm-y += scan.o time-event.o rs.o
 iwlmvm-y += power.o power_legacy.o bt-coex.o
 iwlmvm-y += led.o tt.o
-iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o
 iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
 iwlmvm-$(CONFIG_PM_SLEEP) += d3.o
 
index 93fd145..a137653 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
                return -EINVAL;
 
+       /*
+        * Update SF - Disable if needed. if this fails, SF might still be on
+        * while many macs are bound, which is forbidden - so fail the binding.
+        */
+       if (iwl_mvm_sf_update(mvm, vif, false))
+               return -EINVAL;
+
        return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true);
 }
 
 int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
 
        if (WARN_ON_ONCE(!mvmvif->phy_ctxt))
                return -EINVAL;
 
-       return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+       ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false);
+
+       if (!ret)
+               if (iwl_mvm_sf_update(mvm, vif, true))
+                       IWL_ERR(mvm, "Failed to update SF state\n");
+
+       return ret;
 }
index d126245..76cde6c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -294,9 +294,9 @@ static const __le64 iwl_ci_mask[][3] = {
                cpu_to_le64(0x0)
        },
        {
-               cpu_to_le64(0xFE00000000ULL),
+               cpu_to_le64(0xFFC0000000ULL),
                cpu_to_le64(0x0ULL),
-               cpu_to_le64(0x0)
+               cpu_to_le64(0x0ULL)
        },
 };
 
index 4b6d670..0368576 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 665f87e..f04d2f4 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 27f140c..0e29cd8 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "mvm.h"
 #include "debugfs.h"
 
+static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                enum iwl_dbgfs_pm_mask param, int val)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm;
+
+       dbgfs_pm->mask |= param;
+
+       switch (param) {
+       case MVM_DEBUGFS_PM_KEEP_ALIVE: {
+               struct ieee80211_hw *hw = mvm->hw;
+               int dtimper = hw->conf.ps_dtim_period ?: 1;
+               int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+               IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val);
+               if (val * MSEC_PER_SEC < 3 * dtimper_msec)
+                       IWL_WARN(mvm,
+                                "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n",
+                                val * MSEC_PER_SEC, 3 * dtimper_msec);
+               dbgfs_pm->keep_alive_seconds = val;
+               break;
+       }
+       case MVM_DEBUGFS_PM_SKIP_OVER_DTIM:
+               IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n",
+                               val ? "enabled" : "disabled");
+               dbgfs_pm->skip_over_dtim = val;
+               break;
+       case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS:
+               IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val);
+               dbgfs_pm->skip_dtim_periods = val;
+               break;
+       case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT:
+               IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val);
+               dbgfs_pm->rx_data_timeout = val;
+               break;
+       case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT:
+               IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val);
+               dbgfs_pm->tx_data_timeout = val;
+               break;
+       case MVM_DEBUGFS_PM_DISABLE_POWER_OFF:
+               IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val);
+               dbgfs_pm->disable_power_off = val;
+               break;
+       case MVM_DEBUGFS_PM_LPRX_ENA:
+               IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled");
+               dbgfs_pm->lprx_ena = val;
+               break;
+       case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD:
+               IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val);
+               dbgfs_pm->lprx_rssi_threshold = val;
+               break;
+       case MVM_DEBUGFS_PM_SNOOZE_ENABLE:
+               IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val);
+               dbgfs_pm->snooze_ena = val;
+               break;
+       case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING:
+               IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val);
+               dbgfs_pm->uapsd_misbehaving = val;
+               break;
+       }
+}
+
+static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       enum iwl_dbgfs_pm_mask param;
+       int val, ret;
+
+       if (!strncmp("keep_alive=", buf, 11)) {
+               if (sscanf(buf + 11, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_KEEP_ALIVE;
+       } else if (!strncmp("skip_over_dtim=", buf, 15)) {
+               if (sscanf(buf + 15, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM;
+       } else if (!strncmp("skip_dtim_periods=", buf, 18)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS;
+       } else if (!strncmp("rx_data_timeout=", buf, 16)) {
+               if (sscanf(buf + 16, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT;
+       } else if (!strncmp("tx_data_timeout=", buf, 16)) {
+               if (sscanf(buf + 16, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT;
+       } else if (!strncmp("disable_power_off=", buf, 18) &&
+                  !(mvm->fw->ucode_capa.flags &
+                    IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF;
+       } else if (!strncmp("lprx=", buf, 5)) {
+               if (sscanf(buf + 5, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_LPRX_ENA;
+       } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) {
+               if (sscanf(buf + 20, "%d", &val) != 1)
+                       return -EINVAL;
+               if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val <
+                   POWER_LPRX_RSSI_THRESHOLD_MIN)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD;
+       } else if (!strncmp("snooze_enable=", buf, 14)) {
+               if (sscanf(buf + 14, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_SNOOZE_ENABLE;
+       } else if (!strncmp("uapsd_misbehaving=", buf, 18)) {
+               if (sscanf(buf + 18, "%d", &val) != 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING;
+       } else {
+               return -EINVAL;
+       }
+
+       mutex_lock(&mvm->mutex);
+       iwl_dbgfs_update_pm(mvm, vif, param, val);
+       ret = iwl_mvm_power_update_mode(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_pm_params_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       char buf[512];
+       int bufsz = sizeof(buf);
+       int pos;
+
+       pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
 static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
                                         char __user *user_buf,
                                         size_t count, loff_t *ppos)
@@ -125,6 +269,201 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
 }
 
+static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif,
+                               enum iwl_dbgfs_bf_mask param, int value)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
+
+       dbgfs_bf->mask |= param;
+
+       switch (param) {
+       case MVM_DEBUGFS_BF_ENERGY_DELTA:
+               dbgfs_bf->bf_energy_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA:
+               dbgfs_bf->bf_roaming_energy_delta = value;
+               break;
+       case MVM_DEBUGFS_BF_ROAMING_STATE:
+               dbgfs_bf->bf_roaming_state = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_THRESHOLD:
+               dbgfs_bf->bf_temp_threshold = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_FAST_FILTER:
+               dbgfs_bf->bf_temp_fast_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER:
+               dbgfs_bf->bf_temp_slow_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER:
+               dbgfs_bf->bf_enable_beacon_filter = value;
+               break;
+       case MVM_DEBUGFS_BF_DEBUG_FLAG:
+               dbgfs_bf->bf_debug_flag = value;
+               break;
+       case MVM_DEBUGFS_BF_ESCAPE_TIMER:
+               dbgfs_bf->bf_escape_timer = value;
+               break;
+       case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT:
+               dbgfs_bf->ba_enable_beacon_abort = value;
+               break;
+       case MVM_DEBUGFS_BA_ESCAPE_TIMER:
+               dbgfs_bf->ba_escape_timer = value;
+               break;
+       }
+}
+
+static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = mvmvif->mvm;
+       enum iwl_dbgfs_bf_mask param;
+       int value, ret = 0;
+
+       if (!strncmp("bf_energy_delta=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ENERGY_DELTA_MIN ||
+                   value > IWL_BF_ENERGY_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ENERGY_DELTA;
+       } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) {
+               if (sscanf(buf+24, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN ||
+                   value > IWL_BF_ROAMING_ENERGY_DELTA_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA;
+       } else if (!strncmp("bf_roaming_state=", buf, 17)) {
+               if (sscanf(buf+17, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ROAMING_STATE_MIN ||
+                   value > IWL_BF_ROAMING_STATE_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ROAMING_STATE;
+       } else if (!strncmp("bf_temp_threshold=", buf, 18)) {
+               if (sscanf(buf+18, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_THRESHOLD_MIN ||
+                   value > IWL_BF_TEMP_THRESHOLD_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_THRESHOLD;
+       } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_FAST_FILTER_MIN ||
+                   value > IWL_BF_TEMP_FAST_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER;
+       } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) {
+               if (sscanf(buf+20, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_TEMP_SLOW_FILTER_MIN ||
+                   value > IWL_BF_TEMP_SLOW_FILTER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER;
+       } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) {
+               if (sscanf(buf+24, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER;
+       } else if (!strncmp("bf_debug_flag=", buf, 14)) {
+               if (sscanf(buf+14, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_DEBUG_FLAG;
+       } else if (!strncmp("bf_escape_timer=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BF_ESCAPE_TIMER_MIN ||
+                   value > IWL_BF_ESCAPE_TIMER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BF_ESCAPE_TIMER;
+       } else if (!strncmp("ba_escape_timer=", buf, 16)) {
+               if (sscanf(buf+16, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < IWL_BA_ESCAPE_TIMER_MIN ||
+                   value > IWL_BA_ESCAPE_TIMER_MAX)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BA_ESCAPE_TIMER;
+       } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) {
+               if (sscanf(buf+23, "%d", &value) != 1)
+                       return -EINVAL;
+               if (value < 0 || value > 1)
+                       return -EINVAL;
+               param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT;
+       } else {
+               return -EINVAL;
+       }
+
+       mutex_lock(&mvm->mutex);
+       iwl_dbgfs_update_bf(vif, param, value);
+       if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value)
+               ret = iwl_mvm_disable_beacon_filter(mvm, vif);
+       else
+               ret = iwl_mvm_enable_beacon_filter(mvm, vif);
+       mutex_unlock(&mvm->mutex);
+
+       return ret ?: count;
+}
+
+static ssize_t iwl_dbgfs_bf_params_read(struct file *file,
+                                       char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct ieee80211_vif *vif = file->private_data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       char buf[256];
+       int pos = 0;
+       const size_t bufsz = sizeof(buf);
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter =
+                       cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT),
+               .ba_enable_beacon_abort =
+                       cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT),
+       };
+
+       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
+       if (mvmvif->bf_data.bf_enabled)
+               cmd.bf_enable_beacon_filter = cpu_to_le32(1);
+       else
+               cmd.bf_enable_beacon_filter = 0;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n",
+                        le32_to_cpu(cmd.bf_energy_delta));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n",
+                        le32_to_cpu(cmd.bf_roaming_energy_delta));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n",
+                        le32_to_cpu(cmd.bf_roaming_state));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n",
+                        le32_to_cpu(cmd.bf_temp_threshold));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_fast_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n",
+                        le32_to_cpu(cmd.bf_temp_slow_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n",
+                        le32_to_cpu(cmd.bf_enable_beacon_filter));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n",
+                        le32_to_cpu(cmd.bf_debug_flag));
+       pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n",
+                        le32_to_cpu(cmd.bf_escape_timer));
+       pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n",
+                        le32_to_cpu(cmd.ba_escape_timer));
+       pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n",
+                        le32_to_cpu(cmd.ba_enable_beacon_abort));
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+}
+
+#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
+       _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
+#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
+       _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
 #define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do {              \
                if (!debugfs_create_file(#name, mode, parent, vif,      \
                                         &iwl_dbgfs_##name##_ops))      \
@@ -132,6 +471,8 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file,
        } while (0)
 
 MVM_DEBUGFS_READ_FILE_OPS(mac_params);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
+MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
 
 void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
@@ -155,9 +496,21 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
                return;
        }
 
+       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM &&
+           ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) ||
+            (vif->type == NL80211_IFTYPE_STATION && vif->p2p &&
+             mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS)))
+               MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR |
+                                        S_IRUSR);
+
        MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir,
                                 S_IRUSR);
 
+       if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
+           mvmvif == mvm->bf_allowed_vif)
+               MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir,
+                                        S_IRUSR | S_IWUSR);
+
        /*
         * Create symlink for convenience pointing to interface specific
         * debugfs entries for the driver. For example, under
index e8f62a6..76cdce9 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -123,51 +123,31 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
 {
        struct iwl_mvm *mvm = file->private_data;
        const struct fw_img *img;
-       int ofs, len, pos = 0;
-       size_t bufsz, ret;
-       char *buf;
+       unsigned int ofs, len;
+       size_t ret;
        u8 *ptr;
 
        if (!mvm->ucode_loaded)
                return -EINVAL;
 
        /* default is to dump the entire data segment */
+       img = &mvm->fw->img[mvm->cur_ucode];
+       ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+       len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
        if (!mvm->dbgfs_sram_offset && !mvm->dbgfs_sram_len) {
-               img = &mvm->fw->img[mvm->cur_ucode];
-               ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
-               len = img->sec[IWL_UCODE_SECTION_DATA].len;
-       } else {
                ofs = mvm->dbgfs_sram_offset;
                len = mvm->dbgfs_sram_len;
        }
 
-       bufsz = len * 4 + 256;
-       buf = kzalloc(bufsz, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
        ptr = kzalloc(len, GFP_KERNEL);
-       if (!ptr) {
-               kfree(buf);
+       if (!ptr)
                return -ENOMEM;
-       }
-
-       pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", len);
-       pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", ofs);
 
        iwl_trans_read_mem_bytes(mvm->trans, ofs, ptr, len);
-       for (ofs = 0; ofs < len; ofs += 16) {
-               pos += scnprintf(buf + pos, bufsz - pos, "0x%.4x ", ofs);
-               hex_dump_to_buffer(ptr + ofs, 16, 16, 1, buf + pos,
-                                  bufsz - pos, false);
-               pos += strlen(buf + pos);
-               if (bufsz - pos > 0)
-                       buf[pos++] = '\n';
-       }
 
-       ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+       ret = simple_read_from_buffer(user_buf, count, ppos, ptr, len);
 
-       kfree(buf);
        kfree(ptr);
 
        return ret;
@@ -176,11 +156,24 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, char __user *user_buf,
 static ssize_t iwl_dbgfs_sram_write(struct iwl_mvm *mvm, char *buf,
                                    size_t count, loff_t *ppos)
 {
+       const struct fw_img *img;
        u32 offset, len;
+       u32 img_offset, img_len;
+
+       if (!mvm->ucode_loaded)
+               return -EINVAL;
+
+       img = &mvm->fw->img[mvm->cur_ucode];
+       img_offset = img->sec[IWL_UCODE_SECTION_DATA].offset;
+       img_len = img->sec[IWL_UCODE_SECTION_DATA].len;
 
        if (sscanf(buf, "%x,%x", &offset, &len) == 2) {
                if ((offset & 0x3) || (len & 0x3))
                        return -EINVAL;
+
+               if (offset + len > img_offset + img_len)
+                       return -EINVAL;
+
                mvm->dbgfs_sram_offset = offset;
                mvm->dbgfs_sram_len = len;
        } else {
index 85f9f95..e3a9774 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index af50099..1b4e54d 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 4e7dd8c..8415ff3 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 39c3148..c405cda 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 5cb93ae..884c087 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -85,6 +85,8 @@
  *             PBW Snoozing enabled
  * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
  * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
+ * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
+ *             detection enablement
 */
 enum iwl_power_flags {
        POWER_FLAGS_POWER_SAVE_ENA_MSK          = BIT(0),
@@ -94,6 +96,7 @@ enum iwl_power_flags {
        POWER_FLAGS_BT_SCO_ENA                  = BIT(8),
        POWER_FLAGS_ADVANCE_PM_ENA_MSK          = BIT(9),
        POWER_FLAGS_LPRX_ENA_MSK                = BIT(11),
+       POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK   = BIT(12),
 };
 
 #define IWL_POWER_VEC_SIZE 5
@@ -228,6 +231,19 @@ struct iwl_mac_power_cmd {
        u8 reserved;
 } __packed;
 
+/*
+ * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
+ * associated AP is identified as improperly implementing uAPSD protocol.
+ * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78
+ * @sta_id: index of station in uCode's station table - associated AP ID in
+ *         this context.
+ */
+struct iwl_uapsd_misbehaving_ap_notif {
+       __le32 sta_id;
+       u8 mac_id;
+       u8 reserved[3];
+} __packed;
+
 /**
  * struct iwl_beacon_filter_cmd
  * REPLY_BEACON_FILTERING_CMD = 0xd2 (command)
index 532312c..8505721 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index b3ed592..73cbba7 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 4aca593..6bbbad4 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -138,7 +138,14 @@ enum iwl_sta_flags {
 
 /**
  * enum iwl_sta_key_flag - key flags for the ADD_STA host command
- * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm
+ * @STA_KEY_FLG_NO_ENC: no encryption
+ * @STA_KEY_FLG_WEP: WEP encryption algorithm
+ * @STA_KEY_FLG_CCM: CCMP encryption algorithm
+ * @STA_KEY_FLG_TKIP: TKIP encryption algorithm
+ * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support)
+ * @STA_KEY_FLG_CMAC: CMAC encryption algorithm
+ * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm
+ * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value
  * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from
  *     station info array (1 - n 1X mode)
  * @STA_KEY_FLG_KEYID_MSK: the index of the key
@@ -152,6 +159,7 @@ enum iwl_sta_key_flag {
        STA_KEY_FLG_WEP                 = (1 << 0),
        STA_KEY_FLG_CCM                 = (2 << 0),
        STA_KEY_FLG_TKIP                = (3 << 0),
+       STA_KEY_FLG_EXT                 = (4 << 0),
        STA_KEY_FLG_CMAC                = (6 << 0),
        STA_KEY_FLG_ENC_UNKNOWN         = (7 << 0),
        STA_KEY_FLG_EN_MSK              = (7 << 0),
index d606197..b674c2a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -132,6 +132,7 @@ enum iwl_tx_flags {
 #define TX_CMD_SEC_WEP                 0x01
 #define TX_CMD_SEC_CCM                 0x02
 #define TX_CMD_SEC_TKIP                        0x03
+#define TX_CMD_SEC_EXT                 0x04
 #define TX_CMD_SEC_MSK                 0x07
 #define TX_CMD_SEC_WEP_KEY_IDX_POS     6
 #define TX_CMD_SEC_WEP_KEY_IDX_MSK     0xc0
index bad5a55..989d7db 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -141,6 +141,7 @@ enum {
 
        /* Power - legacy power table command */
        POWER_TABLE_CMD = 0x77,
+       PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78,
 
        /* Thermal Throttling*/
        REPLY_THERMAL_MNG_BACKOFF = 0x7e,
@@ -183,6 +184,7 @@ enum {
        BT_PROFILE_NOTIFICATION = 0xce,
        BT_COEX_CI = 0x5d,
 
+       REPLY_SF_CFG_CMD = 0xd1,
        REPLY_BEACON_FILTERING_CMD = 0xd2,
 
        REPLY_DEBUG_CMD = 0xf0,
@@ -1052,6 +1054,7 @@ enum iwl_mvm_rx_status {
        RX_MPDU_RES_STATUS_SEC_WEP_ENC                  = (1 << 8),
        RX_MPDU_RES_STATUS_SEC_CCM_ENC                  = (2 << 8),
        RX_MPDU_RES_STATUS_SEC_TKIP_ENC                 = (3 << 8),
+       RX_MPDU_RES_STATUS_SEC_EXT_ENC                  = (4 << 8),
        RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC             = (6 << 8),
        RX_MPDU_RES_STATUS_SEC_ENC_ERR                  = (7 << 8),
        RX_MPDU_RES_STATUS_SEC_ENC_MSK                  = (7 << 8),
@@ -1131,6 +1134,7 @@ struct iwl_set_calib_default_cmd {
 } __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */
 
 #define MAX_PORT_ID_NUM        2
+#define MAX_MCAST_FILTERING_ADDRESSES 256
 
 /**
  * struct iwl_mcast_filter_cmd - configure multicast filter.
@@ -1363,4 +1367,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */
        struct mvm_statistics_general general;
 } __packed;
 
+/***********************************
+ * Smart Fifo API
+ ***********************************/
+/* Smart Fifo state */
+enum iwl_sf_state {
+       SF_LONG_DELAY_ON = 0, /* should never be called by driver */
+       SF_FULL_ON,
+       SF_UNINIT,
+       SF_INIT_OFF,
+       SF_HW_NUM_STATES
+};
+
+/* Smart Fifo possible scenario */
+enum iwl_sf_scenario {
+       SF_SCENARIO_SINGLE_UNICAST,
+       SF_SCENARIO_AGG_UNICAST,
+       SF_SCENARIO_MULTICAST,
+       SF_SCENARIO_BA_RESP,
+       SF_SCENARIO_TX_RESP,
+       SF_NUM_SCENARIO
+};
+
+#define SF_TRANSIENT_STATES_NUMBER 2   /* SF_LONG_DELAY_ON and SF_FULL_ON */
+#define SF_NUM_TIMEOUT_TYPES 2         /* Aging timer and Idle timer */
+
+/* smart FIFO default values */
+#define SF_W_MARK_SISO 4096
+#define SF_W_MARK_MIMO2 8192
+#define SF_W_MARK_MIMO3 6144
+#define SF_W_MARK_LEGACY 4096
+#define SF_W_MARK_SCAN 4096
+
+/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */
+#define SF_SINGLE_UNICAST_IDLE_TIMER 320       /* 300 uSec  */
+#define SF_SINGLE_UNICAST_AGING_TIMER 2016     /* 2 mSec */
+#define SF_AGG_UNICAST_IDLE_TIMER 320          /* 300 uSec */
+#define SF_AGG_UNICAST_AGING_TIMER 2016                /* 2 mSec */
+#define SF_MCAST_IDLE_TIMER 2016               /* 2 mSec */
+#define SF_MCAST_AGING_TIMER 10016             /* 10 mSec */
+#define SF_BA_IDLE_TIMER 320                   /* 300 uSec */
+#define SF_BA_AGING_TIMER 2016                 /* 2 mSec */
+#define SF_TX_RE_IDLE_TIMER 320                        /* 300 uSec */
+#define SF_TX_RE_AGING_TIMER 2016              /* 2 mSec */
+
+#define SF_LONG_DELAY_AGING_TIMER 1000000      /* 1 Sec */
+
+/**
+ * Smart Fifo configuration command.
+ * @state: smart fifo state, types listed in iwl_sf_sate.
+ * @watermark: Minimum allowed availabe free space in RXF for transient state.
+ * @long_delay_timeouts: aging and idle timer values for each scenario
+ * in long delay state.
+ * @full_on_timeouts: timer values for each scenario in full on state.
+ */
+struct iwl_sf_cfg_cmd {
+       enum iwl_sf_state state;
+       __le32 watermark[SF_TRANSIENT_STATES_NUMBER];
+       __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
+       __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES];
+} __packed; /* SF_CFG_API_S_VER_2 */
+
 #endif /* __fw_api_h__ */
index 70e5297..c03d395 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -241,7 +241,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 
        lockdep_assert_held(&mvm->mutex);
 
-       if (mvm->init_ucode_complete)
+       if (WARN_ON_ONCE(mvm->init_ucode_complete))
                return 0;
 
        iwl_init_notification_wait(&mvm->notif_wait,
@@ -287,7 +287,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                IWL_DEBUG_RF_KILL(mvm,
                                  "jump over all phy activities due to RF kill\n");
                iwl_remove_notification(&mvm->notif_wait, &calib_wait);
-               return 1;
+               ret = 1;
+               goto out;
        }
 
        /* Send TX valid antennas before triggering calibrations */
@@ -319,9 +320,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
 error:
        iwl_remove_notification(&mvm->notif_wait, &calib_wait);
 out:
-       if (!iwlmvm_mod_params.init_dbg) {
-               iwl_trans_stop_device(mvm->trans);
-       } else if (!mvm->nvm_data) {
+       if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) {
                /* we want to debug INIT and we have no NVM - fake */
                mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) +
                                        sizeof(struct ieee80211_channel) +
@@ -370,11 +369,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                                ret = -ERFKILL;
                        goto error;
                }
-               /* should stop & start HW since that INIT image just loaded */
-               iwl_trans_stop_hw(mvm->trans, false);
-               ret = iwl_trans_start_hw(mvm->trans);
-               if (ret)
-                       return ret;
+               if (!iwlmvm_mod_params.init_dbg) {
+                       /*
+                        * should stop and start HW since that INIT
+                        * image just loaded
+                        */
+                       iwl_trans_stop_device(mvm->trans);
+                       ret = iwl_trans_start_hw(mvm->trans);
+                       if (ret)
+                               return ret;
+               }
        }
 
        if (iwlmvm_mod_params.init_dbg)
@@ -386,6 +390,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                goto error;
        }
 
+       ret = iwl_mvm_sf_update(mvm, NULL, false);
+       if (ret)
+               IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
+
        ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw));
        if (ret)
                goto error;
index 2269a9e..6b4ea6b 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -103,7 +103,7 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm)
                return 0;
        default:
                return -EINVAL;
-       };
+       }
 
        mvm->led.name = kasprintf(GFP_KERNEL, "%s-led",
                                   wiphy_name(mvm->hw->wiphy));
index fb93961..ba723d5 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "mvm.h"
 
 const u8 iwl_mvm_ac_to_tx_fifo[] = {
-       IWL_MVM_TX_FIFO_BK,
-       IWL_MVM_TX_FIFO_BE,
-       IWL_MVM_TX_FIFO_VI,
        IWL_MVM_TX_FIFO_VO,
+       IWL_MVM_TX_FIFO_VI,
+       IWL_MVM_TX_FIFO_BE,
+       IWL_MVM_TX_FIFO_BK,
 };
 
 struct iwl_mvm_mac_iface_iterator_data {
@@ -85,35 +85,15 @@ struct iwl_mvm_mac_iface_iterator_data {
        bool found_vif;
 };
 
-static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
-                                      struct ieee80211_vif *vif)
+static void iwl_mvm_mac_tsf_id_iter(void *_data, u8 *mac,
+                                   struct ieee80211_vif *vif)
 {
        struct iwl_mvm_mac_iface_iterator_data *data = _data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       u32 ac;
 
-       /* Iterator may already find the interface being added -- skip it */
-       if (vif == data->vif) {
-               data->found_vif = true;
+       /* Skip the interface for which we are trying to assign a tsf_id  */
+       if (vif == data->vif)
                return;
-       }
-
-       /* Mark the queues used by the vif */
-       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
-               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
-                       __set_bit(vif->hw_queue[ac], data->used_hw_queues);
-
-       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
-               __set_bit(vif->cab_queue, data->used_hw_queues);
-
-       /*
-        * Mark MAC IDs as used by clearing the available bit, and
-        * (below) mark TSFs as used if their existing use is not
-        * compatible with the new interface type.
-        * No locking or atomic bit operations are needed since the
-        * data is on the stack of the caller function.
-        */
-       __clear_bit(mvmvif->id, data->available_mac_ids);
 
        /*
         * The TSF is a hardware/firmware resource, there are 4 and
@@ -135,21 +115,26 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
        case NL80211_IFTYPE_STATION:
                /*
                 * The new interface is client, so if the existing one
-                * we're iterating is an AP, the TSF should be used to
+                * we're iterating is an AP, and both interfaces have the
+                * same beacon interval, the same TSF should be used to
                 * avoid drift between the new client and existing AP,
                 * the existing AP will get drift updates from the new
                 * client context in this case
                 */
                if (vif->type == NL80211_IFTYPE_AP) {
                        if (data->preferred_tsf == NUM_TSF_IDS &&
-                           test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                           test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
+                           (vif->bss_conf.beacon_int ==
+                            data->vif->bss_conf.beacon_int)) {
                                data->preferred_tsf = mvmvif->tsf_id;
-                       return;
+                               return;
+                       }
                }
                break;
        case NL80211_IFTYPE_AP:
                /*
-                * The new interface is AP/GO, so should get drift
+                * The new interface is AP/GO, so in case both interfaces
+                * have the same beacon interval, it should get drift
                 * updates from an existing client or use the same
                 * TSF as an existing GO. There's no drift between
                 * TSFs internally but if they used different TSFs
@@ -159,9 +144,12 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
                if (vif->type == NL80211_IFTYPE_STATION ||
                    vif->type == NL80211_IFTYPE_AP) {
                        if (data->preferred_tsf == NUM_TSF_IDS &&
-                           test_bit(mvmvif->tsf_id, data->available_tsf_ids))
+                           test_bit(mvmvif->tsf_id, data->available_tsf_ids) &&
+                           (vif->bss_conf.beacon_int ==
+                            data->vif->bss_conf.beacon_int)) {
                                data->preferred_tsf = mvmvif->tsf_id;
-                       return;
+                               return;
+                       }
                }
                break;
        default:
@@ -187,6 +175,39 @@ static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
                data->preferred_tsf = NUM_TSF_IDS;
 }
 
+static void iwl_mvm_mac_iface_iterator(void *_data, u8 *mac,
+                                      struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_mac_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       u32 ac;
+
+       /* Iterator may already find the interface being added -- skip it */
+       if (vif == data->vif) {
+               data->found_vif = true;
+               return;
+       }
+
+       /* Mark the queues used by the vif */
+       for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+               if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE)
+                       __set_bit(vif->hw_queue[ac], data->used_hw_queues);
+
+       if (vif->cab_queue != IEEE80211_INVAL_HW_QUEUE)
+               __set_bit(vif->cab_queue, data->used_hw_queues);
+
+       /* Mark MAC IDs as used by clearing the available bit, and
+        * (below) mark TSFs as used if their existing use is not
+        * compatible with the new interface type.
+        * No locking or atomic bit operations are needed since the
+        * data is on the stack of the caller function.
+        */
+       __clear_bit(mvmvif->id, data->available_mac_ids);
+
+       /* find a suitable tsf_id */
+       iwl_mvm_mac_tsf_id_iter(_data, mac, vif);
+}
+
 /*
  * Get the mask of the queus used by the vif
  */
@@ -205,6 +226,29 @@ u32 iwl_mvm_mac_get_queues_mask(struct iwl_mvm *mvm,
        return qmask;
 }
 
+void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm_mac_iface_iterator_data data = {
+               .mvm = mvm,
+               .vif = vif,
+               .available_tsf_ids = { (1 << NUM_TSF_IDS) - 1 },
+               /* no preference yet */
+               .preferred_tsf = NUM_TSF_IDS,
+       };
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
+               iwl_mvm_mac_tsf_id_iter, &data);
+
+       if (data.preferred_tsf != NUM_TSF_IDS)
+               mvmvif->tsf_id = data.preferred_tsf;
+       else if (!test_bit(mvmvif->tsf_id, data.available_tsf_ids))
+               mvmvif->tsf_id = find_first_bit(data.available_tsf_ids,
+                                               NUM_TSF_IDS);
+}
+
 static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm,
                                               struct ieee80211_vif *vif)
 {
@@ -586,18 +630,23 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
                cpu_to_le32(vif->bss_conf.use_short_slot ?
                            MAC_FLG_SHORT_SLOT : 0);
 
-       for (i = 0; i < AC_NUM; i++) {
-               cmd->ac[i].cw_min = cpu_to_le16(mvmvif->queue_params[i].cw_min);
-               cmd->ac[i].cw_max = cpu_to_le16(mvmvif->queue_params[i].cw_max);
-               cmd->ac[i].aifsn = mvmvif->queue_params[i].aifs;
-               cmd->ac[i].edca_txop =
+       for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+               u8 txf = iwl_mvm_ac_to_tx_fifo[i];
+
+               cmd->ac[txf].cw_min =
+                       cpu_to_le16(mvmvif->queue_params[i].cw_min);
+               cmd->ac[txf].cw_max =
+                       cpu_to_le16(mvmvif->queue_params[i].cw_max);
+               cmd->ac[txf].edca_txop =
                        cpu_to_le16(mvmvif->queue_params[i].txop * 32);
-               cmd->ac[i].fifos_mask = BIT(iwl_mvm_ac_to_tx_fifo[i]);
+               cmd->ac[txf].aifsn = mvmvif->queue_params[i].aifs;
+               cmd->ac[txf].fifos_mask = BIT(txf);
        }
 
        /* in AP mode, the MCAST FIFO takes the EDCA params from VO */
        if (vif->type == NL80211_IFTYPE_AP)
-               cmd->ac[AC_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST);
+               cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |=
+                       BIT(IWL_MVM_TX_FIFO_MCAST);
 
        if (vif->bss_conf.qos)
                cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
@@ -1007,7 +1056,7 @@ static void iwl_mvm_mac_ctxt_cmd_fill_ap(struct iwl_mvm *mvm,
                        iwl_mvm_mac_ap_iterator, &data);
 
                if (data.beacon_device_ts) {
-                       u32 rand = (prandom_u32() % (80 - 20)) + 20;
+                       u32 rand = (prandom_u32() % (64 - 36)) + 36;
                        mvmvif->ap_beacon_time = data.beacon_device_ts +
                                ieee80211_tu_to_usec(data.beacon_int * rand /
                                                     100);
@@ -1186,10 +1235,18 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
 static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
                                         struct ieee80211_vif *vif)
 {
-       u16 *id = _data;
+       struct iwl_missed_beacons_notif *missed_beacons = _data;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-       if (mvmvif->id == *id)
+       if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id))
+               return;
+
+       /*
+        * TODO: the threshold should be adjusted based on latency conditions,
+        * and/or in case of a CS flow on one of the other AP vifs.
+        */
+       if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) >
+            IWL_MVM_MISSED_BEACONS_THRESHOLD)
                ieee80211_beacon_loss(vif);
 }
 
@@ -1198,12 +1255,19 @@ int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
                                    struct iwl_device_cmd *cmd)
 {
        struct iwl_rx_packet *pkt = rxb_addr(rxb);
-       struct iwl_missed_beacons_notif *missed_beacons = (void *)pkt->data;
-       u16 id = (u16)le32_to_cpu(missed_beacons->mac_id);
+       struct iwl_missed_beacons_notif *mb = (void *)pkt->data;
+
+       IWL_DEBUG_INFO(mvm,
+                      "missed bcn mac_id=%u, consecutive=%u (%u, %u, %u)\n",
+                      le32_to_cpu(mb->mac_id),
+                      le32_to_cpu(mb->consec_missed_beacons),
+                      le32_to_cpu(mb->consec_missed_beacons_since_last_rx),
+                      le32_to_cpu(mb->num_recvd_beacons),
+                      le32_to_cpu(mb->num_expected_beacons));
 
        ieee80211_iterate_active_interfaces_atomic(mvm->hw,
                                                   IEEE80211_IFACE_ITER_NORMAL,
                                                   iwl_mvm_beacon_loss_iterator,
-                                                  &id);
+                                                  mb);
        return 0;
 }
index a77007c..b41177e 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -261,6 +261,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
 
        mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
 
+       /* currently FW API supports only one optional cipher scheme */
+       if (mvm->fw->cs->cipher) {
+               mvm->hw->n_cipher_schemes = 1;
+               mvm->hw->cipher_schemes = mvm->fw->cs;
+       }
+
 #ifdef CONFIG_PM_SLEEP
        if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len &&
            mvm->trans->ops->d3_suspend &&
@@ -399,7 +405,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
 static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 {
        iwl_trans_stop_device(mvm->trans);
-       iwl_trans_stop_hw(mvm->trans, false);
 
        mvm->scan_status = IWL_MVM_SCAN_NONE;
 
@@ -471,7 +476,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        cancel_work_sync(&mvm->roc_done_wk);
 
        iwl_trans_stop_device(mvm->trans);
-       iwl_trans_stop_hw(mvm->trans, false);
 
        iwl_mvm_async_handlers_purge(mvm);
        /* async_handlers_list is empty and will stay empty: HW is stopped */
@@ -488,17 +492,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        cancel_work_sync(&mvm->async_handlers_wk);
 }
 
-static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac,
-                                       struct ieee80211_vif *vif)
-{
-       struct iwl_mvm *mvm = data;
-       int ret;
-
-       ret = iwl_mvm_power_disable(mvm, vif);
-       if (ret)
-               IWL_ERR(mvm, "failed to disable power management\n");
-}
-
 static void iwl_mvm_power_update_iterator(void *data, u8 *mac,
                                          struct ieee80211_vif *vif)
 {
@@ -521,6 +514,20 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm)
        return NULL;
 }
 
+static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               s8 tx_power)
+{
+       /* FW is in charge of regulatory enforcement */
+       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
+               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
+               .pwr_restriction = cpu_to_le16(tx_power),
+       };
+
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
+                                   sizeof(reduce_txpwr_cmd),
+                                   &reduce_txpwr_cmd);
+}
+
 static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
 {
@@ -541,26 +548,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_unlock;
 
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * If new interface added, disable PM on existing interface.
-        * P2P device is a special case, since it is handled by FW similary to
-        * scan. If P2P deviced is added, PM remains enabled on existing
-        * interface.
-        * Note: the method below does not count the new interface being added
-        * at this moment.
-        */
+       /* Counting number of interfaces is needed for legacy PM */
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count++;
-       if (mvm->vif_count > 1) {
-               IWL_DEBUG_MAC80211(mvm,
-                                  "Disable power on existing interfaces\n");
-               ieee80211_iterate_active_interfaces_atomic(
-                                           mvm->hw,
-                                           IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_pm_disable_iterator, mvm);
-       }
 
        /*
         * The AP binding flow can be done only after the beacon
@@ -591,11 +581,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
        if (ret)
                goto out_release;
 
-       /*
-        * Update power state on the new interface. Admittedly, based on
-        * mac80211 logics this power update will disable power management
-        */
-       iwl_mvm_power_update_mode(mvm, vif);
+       iwl_mvm_power_disable(mvm, vif);
 
        /* beacon filtering */
        ret = iwl_mvm_disable_beacon_filter(mvm, vif);
@@ -656,9 +642,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
  out_release:
        if (vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
+
+       /* TODO: remove this when legacy PM will be discarded */
        ieee80211_iterate_active_interfaces(
                mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
                iwl_mvm_power_update_iterator, mvm);
+
        iwl_mvm_mac_ctxt_release(mvm, vif);
  out_unlock:
        mutex_unlock(&mvm->mutex);
@@ -744,21 +733,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
                mvmvif->phy_ctxt = NULL;
        }
 
-       /*
-        * TODO: remove this temporary code.
-        * Currently MVM FW supports power management only on single MAC.
-        * Check if only one additional interface remains after removing
-        * current one. Update power mode on the remaining interface.
-        */
        if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE)
                mvm->vif_count--;
-       IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n",
-                          mvm->vif_count);
-       if (mvm->vif_count == 1) {
-               ieee80211_iterate_active_interfaces(
-                                       mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
-                                       iwl_mvm_power_update_iterator, mvm);
-       }
+
+       /* TODO: remove this when legacy PM will be discarded */
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_power_update_iterator, mvm);
 
        iwl_mvm_mac_ctxt_remove(mvm, vif);
 
@@ -767,23 +748,91 @@ out_release:
        mutex_unlock(&mvm->mutex);
 }
 
-static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                               s8 tx_power)
+static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
 {
-       /* FW is in charge of regulatory enforcement */
-       struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
-               .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id,
-               .pwr_restriction = cpu_to_le16(tx_power),
+       return 0;
+}
+
+struct iwl_mvm_mc_iter_data {
+       struct iwl_mvm *mvm;
+       int port_id;
+};
+
+static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac,
+                                     struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_mc_iter_data *data = _data;
+       struct iwl_mvm *mvm = data->mvm;
+       struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
+       int ret, len;
+
+       /* if we don't have free ports, mcast frames will be dropped */
+       if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM))
+               return;
+
+       if (vif->type != NL80211_IFTYPE_STATION ||
+           !vif->bss_conf.assoc)
+               return;
+
+       cmd->port_id = data->port_id++;
+       memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
+       len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, len, cmd);
+       if (ret)
+               IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
+}
+
+static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm)
+{
+       struct iwl_mvm_mc_iter_data iter_data = {
+               .mvm = mvm,
        };
 
-       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC,
-                                   sizeof(reduce_txpwr_cmd),
-                                   &reduce_txpwr_cmd);
+       lockdep_assert_held(&mvm->mutex);
+
+       if (WARN_ON_ONCE(!mvm->mcast_filter_cmd))
+               return;
+
+       ieee80211_iterate_active_interfaces(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_mc_iface_iterator, &iter_data);
 }
 
-static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed)
+static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
+                                    struct netdev_hw_addr_list *mc_list)
 {
-       return 0;
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mcast_filter_cmd *cmd;
+       struct netdev_hw_addr *addr;
+       int addr_count = netdev_hw_addr_list_count(mc_list);
+       bool pass_all = false;
+       int len;
+
+       if (addr_count > MAX_MCAST_FILTERING_ADDRESSES) {
+               pass_all = true;
+               addr_count = 0;
+       }
+
+       len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4);
+       cmd = kzalloc(len, GFP_ATOMIC);
+       if (!cmd)
+               return 0;
+
+       if (pass_all) {
+               cmd->pass_all = 1;
+               return (u64)(unsigned long)cmd;
+       }
+
+       netdev_hw_addr_list_for_each(addr, mc_list) {
+               IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n",
+                                  cmd->count, addr->addr);
+               memcpy(&cmd->addr_list[cmd->count * ETH_ALEN],
+                      addr->addr, ETH_ALEN);
+               cmd->count++;
+       }
+
+       return (u64)(unsigned long)cmd;
 }
 
 static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
@@ -791,21 +840,22 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw,
                                     unsigned int *total_flags,
                                     u64 multicast)
 {
-       *total_flags = 0;
-}
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
 
-static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm,
-                                         struct ieee80211_vif *vif)
-{
-       struct iwl_mcast_filter_cmd mcast_filter_cmd = {
-               .pass_all = 1,
-       };
+       mutex_lock(&mvm->mutex);
+
+       /* replace previous configuration */
+       kfree(mvm->mcast_filter_cmd);
+       mvm->mcast_filter_cmd = cmd;
 
-       memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN);
+       if (!cmd)
+               goto out;
 
-       return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC,
-                                   sizeof(mcast_filter_cmd),
-                                   &mcast_filter_cmd);
+       iwl_mvm_recalc_multicast(mvm);
+out:
+       mutex_unlock(&mvm->mutex);
+       *total_flags = 0;
 }
 
 static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
@@ -816,6 +866,14 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
+       /*
+        * Re-calculate the tsf id, as the master-slave relations depend on the
+        * beacon interval, which was not known when the station interface was
+        * added.
+        */
+       if (changes & BSS_CHANGED_ASSOC && bss_conf->assoc)
+               iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
+
        ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
        if (ret)
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
@@ -828,7 +886,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                                return;
                        }
-                       iwl_mvm_configure_mcast_filter(mvm, vif);
 
                        if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
                                     &mvm->status)) {
@@ -850,7 +907,17 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                iwl_mvm_protect_session(mvm, vif, dur, dur,
                                                        5 * dur);
                        }
+
+                       iwl_mvm_sf_update(mvm, vif, false);
+                       iwl_mvm_power_vif_assoc(mvm, vif);
                } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) {
+                       /*
+                        * If update fails - SF might be running in associated
+                        * mode while disassociated - which is forbidden.
+                        */
+                       WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false),
+                                 "Failed to update SF upon disassociation\n");
+
                        /* remove AP station now that the MAC is unassoc */
                        ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id);
                        if (ret)
@@ -862,6 +929,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                                IWL_ERR(mvm, "failed to update quotas\n");
                }
 
+               iwl_mvm_recalc_multicast(mvm);
+
                /* reset rssi values */
                mvmvif->bf_data.ave_beacon_signal = 0;
 
@@ -882,7 +951,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
                 */
                iwl_mvm_remove_time_event(mvm, mvmvif,
                                          &mvmvif->time_event_data);
-       } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) {
+       } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS |
+                             BSS_CHANGED_QOS)) {
                ret = iwl_mvm_power_update_mode(mvm, vif);
                if (ret)
                        IWL_ERR(mvm, "failed to update power mode\n");
@@ -917,6 +987,13 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
        if (ret)
                goto out_unlock;
 
+       /*
+        * Re-calculate the tsf id, as the master-slave relations depend on the
+        * beacon interval, which was not known when the AP interface was added.
+        */
+       if (vif->type == NL80211_IFTYPE_AP)
+               iwl_mvm_mac_ctxt_recalc_tsf_id(mvm, vif);
+
        /* Add the mac context */
        ret = iwl_mvm_mac_ctxt_add(mvm, vif);
        if (ret)
@@ -991,11 +1068,16 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm,
                                 struct ieee80211_bss_conf *bss_conf,
                                 u32 changes)
 {
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT |
                                              BSS_CHANGED_HT |
                                              BSS_CHANGED_BANDWIDTH;
        int ret;
 
+       /* Changes will be applied when the AP/IBSS is started */
+       if (!mvmvif->ap_ibss_active)
+               return;
+
        if (changes & ht_change) {
                ret = iwl_mvm_mac_ctxt_changed(mvm, vif);
                if (ret)
@@ -1222,6 +1304,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
        return 0;
 }
 
+static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw,
+                                 struct ieee80211_vif *vif,
+                                 struct ieee80211_sta *sta, u32 changed)
+{
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+       if (vif->type == NL80211_IFTYPE_STATION &&
+           changed & IEEE80211_RC_NSS_CHANGED)
+               iwl_mvm_sf_update(mvm, vif, false);
+}
+
 static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif, u16 ac,
                               const struct ieee80211_tx_queue_params *params)
@@ -1344,7 +1437,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw,
                 */
                return 0;
        default:
-               return -EOPNOTSUPP;
+               /* currently FW supports only one optional cipher scheme */
+               if (hw->n_cipher_schemes &&
+                   hw->cipher_schemes->cipher == key->cipher)
+                       key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+               else
+                       return -EOPNOTSUPP;
        }
 
        mutex_lock(&mvm->mutex);
@@ -1550,7 +1648,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
                goto out;
        }
 
-       ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+       ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
                                       ctx->rx_chains_static,
                                       ctx->rx_chains_dynamic);
        if (ret) {
@@ -1588,13 +1686,14 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
        if (WARN_ONCE((phy_ctxt->ref > 1) &&
                      (changed & ~(IEEE80211_CHANCTX_CHANGE_WIDTH |
                                   IEEE80211_CHANCTX_CHANGE_RX_CHAINS |
-                                  IEEE80211_CHANCTX_CHANGE_RADAR)),
+                                  IEEE80211_CHANCTX_CHANGE_RADAR |
+                                  IEEE80211_CHANCTX_CHANGE_MIN_WIDTH)),
                      "Cannot change PHY. Ref=%d, changed=0x%X\n",
                      phy_ctxt->ref, changed))
                return;
 
        mutex_lock(&mvm->mutex);
-       iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def,
+       iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def,
                                 ctx->rx_chains_static,
                                 ctx->rx_chains_dynamic);
        iwl_mvm_bt_coex_vif_change(mvm);
@@ -1637,7 +1736,13 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
                goto out_unlock;
 
        /*
-        * Setting the quota at this stage is only required for monitor
+        * Power state must be updated before quotas,
+        * otherwise fw will complain.
+        */
+       mvm->bound_vif_cnt++;
+       iwl_mvm_power_update_binding(mvm, vif, true);
+
+       /* Setting the quota at this stage is only required for monitor
         * interfaces. For the other types, the bss_info changed flow
         * will handle quota settings.
         */
@@ -1652,6 +1757,8 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
 
  out_remove_binding:
        iwl_mvm_binding_remove_vif(mvm, vif);
+       mvm->bound_vif_cnt--;
+       iwl_mvm_power_update_binding(mvm, vif, false);
  out_unlock:
        mutex_unlock(&mvm->mutex);
        if (ret)
@@ -1685,6 +1792,9 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
        iwl_mvm_binding_remove_vif(mvm, vif);
 out_unlock:
        mvmvif->phy_ctxt = NULL;
+       mvm->bound_vif_cnt--;
+       iwl_mvm_power_update_binding(mvm, vif, false);
+
        mutex_unlock(&mvm->mutex);
 }
 
@@ -1779,6 +1889,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .add_interface = iwl_mvm_mac_add_interface,
        .remove_interface = iwl_mvm_mac_remove_interface,
        .config = iwl_mvm_mac_config,
+       .prepare_multicast = iwl_mvm_prepare_multicast,
        .configure_filter = iwl_mvm_configure_filter,
        .bss_info_changed = iwl_mvm_bss_info_changed,
        .hw_scan = iwl_mvm_mac_hw_scan,
@@ -1788,6 +1899,7 @@ struct ieee80211_ops iwl_mvm_hw_ops = {
        .sta_notify = iwl_mvm_mac_sta_notify,
        .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames,
        .set_rts_threshold = iwl_mvm_mac_set_rts_threshold,
+       .sta_rc_update = iwl_mvm_sta_rc_update,
        .conf_tx = iwl_mvm_mac_conf_tx,
        .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
        .sched_scan_start = iwl_mvm_mac_sched_scan_start,
index 7dc57cf..e4ead86 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -81,6 +81,7 @@
 #define IWL_MVM_MAX_ADDRESSES          5
 /* RSSI offset for WkP */
 #define IWL_RSSI_OFFSET 50
+#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
 
 enum iwl_mvm_tx_fifo {
        IWL_MVM_TX_FIFO_BK = 0,
@@ -163,6 +164,8 @@ struct iwl_mvm_power_ops {
                                 struct ieee80211_vif *vif);
        int (*power_update_device_mode)(struct iwl_mvm *mvm);
        int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+       void (*power_update_binding)(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif, bool assign);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                                char *buf, int bufsz);
@@ -181,6 +184,7 @@ enum iwl_dbgfs_pm_mask {
        MVM_DEBUGFS_PM_LPRX_ENA = BIT(6),
        MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7),
        MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8),
+       MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9),
 };
 
 struct iwl_dbgfs_pm {
@@ -193,6 +197,7 @@ struct iwl_dbgfs_pm {
        bool lprx_ena;
        u32 lprx_rssi_threshold;
        bool snooze_ena;
+       bool uapsd_misbehaving;
        int mask;
 };
 
@@ -269,8 +274,8 @@ struct iwl_mvm_vif_bf_data {
  * @bcast_sta: station used for broadcast packets. Used by the following
  *  vifs: P2P_DEVICE, GO and AP.
  * @beacon_skb: the skb used to hold the AP/GO beacon template
- * @smps_requests: the requests of of differents parts of the driver, regard
      the desired smps mode.
+ * @smps_requests: the SMPS requests of differents parts of the driver,
*     combined on update to yield the overall request to mac80211.
  */
 struct iwl_mvm_vif {
        u16 id;
@@ -331,6 +336,11 @@ struct iwl_mvm_vif {
 #endif
 
        enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ];
+
+       /* FW identified misbehaving AP */
+       u8 uapsd_misbehaving_bssid[ETH_ALEN];
+
+       bool pm_prevented;
 };
 
 static inline struct iwl_mvm_vif *
@@ -479,6 +489,7 @@ struct iwl_mvm {
        /* Scan status, cmd (pre-allocated) and auxiliary station */
        enum iwl_scan_status scan_status;
        struct iwl_scan_cmd *scan_cmd;
+       struct iwl_mcast_filter_cmd *mcast_filter_cmd;
 
        /* rx chain antennas set through debugfs for the scan command */
        u8 scan_rx_ant;
@@ -489,6 +500,9 @@ struct iwl_mvm {
        u8 scan_last_antenna_idx; /* to toggle TX between antennas */
        u8 mgmt_last_antenna_idx;
 
+       /* last smart fifo state that was successfully sent to firmware */
+       enum iwl_sf_state sf_state;
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        struct dentry *debugfs_dir;
        u32 dbgfs_sram_offset, dbgfs_sram_len;
@@ -512,12 +526,6 @@ struct iwl_mvm {
         */
        unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
 
-       /*
-        * This counter of created interfaces is referenced only in conjunction
-        * with FW limitation related to power management. Currently PM is
-        * supported only on a single interface.
-        * IMPORTANT: this variable counts all interfaces except P2P device.
-        */
        u8 vif_count;
 
        /* -1 for always, 0 for never, >0 for that many times */
@@ -560,6 +568,11 @@ struct iwl_mvm {
        u8 aux_queue;
        u8 first_agg_queue;
        u8 last_agg_queue;
+
+       u8 bound_vif_cnt;
+
+       /* Indicate if device power save is allowed */
+       bool ps_prevented;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -699,6 +712,8 @@ int iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
 int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
                                    struct iwl_rx_cmd_buffer *rxb,
                                    struct iwl_device_cmd *cmd);
+void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif);
 
 /* Bindings */
 int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -778,6 +793,19 @@ static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm)
        return 0;
 }
 
+static inline void iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
+                                               struct ieee80211_vif *vif,
+                                               bool assign)
+{
+       if (mvm->pm_ops->power_update_binding)
+               mvm->pm_ops->power_update_binding(mvm, vif, assign);
+}
+
+void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
+                                            struct iwl_rx_cmd_buffer *rxb,
+                                            struct iwl_device_cmd *cmd);
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm,
                                            struct ieee80211_vif *vif,
@@ -869,4 +897,8 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm);
 void iwl_mvm_tt_exit(struct iwl_mvm *mvm);
 void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
 
+/* smart fifo */
+int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                     bool added_vif);
+
 #endif /* __IWL_MVM_H__ */
index 48089b1..c6beb0f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -367,16 +367,17 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
                        break;
                }
 
+               if (WARN(section_id >= NVM_NUM_OF_SECTIONS,
+                        "Invalid NVM section ID %d\n", section_id)) {
+                       ret = -EINVAL;
+                       break;
+               }
+
                temp = kmemdup(file_sec->data, section_size, GFP_KERNEL);
                if (!temp) {
                        ret = -ENOMEM;
                        break;
                }
-               if (WARN_ON(section_id >= NVM_NUM_OF_SECTIONS)) {
-                       IWL_ERR(mvm, "Invalid NVM section ID\n");
-                       ret = -EINVAL;
-                       break;
-               }
                mvm->nvm_sections[section_id].data = temp;
                mvm->nvm_sections[section_id].length = section_size;
 
index d86083c..552c76a 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -236,6 +236,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
                   false),
 
        RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false),
+       RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,
+                  iwl_mvm_power_uapsd_misbehaving_ap_notif, false),
 };
 #undef RX_HANDLER
 #define CMD(x) [x] = #x
@@ -311,6 +313,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(REPLY_THERMAL_MNG_BACKOFF),
        CMD(MAC_PM_POWER_TABLE),
        CMD(BT_COEX_CI),
+       CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
 };
 #undef CMD
 
@@ -341,7 +344,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        op_mode = hw->priv;
        op_mode->ops = &iwl_mvm_ops;
-       op_mode->trans = trans;
 
        mvm = IWL_OP_MODE_GET_MVM(op_mode);
        mvm->dev = trans->dev;
@@ -359,6 +361,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
                mvm->aux_queue = 11;
                mvm->first_agg_queue = 12;
        }
+       mvm->sf_state = SF_UNINIT;
 
        mutex_init(&mvm->mutex);
        spin_lock_init(&mvm->async_handlers_lock);
@@ -424,7 +427,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
         * there is no need to unnecessarily power up the NIC at driver load
         */
        if (iwlwifi_mod_params.nvm_file) {
-                       iwl_nvm_init(mvm);
+               err = iwl_nvm_init(mvm);
+               if (err)
+                       goto out_free;
        } else {
                err = iwl_trans_start_hw(mvm->trans);
                if (err)
@@ -432,16 +437,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
                mutex_lock(&mvm->mutex);
                err = iwl_run_init_mvm_ucode(mvm, true);
+               iwl_trans_stop_device(trans);
                mutex_unlock(&mvm->mutex);
                /* returns 0 if successful, 1 if success but in rfkill */
                if (err < 0 && !iwlmvm_mod_params.init_dbg) {
                        IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err);
                        goto out_free;
                }
-
-               /* Stop the hw after the ALIVE and NVM has been read */
-               if (!iwlmvm_mod_params.init_dbg)
-                       iwl_trans_stop_hw(mvm->trans, false);
        }
 
        scan_size = sizeof(struct iwl_scan_cmd) +
@@ -474,7 +476,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
        iwl_phy_db_free(mvm->phy_db);
        kfree(mvm->scan_cmd);
        if (!iwlwifi_mod_params.nvm_file)
-               iwl_trans_stop_hw(trans, true);
+               iwl_trans_op_mode_leave(trans);
        ieee80211_free_hw(mvm->hw);
        return NULL;
 }
@@ -491,12 +493,14 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
        ieee80211_unregister_hw(mvm->hw);
 
        kfree(mvm->scan_cmd);
+       kfree(mvm->mcast_filter_cmd);
+       mvm->mcast_filter_cmd = NULL;
 
 #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS)
        kfree(mvm->d3_resume_sram);
 #endif
 
-       iwl_trans_stop_hw(mvm->trans, true);
+       iwl_trans_op_mode_leave(mvm->trans);
 
        iwl_phy_db_free(mvm->phy_db);
        mvm->phy_db = NULL;
@@ -661,6 +665,8 @@ static void iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)
        else
                clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status);
 
+       if (state && mvm->cur_ucode != IWL_UCODE_INIT)
+               iwl_trans_stop_device(mvm->trans);
        wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm));
 }
 
index a8652dd..b7268c0 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 550824a..d9eab3b 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -64,7 +64,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 
 #include <net/mac80211.h>
 
@@ -186,6 +185,92 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm,
        }
 }
 
+static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         struct iwl_mac_power_cmd *cmd)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       enum ieee80211_ac_numbers ac;
+       bool tid_found = false;
+
+       for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
+               if (!mvmvif->queue_params[ac].uapsd)
+                       continue;
+
+               if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
+                       cmd->flags |=
+                               cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
+
+               cmd->uapsd_ac_flags |= BIT(ac);
+
+               /* QNDP TID - the highest TID with no admission control */
+               if (!tid_found && !mvmvif->queue_params[ac].acm) {
+                       tid_found = true;
+                       switch (ac) {
+                       case IEEE80211_AC_VO:
+                               cmd->qndp_tid = 6;
+                               break;
+                       case IEEE80211_AC_VI:
+                               cmd->qndp_tid = 5;
+                               break;
+                       case IEEE80211_AC_BE:
+                               cmd->qndp_tid = 0;
+                               break;
+                       case IEEE80211_AC_BK:
+                               cmd->qndp_tid = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
+               return;
+
+       cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
+
+       if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
+                                   BIT(IEEE80211_AC_VI) |
+                                   BIT(IEEE80211_AC_BE) |
+                                   BIT(IEEE80211_AC_BK))) {
+               cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
+               cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
+               cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
+                       cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
+                       cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
+       }
+
+       cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
+
+       if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
+           cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+               cmd->rx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
+       } else {
+               cmd->rx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
+               cmd->tx_data_timeout_uapsd =
+                       cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
+       }
+
+       if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
+               cmd->heavy_tx_thld_packets =
+                       IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
+               cmd->heavy_rx_thld_packets =
+                       IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
+       } else {
+               cmd->heavy_tx_thld_packets =
+                       IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
+               cmd->heavy_rx_thld_packets =
+                       IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
+       }
+       cmd->heavy_tx_thld_percentage =
+               IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
+       cmd->heavy_rx_thld_percentage =
+               IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
+}
+
 static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif,
                                    struct iwl_mac_power_cmd *cmd)
@@ -198,8 +283,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        bool radar_detect = false;
        struct iwl_mvm_vif *mvmvif __maybe_unused =
                iwl_mvm_vif_from_mac80211(vif);
-       enum ieee80211_ac_numbers ac;
-       bool tid_found = false;
+       bool allow_uapsd = true;
 
        cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
                                                            mvmvif->color));
@@ -217,7 +301,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
        cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
+           mvm->ps_prevented)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@@ -227,7 +312,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
            mvmvif->dbgfs_pm.disable_power_off)
                cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
 #endif
-       if (!vif->bss_conf.ps)
+       if (!vif->bss_conf.ps || mvmvif->pm_prevented)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -269,81 +354,24 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                        cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
        }
 
-       for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
-               if (!mvmvif->queue_params[ac].uapsd)
-                       continue;
-
-               if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
-                       cmd->flags |=
-                               cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
-
-               cmd->uapsd_ac_flags |= BIT(ac);
+       if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+                   ETH_ALEN))
+               allow_uapsd = false;
 
-               /* 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)) {
-               if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
-                                           BIT(IEEE80211_AC_VI) |
-                                           BIT(IEEE80211_AC_BE) |
-                                           BIT(IEEE80211_AC_BK))) {
-                       cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
-                       cmd->snooze_interval =
-                               cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
-                       cmd->snooze_window =
-                               (mvm->cur_ucode == IWL_UCODE_WOWLAN) ?
-                               cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
-                               cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
-               }
-
-               cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP;
-
-               if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags &
-                   cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
-                       cmd->rx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
-                       cmd->tx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
-               } else {
-                       cmd->rx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
-                       cmd->tx_data_timeout_uapsd =
-                               cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
-               }
+       if (vif->p2p &&
+           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD))
+               allow_uapsd = false;
+       /*
+        * Avoid using uAPSD if P2P client is associated to GO that uses
+        * opportunistic power save. This is due to current FW limitation.
+        */
+       if (vif->p2p &&
+           vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
+           IEEE80211_P2P_OPPPS_ENABLE_BIT)
+               allow_uapsd = false;
 
-               if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
-                       cmd->heavy_tx_thld_packets =
-                               IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
-                       cmd->heavy_rx_thld_packets =
-                               IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
-               } else {
-                       cmd->heavy_tx_thld_packets =
-                               IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
-                       cmd->heavy_rx_thld_packets =
-                               IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
-               }
-               cmd->heavy_tx_thld_percentage =
-                       IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
-               cmd->heavy_rx_thld_percentage =
-                       IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
-       }
+       if (allow_uapsd)
+               iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
@@ -381,6 +409,13 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
                        cmd->flags &=
                                cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
        }
+       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
+               u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
+               if (mvmvif->dbgfs_pm.uapsd_misbehaving)
+                       cmd->flags |= cpu_to_le16(flag);
+               else
+                       cmd->flags &= cpu_to_le16(flag);
+       }
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
@@ -391,18 +426,11 @@ static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
        bool ba_enable;
        struct iwl_mac_power_cmd cmd = {};
 
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (vif->type != NL80211_IFTYPE_STATION)
                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)
+       if (vif->p2p &&
+           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS))
                return 0;
 
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
@@ -446,7 +474,7 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
                                    sizeof(cmd), &cmd);
 }
 
-static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
 {
        struct iwl_device_power_cmd cmd = {
                .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
@@ -455,7 +483,8 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                return 0;
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
+           force_disable)
                cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -472,6 +501,78 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
                                    &cmd);
 }
 
+static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
+{
+       return _iwl_mvm_power_update_device(mvm, false);
+}
+
+void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
+                  ETH_ALEN))
+               memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN);
+}
+
+static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
+                                                    struct ieee80211_vif *vif)
+{
+       u8 *ap_sta_id = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       /* The ap_sta_id is not expected to change during current association
+        * so no explicit protection is needed
+        */
+       if (mvmvif->ap_sta_id == *ap_sta_id)
+               memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
+                      ETH_ALEN);
+}
+
+int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
+                                            struct iwl_rx_cmd_buffer *rxb,
+                                            struct iwl_device_cmd *cmd)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
+       u8 ap_sta_id = le32_to_cpu(notif->sta_id);
+
+       ieee80211_iterate_active_interfaces_atomic(
+               mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+               iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);
+
+       return 0;
+}
+
+static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
+                                          struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mvm *mvm = _data;
+       int ret;
+
+       mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
+
+       ret = iwl_mvm_power_mac_update_mode(mvm, vif);
+       WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
+}
+
+static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         bool assign)
+{
+       if (vif->type == NL80211_IFTYPE_MONITOR) {
+               int ret = _iwl_mvm_power_update_device(mvm, assign);
+               mvm->ps_prevented = assign;
+               WARN_ONCE(ret, "Failed to update power device state\n");
+       }
+
+       ieee80211_iterate_active_interfaces(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_power_binding_iterator,
+                                           mvm);
+}
+
 #ifdef CONFIG_IWLWIFI_DEBUGFS
 static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
                                        struct ieee80211_vif *vif, char *buf,
@@ -494,70 +595,58 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
        pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
                         le16_to_cpu(cmd.keep_alive_seconds));
 
-       if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) {
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
-                                (cmd.flags &
-                                cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ?
-                                1 : 0);
-               pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
-                                cmd.skip_dtim_periods);
-               if (!(cmd.flags &
-                     cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "rx_data_timeout = %d\n",
-                                        le32_to_cpu(cmd.rx_data_timeout));
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "tx_data_timeout = %d\n",
-                                        le32_to_cpu(cmd.tx_data_timeout));
-               }
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "lprx_rssi_threshold = %d\n",
-                                        cmd.lprx_rssi_threshold);
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
-                       pos +=
-                       scnprintf(buf+pos, bufsz-pos,
-                                 "rx_data_timeout_uapsd = %d\n",
-                                 le32_to_cpu(cmd.rx_data_timeout_uapsd));
-                       pos +=
-                       scnprintf(buf+pos, bufsz-pos,
-                                 "tx_data_timeout_uapsd = %d\n",
-                                 le32_to_cpu(cmd.tx_data_timeout_uapsd));
-                       pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n",
-                                        cmd.qndp_tid);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "uapsd_ac_flags = 0x%x\n",
-                                        cmd.uapsd_ac_flags);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "uapsd_max_sp = %d\n",
-                                        cmd.uapsd_max_sp);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_tx_thld_packets = %d\n",
-                                        cmd.heavy_tx_thld_packets);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_rx_thld_packets = %d\n",
-                                        cmd.heavy_rx_thld_packets);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_tx_thld_percentage = %d\n",
-                                        cmd.heavy_tx_thld_percentage);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "heavy_rx_thld_percentage = %d\n",
-                                        cmd.heavy_rx_thld_percentage);
-                       pos +=
-                       scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n",
-                                 (cmd.flags &
-                                  cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ?
-                                 1 : 0);
-               }
-               if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "snooze_interval = %d\n",
-                                        cmd.snooze_interval);
-                       pos += scnprintf(buf+pos, bufsz-pos,
-                                        "snooze_window = %d\n",
-                                        cmd.snooze_window);
-               }
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))
+               return pos;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
+                        (cmd.flags &
+                        cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);
+       pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
+                        cmd.skip_dtim_periods);
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
+               pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.rx_data_timeout));
+               pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
+                                le32_to_cpu(cmd.tx_data_timeout));
        }
+       if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
+               pos += scnprintf(buf+pos, bufsz-pos,
+                                "lprx_rssi_threshold = %d\n",
+                                cmd.lprx_rssi_threshold);
+
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
+               return pos;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",
+                        le32_to_cpu(cmd.rx_data_timeout_uapsd));
+       pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",
+                        le32_to_cpu(cmd.tx_data_timeout_uapsd));
+       pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);
+       pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",
+                        cmd.uapsd_ac_flags);
+       pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",
+                        cmd.uapsd_max_sp);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",
+                        cmd.heavy_tx_thld_packets);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",
+                        cmd.heavy_rx_thld_packets);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",
+                        cmd.heavy_tx_thld_percentage);
+       pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",
+                        cmd.heavy_rx_thld_percentage);
+       pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",
+                        (cmd.flags &
+                         cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?
+                        1 : 0);
+
+       if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))
+               return pos;
+
+       pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",
+                        cmd.snooze_interval);
+       pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",
+                        cmd.snooze_window);
+
        return pos;
 }
 
@@ -654,6 +743,7 @@ const struct iwl_mvm_power_ops pm_mac_ops = {
        .power_update_mode = iwl_mvm_power_mac_update_mode,
        .power_update_device_mode = iwl_mvm_power_update_device,
        .power_disable = iwl_mvm_power_mac_disable,
+       .power_update_binding = _iwl_mvm_power_update_binding,
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
 #endif
index 2ce79ba..ef712ae 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 38165eb..ce5db6c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index bf6e29f..ba078a3 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -24,7 +24,6 @@
  *
  *****************************************************************************/
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <net/mac80211.h>
 
 #define RS_NAME "iwl-mvm-rs"
 
-#define NUM_TRY_BEFORE_ANT_TOGGLE 1
-#define IWL_NUMBER_TRY      1
-#define IWL_HT_NUMBER_TRY   3
+#define NUM_TRY_BEFORE_ANT_TOGGLE       1
+#define RS_LEGACY_RETRIES_PER_RATE      1
+#define RS_HT_VHT_RETRIES_PER_RATE      2
+#define RS_HT_VHT_RETRIES_PER_RATE_TW   1
+#define RS_INITIAL_MIMO_NUM_RATES       3
+#define RS_INITIAL_SISO_NUM_RATES       3
+#define RS_INITIAL_LEGACY_NUM_RATES     LINK_QUAL_MAX_RETRY_NUM
+#define RS_SECONDARY_LEGACY_NUM_RATES   LINK_QUAL_MAX_RETRY_NUM
+#define RS_SECONDARY_SISO_NUM_RATES     3
+#define RS_SECONDARY_SISO_RETRIES       1
 
 #define IWL_RATE_MAX_WINDOW            62      /* # tx in history window */
 #define IWL_RATE_MIN_FAILURE_TH                3       /* min failures to calc tpt */
@@ -123,6 +129,12 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = {
        IWL_DECLARE_MCS_RATE(9),                 /* MCS 9 */
 };
 
+enum rs_action {
+       RS_ACTION_STAY = 0,
+       RS_ACTION_DOWNSCALE = -1,
+       RS_ACTION_UPSCALE = 1,
+};
+
 enum rs_column_mode {
        RS_INVALID = 0,
        RS_LEGACY,
@@ -351,20 +363,12 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                                   struct sk_buff *skb,
                                   struct ieee80211_sta *sta,
                                   struct iwl_lq_sta *lq_sta);
-static void rs_fill_link_cmd(struct iwl_mvm *mvm,
-                            struct ieee80211_sta *sta,
-                            struct iwl_lq_sta *lq_sta, u32 rate_n_flags);
+static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
+                          struct ieee80211_sta *sta,
+                          struct iwl_lq_sta *lq_sta,
+                          const struct rs_rate *initial_rate);
 static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search);
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags);
-#else
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags)
-{}
-#endif
-
 /**
  * The following tables contain the expected throughput metrics for all rates
  *
@@ -504,30 +508,6 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type)
        return (ant_type & valid_antenna) == ant_type;
 }
 
-#ifdef CONFIG_MAC80211_DEBUGFS
-/**
- * Program the device to use fixed rate for frame transmit
- * This is for debugging/testing only
- * once the device start use fixed rate, we need to reload the module
- * to being back the normal operation.
- */
-static void rs_program_fix_rate(struct iwl_mvm *mvm,
-                               struct iwl_lq_sta *lq_sta)
-{
-       lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
-       lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
-       lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
-
-       IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
-                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
-
-       if (lq_sta->dbg_fixed_rate) {
-               rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate);
-               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
-       }
-}
-#endif
-
 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)
@@ -658,7 +638,7 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl,
 
 /* Convert rs_rate object into ucode rate bitmask */
 static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm,
-                                  struct rs_rate *rate)
+                                 struct rs_rate *rate)
 {
        u32 ucode_rate = 0;
        int index = rate->index;
@@ -719,7 +699,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
        u8 num_of_ant = get_num_of_ant_from_rate(ucode_rate);
        u8 nss;
 
-       memset(rate, 0, sizeof(struct rs_rate));
+       memset(rate, 0, sizeof(*rate));
        rate->index = iwl_hwrate_to_plcp_idx(ucode_rate);
 
        if (rate->index == IWL_RATE_INVALID) {
@@ -785,8 +765,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate,
 
 /* switch to another antenna/antennas and return 1 */
 /* if no other valid antenna found, return 0 */
-static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate,
-                            struct rs_rate *rate)
+static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate)
 {
        u8 new_ant_type;
 
@@ -807,9 +786,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate,
 
        rate->ant = new_ant_type;
 
-       /* TODO: get rid of ucode_rate here. This should handle only rs_rate */
-       *ucode_rate &= ~RATE_MCS_ANT_ABC_MSK;
-       *ucode_rate |= new_ant_type << RATE_MCS_ANT_POS;
        return 1;
 }
 
@@ -883,65 +859,73 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask,
        return (high << 8) | low;
 }
 
-static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta,
-                            struct rs_rate *rate,
-                            u8 scale_index, u8 ht_possible)
+static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta,
+                                    struct rs_rate *rate)
 {
-       s32 low;
-       u16 rate_mask;
+       return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate);
+}
+
+/* Get the next supported lower rate in the current column.
+ * Return true if bottom rate in the current column was reached
+ */
+static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
+                                       struct rs_rate *rate)
+{
+       u8 low;
        u16 high_low;
-       u8 switch_to_legacy = 0;
+       u16 rate_mask;
        struct iwl_mvm *mvm = lq_sta->drv;
 
-       /* check if we need to switch from HT to legacy rates.
-        * assumption is that mandatory rates (1Mbps or 6Mbps)
-        * are always supported (spec demand) */
-       if (!is_legacy(rate) && (!ht_possible || !scale_index)) {
-               switch_to_legacy = 1;
-               WARN_ON_ONCE(scale_index < IWL_RATE_MCS_0_INDEX &&
-                            scale_index > IWL_RATE_MCS_9_INDEX);
-               scale_index = rs_ht_to_legacy[scale_index];
+       rate_mask = rs_get_supported_rates(lq_sta, rate);
+       high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
+                                       rate->type);
+       low = high_low & 0xff;
+
+       /* Bottom rate of column reached */
+       if (low == IWL_RATE_INVALID)
+               return true;
+
+       rate->index = low;
+       return false;
+}
+
+/* Get the next rate to use following a column downgrade */
+static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta,
+                                         struct rs_rate *rate)
+{
+       struct iwl_mvm *mvm = lq_sta->drv;
+
+       if (is_legacy(rate)) {
+               /* No column to downgrade from Legacy */
+               return;
+       } else if (is_siso(rate)) {
+               /* Downgrade to Legacy if we were in SISO */
                if (lq_sta->band == IEEE80211_BAND_5GHZ)
                        rate->type = LQ_LEGACY_A;
                else
                        rate->type = LQ_LEGACY_G;
 
-               if (num_of_ant(rate->ant) > 1)
-                       rate->ant =
-                           first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
-
                rate->bw = RATE_MCS_CHAN_WIDTH_20;
-               rate->sgi = false;
-       }
 
-       rate_mask = rs_get_supported_rates(lq_sta, rate);
+               WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
+                            rate->index > IWL_RATE_MCS_9_INDEX);
 
-       /* Mask with station rate restriction */
-       if (is_legacy(rate)) {
-               /* supp_rates has no CCK bits in A mode */
-               if (lq_sta->band == IEEE80211_BAND_5GHZ)
-                       rate_mask = (u16)(rate_mask &
-                          (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
-               else
-                       rate_mask = (u16)(rate_mask & lq_sta->supp_rates);
+               rate->index = rs_ht_to_legacy[rate->index];
+       } else {
+               /* Downgrade to SISO with same MCS if in MIMO  */
+               rate->type = is_vht_mimo2(rate) ?
+                       LQ_VHT_SISO : LQ_HT_SISO;
        }
 
-       /* If we switched from HT to legacy, check current rate */
-       if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
-               low = scale_index;
-               goto out;
-       }
 
-       high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
-                                       rate->type);
-       low = high_low & 0xff;
+       if (num_of_ant(rate->ant) > 1)
+               rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
 
-       if (low == IWL_RATE_INVALID)
-               low = scale_index;
+       /* Relevant in both switching to SISO or Legacy */
+       rate->sgi = false;
 
-out:
-       rate->index = low;
-       return ucode_rate_from_rs_rate(lq_sta->drv, rate);
+       if (!rs_rate_supported(lq_sta, rate))
+               rs_get_lower_rate_in_column(lq_sta, rate);
 }
 
 /* Simple function to compare two rate scale table types */
@@ -1137,14 +1121,9 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
                                tmp_tbl = curr_tbl;
                        else if (rs_rate_match(&rate, &other_tbl->rate))
                                tmp_tbl = other_tbl;
-                       else {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Tx packet rate doesn't match ACTIVE or SEARCH tables\n");
-                               rs_dump_rate(mvm, &rate, "Tx PACKET:");
-                               rs_dump_rate(mvm, &curr_tbl->rate, "CURRENT:");
-                               rs_dump_rate(mvm, &other_tbl->rate, "OTHER:");
+                       else
                                continue;
-                       }
+
                        rs_collect_tx_data(tmp_tbl, rate.index, 1,
                                           i < retries ? 0 : legacy_success);
                }
@@ -1471,10 +1450,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm,
                               struct iwl_lq_sta *lq_sta,
                               struct rs_rate *rate)
 {
-       u32 ucode_rate;
-
-       ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
-       rs_fill_link_cmd(mvm, sta, lq_sta, ucode_rate);
+       rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
 }
 
@@ -1634,10 +1610,6 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
                rate->index = rate_idx;
        }
 
-       /* TODO: remove current_rate and keep using rs_rate all the way until
-        * we need to fill in the rs_table in the LQ command
-        */
-       search_tbl->current_rate = ucode_rate_from_rs_rate(mvm, rate);
        IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n",
                       col_id, rate->index);
 
@@ -1649,6 +1621,97 @@ err:
        return -1;
 }
 
+static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
+                                        struct iwl_scale_tbl_info *tbl,
+                                        s32 sr, int low, int high,
+                                        int current_tpt,
+                                        int low_tpt, int high_tpt)
+{
+       enum rs_action action = RS_ACTION_STAY;
+
+       /* Too many failures, decrease rate */
+       if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
+               IWL_DEBUG_RATE(mvm,
+                              "decrease rate because of low SR\n");
+               action = RS_ACTION_DOWNSCALE;
+       /* No throughput measured yet for adjacent rates; try increase. */
+       } else if ((low_tpt == IWL_INVALID_VALUE) &&
+                  (high_tpt == IWL_INVALID_VALUE)) {
+               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "Good SR and no high rate measurement. "
+                                      "Increase rate\n");
+                       action = RS_ACTION_UPSCALE;
+               } else if (low != IWL_RATE_INVALID) {
+                       IWL_DEBUG_RATE(mvm,
+                                      "Remain in current rate\n");
+                       action = RS_ACTION_STAY;
+               }
+       }
+
+       /* Both adjacent throughputs are measured, but neither one has better
+        * throughput; we're using the best rate, don't change it!
+        */
+       else if ((low_tpt != IWL_INVALID_VALUE) &&
+                (high_tpt != IWL_INVALID_VALUE) &&
+                (low_tpt < current_tpt) &&
+                (high_tpt < current_tpt)) {
+               IWL_DEBUG_RATE(mvm,
+                              "Both high and low are worse. "
+                              "Maintain rate\n");
+               action = RS_ACTION_STAY;
+       }
+
+       /* At least one adjacent rate's throughput is measured,
+        * and may have better performance.
+        */
+       else {
+               /* Higher adjacent rate's throughput is measured */
+               if (high_tpt != IWL_INVALID_VALUE) {
+                       /* Higher rate has better throughput */
+                       if (high_tpt > current_tpt &&
+                           sr >= IWL_RATE_INCREASE_TH) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Higher rate is better and good "
+                                              "SR. Increate rate\n");
+                               action = RS_ACTION_UPSCALE;
+                       } else {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Higher rate isn't better OR "
+                                              "no good SR. Maintain rate\n");
+                               action = RS_ACTION_STAY;
+                       }
+
+               /* Lower adjacent rate's throughput is measured */
+               } else if (low_tpt != IWL_INVALID_VALUE) {
+                       /* Lower rate has better throughput */
+                       if (low_tpt > current_tpt) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Lower rate is better. "
+                                              "Decrease rate\n");
+                               action = RS_ACTION_DOWNSCALE;
+                       } else if (sr >= IWL_RATE_INCREASE_TH) {
+                               IWL_DEBUG_RATE(mvm,
+                                              "Lower rate isn't better and "
+                                              "good SR. Increase rate\n");
+                               action = RS_ACTION_UPSCALE;
+                       }
+               }
+       }
+
+       /* Sanity check; asked for decrease, but success rate or throughput
+        * has been good at old rate.  Don't change it.
+        */
+       if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID) &&
+           ((sr > IWL_RATE_HIGH_TH) ||
+            (current_tpt > (100 * tbl->expected_tpt[low])))) {
+               IWL_DEBUG_RATE(mvm,
+                              "Sanity check failed. Maintain rate\n");
+               action = RS_ACTION_STAY;
+       }
+
+       return action;
+}
 
 /*
  * Do rate scaling and search for new modulation mode.
@@ -1669,11 +1732,10 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        int low_tpt = IWL_INVALID_VALUE;
        int high_tpt = IWL_INVALID_VALUE;
        u32 fail_count;
-       s8 scale_action = 0;
+       enum rs_action scale_action = RS_ACTION_STAY;
        u16 rate_mask;
        u8 update_lq = 0;
        struct iwl_scale_tbl_info *tbl, *tbl1;
-       u16 rate_scale_index_msk = 0;
        u8 active_tbl = 0;
        u8 done_search = 0;
        u16 high_low;
@@ -1690,8 +1752,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
            info->flags & IEEE80211_TX_CTL_NO_ACK)
                return;
 
-       lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
-
        tid = rs_get_tid(lq_sta, hdr);
        if ((tid != IWL_MAX_TID_COUNT) &&
            (lq_sta->tx_agg_tid_en & (1 << tid))) {
@@ -1730,33 +1790,13 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        /* rates available for this association, and for modulation mode */
        rate_mask = rs_get_supported_rates(lq_sta, rate);
 
-       /* mask with station rate restriction */
-       if (is_legacy(rate)) {
-               if (lq_sta->band == IEEE80211_BAND_5GHZ)
-                       /* supp_rates has no CCK bits in A mode */
-                       rate_scale_index_msk = (u16) (rate_mask &
-                               (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
-               else
-                       rate_scale_index_msk = (u16) (rate_mask &
-                                                     lq_sta->supp_rates);
-
-       } else {
-               rate_scale_index_msk = rate_mask;
-       }
-
-       if (!rate_scale_index_msk)
-               rate_scale_index_msk = rate_mask;
-
-       if (!((BIT(index) & rate_scale_index_msk))) {
+       if (!(BIT(index) & rate_mask)) {
                IWL_ERR(mvm, "Current Rate is not valid\n");
                if (lq_sta->search_better_tbl) {
                        /* revert to active table if search table is not valid*/
                        rate->type = LQ_NONE;
                        lq_sta->search_better_tbl = 0;
                        tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
-                       /* get "active" rate info */
-                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
-                       tbl->rate.index = index;
                        rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
                }
                return;
@@ -1847,7 +1887,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                        tbl = &(lq_sta->lq_info[active_tbl]);
 
                        /* Revert to "active" rate and throughput info */
-                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       index = tbl->rate.index;
                        current_tpt = lq_sta->last_tpt;
 
                        /* Need to set up a new rate table in uCode */
@@ -1863,8 +1903,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
 
        /* (Else) not in search of better modulation mode, try for better
         * starting rate, while staying in this mode. */
-       high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk,
-                                       rate->type);
+       high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type);
        low = high_low & 0xff;
        high = (high_low >> 8) & 0xff;
 
@@ -1887,85 +1926,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                       rs_pretty_lq_type(rate->type), index, current_tpt, sr,
                       low, high, low_tpt, high_tpt);
 
-       scale_action = 0;
-
-       /* Too many failures, decrease rate */
-       if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
-               IWL_DEBUG_RATE(mvm,
-                              "decrease rate because of low SR\n");
-               scale_action = -1;
-       /* No throughput measured yet for adjacent rates; try increase. */
-       } else if ((low_tpt == IWL_INVALID_VALUE) &&
-                  (high_tpt == IWL_INVALID_VALUE)) {
-               if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
-                       IWL_DEBUG_RATE(mvm,
-                                      "Good SR and no high rate measurement. "
-                                      "Increase rate\n");
-                       scale_action = 1;
-               } else if (low != IWL_RATE_INVALID) {
-                       IWL_DEBUG_RATE(mvm,
-                                      "Remain in current rate\n");
-                       scale_action = 0;
-               }
-       }
-
-       /* Both adjacent throughputs are measured, but neither one has better
-        * throughput; we're using the best rate, don't change it! */
-       else if ((low_tpt != IWL_INVALID_VALUE) &&
-                (high_tpt != IWL_INVALID_VALUE) &&
-                (low_tpt < current_tpt) &&
-                (high_tpt < current_tpt)) {
-               IWL_DEBUG_RATE(mvm,
-                              "Both high and low are worse. "
-                              "Maintain rate\n");
-               scale_action = 0;
-       }
-
-       /* At least one adjacent rate's throughput is measured,
-        * and may have better performance. */
-       else {
-               /* Higher adjacent rate's throughput is measured */
-               if (high_tpt != IWL_INVALID_VALUE) {
-                       /* Higher rate has better throughput */
-                       if (high_tpt > current_tpt &&
-                           sr >= IWL_RATE_INCREASE_TH) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Higher rate is better and good "
-                                              "SR. Increate rate\n");
-                               scale_action = 1;
-                       } else {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Higher rate isn't better OR "
-                                              "no good SR. Maintain rate\n");
-                               scale_action = 0;
-                       }
-
-               /* Lower adjacent rate's throughput is measured */
-               } else if (low_tpt != IWL_INVALID_VALUE) {
-                       /* Lower rate has better throughput */
-                       if (low_tpt > current_tpt) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Lower rate is better. "
-                                              "Decrease rate\n");
-                               scale_action = -1;
-                       } else if (sr >= IWL_RATE_INCREASE_TH) {
-                               IWL_DEBUG_RATE(mvm,
-                                              "Lower rate isn't better and "
-                                              "good SR. Increase rate\n");
-                               scale_action = 1;
-                       }
-               }
-       }
-
-       /* Sanity check; asked for decrease, but success rate or throughput
-        * has been good at old rate.  Don't change it. */
-       if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
-           ((sr > IWL_RATE_HIGH_TH) ||
-            (current_tpt > (100 * tbl->expected_tpt[low])))) {
-               IWL_DEBUG_RATE(mvm,
-                              "Sanity check failed. Maintain rate\n");
-               scale_action = 0;
-       }
+       scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
+                                         current_tpt, low_tpt, high_tpt);
 
        /* Force a search in case BT doesn't like us being in MIMO */
        if (is_mimo(rate) &&
@@ -1977,7 +1939,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
        }
 
        switch (scale_action) {
-       case -1:
+       case RS_ACTION_DOWNSCALE:
                /* Decrease starting rate, update uCode's rate table */
                if (low != IWL_RATE_INVALID) {
                        update_lq = 1;
@@ -1988,7 +1950,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                }
 
                break;
-       case 1:
+       case RS_ACTION_UPSCALE:
                /* Increase starting rate, update uCode's rate table */
                if (high != IWL_RATE_INVALID) {
                        update_lq = 1;
@@ -1999,7 +1961,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
                }
 
                break;
-       case 0:
+       case RS_ACTION_STAY:
                /* No change */
        default:
                break;
@@ -2053,11 +2015,11 @@ lq_update:
                                rs_rate_scale_clear_window(&(tbl->win[i]));
 
                        /* Use new "search" start rate */
-                       index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
+                       index = tbl->rate.index;
 
                        rs_dump_rate(mvm, &tbl->rate,
                                     "Switch to SEARCH TABLE:");
-                       rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate);
+                       rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
                        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
                } else {
                        done_search = 1;
@@ -2095,8 +2057,6 @@ lq_update:
        }
 
 out:
-       tbl->rate.index = index;
-       tbl->current_rate = ucode_rate_from_rs_rate(mvm, &tbl->rate);
        lq_sta->last_txrate_idx = index;
 }
 
@@ -2123,7 +2083,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        struct iwl_scale_tbl_info *tbl;
        struct rs_rate *rate;
        int i;
-       u32 ucode_rate;
        u8 active_tbl = 0;
        u8 valid_tx_ant;
 
@@ -2154,9 +2113,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
        else
                rate->type = LQ_LEGACY_G;
 
-       ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
-       tbl->current_rate = ucode_rate;
-
        WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
        if (rate->ant == ANT_A)
                tbl->column = RS_COLUMN_LEGACY_ANT_A;
@@ -2164,7 +2120,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
                tbl->column = RS_COLUMN_LEGACY_ANT_B;
 
        rs_set_expected_tpt_table(lq_sta, tbl);
-       rs_fill_link_cmd(NULL, NULL, lq_sta, ucode_rate);
+       rs_fill_lq_cmd(mvm, sta, lq_sta, rate);
        /* TODO restore station should remember the lq cmd */
        iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
 }
@@ -2250,6 +2206,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
+                       /* Disable MCS9 as a workaround */
+                       if (i == IWL_RATE_MCS_9_INDEX)
+                               continue;
+
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2268,6 +2228,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
                        if (i == IWL_RATE_9M_INDEX)
                                continue;
 
+                       /* Disable MCS9 as a workaround */
+                       if (i == IWL_RATE_MCS_9_INDEX)
+                               continue;
+
                        /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
                        if (i == IWL_RATE_MCS_9_INDEX &&
                            sta->bandwidth == IEEE80211_STA_RX_BW_20)
@@ -2306,7 +2270,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
                        rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
 
        lq_sta->flush_timer = 0;
-       lq_sta->supp_rates = sta->supp_rates[sband->band];
 
        IWL_DEBUG_RATE(mvm,
                       "LQ: *** rate scale station global init for station %d ***\n",
@@ -2395,112 +2358,163 @@ static void rs_rate_update(void *mvm_r,
        iwl_mvm_rs_rate_init(mvm, sta, sband->band, false);
 }
 
-static void rs_fill_link_cmd(struct iwl_mvm *mvm,
-                            struct ieee80211_sta *sta,
-                            struct iwl_lq_sta *lq_sta, u32 new_rate)
+#ifdef CONFIG_MAC80211_DEBUGFS
+static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm,
+                                           struct iwl_lq_cmd *lq_cmd,
+                                           enum ieee80211_band band,
+                                           u32 ucode_rate)
 {
        struct rs_rate rate;
-       int index = 0;
-       int repeat_rate = 0;
-       u8 ant_toggle_cnt = 0;
-       u8 use_ht_possible = 1;
+       int i;
+       int num_rates = ARRAY_SIZE(lq_cmd->rs_table);
+       __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate);
+
+       for (i = 0; i < num_rates; i++)
+               lq_cmd->rs_table[i] = ucode_rate_le32;
+
+       rs_rate_from_ucode_rate(ucode_rate, band, &rate);
+
+       if (is_mimo(&rate))
+               lq_cmd->mimo_delim = num_rates - 1;
+       else
+               lq_cmd->mimo_delim = 0;
+}
+#endif /* CONFIG_MAC80211_DEBUGFS */
+
+static void rs_fill_rates_for_column(struct iwl_mvm *mvm,
+                                    struct iwl_lq_sta *lq_sta,
+                                    struct rs_rate *rate,
+                                    __le32 *rs_table, int *rs_table_index,
+                                    int num_rates, int num_retries,
+                                    u8 valid_tx_ant, bool toggle_ant)
+{
+       int i, j;
+       __le32 ucode_rate;
+       bool bottom_reached = false;
+       int prev_rate_idx = rate->index;
+       int end = LINK_QUAL_MAX_RETRY_NUM;
+       int index = *rs_table_index;
+
+       for (i = 0; i < num_rates && index < end; i++) {
+               ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate));
+               for (j = 0; j < num_retries && index < end; j++, index++)
+                       rs_table[index] = ucode_rate;
+
+               if (toggle_ant)
+                       rs_toggle_antenna(valid_tx_ant, rate);
+
+               prev_rate_idx = rate->index;
+               bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate);
+               if (bottom_reached && !is_legacy(rate))
+                       break;
+       }
+
+       if (!bottom_reached)
+               rate->index = prev_rate_idx;
+
+       *rs_table_index = index;
+}
+
+/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI
+ * column the rate table should look like this:
+ *
+ * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI
+ * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI
+ * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI
+ * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI
+ * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI
+ * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI
+ * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps
+ * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps
+ * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps
+ * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps
+ * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps
+ * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps
+ * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps
+ */
+static void rs_build_rates_table(struct iwl_mvm *mvm,
+                                struct iwl_lq_sta *lq_sta,
+                                const struct rs_rate *initial_rate)
+{
+       struct rs_rate rate;
+       int num_rates, num_retries, index = 0;
        u8 valid_tx_ant = 0;
        struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+       bool toggle_ant = false;
 
-       /* Override starting rate (index 0) if needed for debug purposes */
-       rs_dbgfs_set_mcs(lq_sta, &new_rate);
+       memcpy(&rate, initial_rate, sizeof(rate));
 
-       rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate);
+       valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
 
-       /* How many times should we repeat the initial rate? */
-       if (is_legacy(&rate)) {
-               ant_toggle_cnt = 1;
-               repeat_rate = IWL_NUMBER_TRY;
+       if (is_siso(&rate)) {
+               num_rates = RS_INITIAL_SISO_NUM_RATES;
+               num_retries = RS_HT_VHT_RETRIES_PER_RATE;
+       } else if (is_mimo(&rate)) {
+               num_rates = RS_INITIAL_MIMO_NUM_RATES;
+               num_retries = RS_HT_VHT_RETRIES_PER_RATE;
        } else {
-               repeat_rate = min(IWL_HT_NUMBER_TRY,
-                                 LINK_QUAL_AGG_DISABLE_START_DEF - 1);
+               num_rates = RS_INITIAL_LEGACY_NUM_RATES;
+               num_retries = RS_LEGACY_RETRIES_PER_RATE;
+               toggle_ant = true;
        }
 
-       lq_cmd->mimo_delim = is_mimo(&rate) ? 1 : 0;
-
-       /* Fill 1st table entry (index 0) */
-       lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
-
-       if (num_of_ant(rate.ant) == 1)
-               lq_cmd->single_stream_ant_msk = rate.ant;
-       /* otherwise we don't modify the existing value */
-
-       index++;
-       repeat_rate--;
-       if (mvm)
-               valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
-
-       /* Fill rest of rate table */
-       while (index < LINK_QUAL_MAX_RETRY_NUM) {
-               /* Repeat initial/next rate.
-                * For legacy IWL_NUMBER_TRY == 1, this loop will not execute.
-                * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */
-               while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) {
-                       if (is_legacy(&rate)) {
-                               if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
-                                       ant_toggle_cnt++;
-                               else if (mvm &&
-                                        rs_toggle_antenna(valid_tx_ant,
-                                                       &new_rate, &rate))
-                                       ant_toggle_cnt = 1;
-                       }
+       rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+                                num_rates, num_retries, valid_tx_ant,
+                                toggle_ant);
 
-                       /* Override next rate if needed for debug purposes */
-                       rs_dbgfs_set_mcs(lq_sta, &new_rate);
+       rs_get_lower_rate_down_column(lq_sta, &rate);
 
-                       /* Fill next table entry */
-                       lq_cmd->rs_table[index] =
-                                       cpu_to_le32(new_rate);
-                       repeat_rate--;
-                       index++;
-               }
+       if (is_siso(&rate)) {
+               num_rates = RS_SECONDARY_SISO_NUM_RATES;
+               num_retries = RS_SECONDARY_SISO_RETRIES;
+       } else if (is_legacy(&rate)) {
+               num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
+               num_retries = RS_LEGACY_RETRIES_PER_RATE;
+       } else {
+               WARN_ON_ONCE(1);
+       }
 
-               rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate);
+       toggle_ant = true;
 
-               /* Indicate to uCode which entries might be MIMO.
-                * If initial rate was MIMO, this will finally end up
-                * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */
-               if (is_mimo(&rate))
-                       lq_cmd->mimo_delim = index;
+       rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+                                num_rates, num_retries, valid_tx_ant,
+                                toggle_ant);
 
-               /* Get next rate */
-               new_rate = rs_get_lower_rate(lq_sta, &rate, rate.index,
-                                            use_ht_possible);
+       rs_get_lower_rate_down_column(lq_sta, &rate);
 
-               /* How many times should we repeat the next rate? */
-               if (is_legacy(&rate)) {
-                       if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE)
-                               ant_toggle_cnt++;
-                       else if (mvm &&
-                                rs_toggle_antenna(valid_tx_ant,
-                                                  &new_rate, &rate))
-                               ant_toggle_cnt = 1;
+       num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
+       num_retries = RS_LEGACY_RETRIES_PER_RATE;
 
-                       repeat_rate = IWL_NUMBER_TRY;
-               } else {
-                       repeat_rate = IWL_HT_NUMBER_TRY;
-               }
+       rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
+                                num_rates, num_retries, valid_tx_ant,
+                                toggle_ant);
 
-               /* Don't allow HT rates after next pass.
-                * rs_get_lower_rate() will change type to LQ_LEGACY_A
-                * or LQ_LEGACY_G.
-                */
-               use_ht_possible = 0;
+}
 
-               /* Override next rate if needed for debug purposes */
-               rs_dbgfs_set_mcs(lq_sta, &new_rate);
+static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
+                          struct ieee80211_sta *sta,
+                          struct iwl_lq_sta *lq_sta,
+                          const struct rs_rate *initial_rate)
+{
+       struct iwl_lq_cmd *lq_cmd = &lq_sta->lq;
+       u8 ant = initial_rate->ant;
 
-               /* Fill next table entry */
-               lq_cmd->rs_table[index] = cpu_to_le32(new_rate);
+#ifdef CONFIG_MAC80211_DEBUGFS
+       if (lq_sta->dbg_fixed_rate) {
+               rs_build_rates_table_from_fixed(mvm, lq_cmd,
+                                               lq_sta->band,
+                                               lq_sta->dbg_fixed_rate);
+               ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
+                       RATE_MCS_ANT_POS;
+       } else
+#endif
+               rs_build_rates_table(mvm, lq_sta, initial_rate);
 
-               index++;
-               repeat_rate--;
-       }
+       if (num_of_ant(ant) == 1)
+               lq_cmd->single_stream_ant_msk = ant;
 
        lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF;
        lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
@@ -2534,31 +2548,6 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta,
 }
 
 #ifdef CONFIG_MAC80211_DEBUGFS
-static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta,
-                            u32 *rate_n_flags)
-{
-       struct iwl_mvm *mvm;
-       u8 valid_tx_ant;
-       u8 ant_sel_tx;
-
-       mvm = lq_sta->drv;
-       valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw);
-       if (lq_sta->dbg_fixed_rate) {
-               ant_sel_tx =
-                 ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK)
-                 >> RATE_MCS_ANT_POS);
-               if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) {
-                       *rate_n_flags = lq_sta->dbg_fixed_rate;
-               } else {
-                       lq_sta->dbg_fixed_rate = 0;
-                       IWL_ERR(mvm,
-                               "Invalid antenna selection 0x%X, Valid is 0x%X\n",
-                               ant_sel_tx, valid_tx_ant);
-                       IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n");
-               }
-       }
-}
-
 static int rs_pretty_print_rate(char *buf, const u32 rate)
 {
 
@@ -2612,6 +2601,31 @@ static int rs_pretty_print_rate(char *buf, const u32 rate)
                       (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : "");
 }
 
+/**
+ * Program the device to use fixed rate for frame transmit
+ * This is for debugging/testing only
+ * once the device start use fixed rate, we need to reload the module
+ * to being back the normal operation.
+ */
+static void rs_program_fix_rate(struct iwl_mvm *mvm,
+                               struct iwl_lq_sta *lq_sta)
+{
+       lq_sta->active_legacy_rate = 0x0FFF;    /* 1 - 54 MBits, includes CCK */
+       lq_sta->active_siso_rate   = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+       lq_sta->active_mimo2_rate  = 0x1FD0;    /* 6 - 60 MBits, no 9, no CCK */
+
+       IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n",
+                      lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate);
+
+       if (lq_sta->dbg_fixed_rate) {
+               struct rs_rate rate;
+               rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate,
+                                       lq_sta->band, &rate);
+               rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate);
+               iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
+       }
+}
+
 static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
                        const char __user *user_buf, size_t count, loff_t *ppos)
 {
@@ -2702,12 +2716,10 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
                        lq_sta->lq.initial_rate_index[3]);
 
        for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
-               u32 rate = le32_to_cpu(lq_sta->lq.rs_table[i]);
-               desc += sprintf(buff+desc,
-                               " rate[%d] 0x%X ",
-                               i, rate);
+               u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]);
 
-               desc += rs_pretty_print_rate(buff+desc, rate);
+               desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r);
+               desc += rs_pretty_print_rate(buff+desc, r);
        }
 
        ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc);
@@ -2741,14 +2753,14 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file,
                rate = &tbl->rate;
                desc += sprintf(buff+desc,
                                "%s type=%d SGI=%d BW=%s DUP=0\n"
-                               "rate=0x%X\n",
+                               "index=%d\n",
                                lq_sta->active_tbl == i ? "*" : "x",
                                rate->type,
                                rate->sgi,
                                is_ht20(rate) ? "20Mhz" :
                                is_ht40(rate) ? "40Mhz" :
                                is_ht80(rate) ? "80Mhz" : "ERR",
-                               tbl->current_rate);
+                               rate->index);
                for (j = 0; j < IWL_RATE_COUNT; j++) {
                        desc += sprintf(buff+desc,
                                "counter=%d success=%d %%=%d\n",
index b329607..7bc6404 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of version 2 of the GNU General Public License as
@@ -278,7 +278,6 @@ struct iwl_scale_tbl_info {
        struct rs_rate rate;
        enum rs_column column;
        s32 *expected_tpt;      /* throughput metrics; expected_tpt_G, etc. */
-       u32 current_rate;  /* rate_n_flags, uCode API format */
        struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */
 };
 
@@ -315,7 +314,6 @@ struct iwl_lq_sta {
        enum ieee80211_band band;
 
        /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
-       u32 supp_rates;
        u16 active_legacy_rate;
        u16 active_siso_rate;
        u16 active_mimo2_rate;
index 3a1f398..a85b60f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -251,6 +251,12 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm,
                stats->flag |= RX_FLAG_DECRYPTED;
                return 0;
 
+       case RX_MPDU_RES_STATUS_SEC_EXT_ENC:
+               if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK))
+                       return -1;
+               stats->flag |= RX_FLAG_DECRYPTED;
+               return 0;
+
        default:
                IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status);
        }
index 4ce9bb5..0e00079 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -473,13 +473,18 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
        if (mvm->scan_status == IWL_MVM_SCAN_NONE)
                return;
 
+       if (iwl_mvm_is_radio_killed(mvm)) {
+               ieee80211_scan_completed(mvm->hw, true);
+               mvm->scan_status = IWL_MVM_SCAN_NONE;
+               return;
+       }
+
        iwl_init_notification_wait(&mvm->notif_wait, &wait_scan_abort,
                                   scan_abort_notif,
                                   ARRAY_SIZE(scan_abort_notif),
                                   iwl_mvm_scan_abort_notif, NULL);
 
-       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD,
-                                  CMD_SYNC | CMD_SEND_IN_RFKILL, 0, NULL);
+       ret = iwl_mvm_send_cmd_pdu(mvm, SCAN_ABORT_CMD, CMD_SYNC, 0, NULL);
        if (ret) {
                IWL_ERR(mvm, "Couldn't send SCAN_ABORT_CMD: %d\n", ret);
                /* mac80211's state will be cleaned in the fw_restart flow */
diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c
new file mode 100644 (file)
index 0000000..8401627
--- /dev/null
@@ -0,0 +1,291 @@
+/******************************************************************************
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * Contact Information:
+ *  Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+#include "mvm.h"
+
+/* For counting bound interfaces */
+struct iwl_mvm_active_iface_iterator_data {
+       struct ieee80211_vif *ignore_vif;
+       u8 sta_vif_ap_sta_id;
+       enum iwl_sf_state sta_vif_state;
+       int num_active_macs;
+};
+
+/*
+ * Count bound interfaces which are not p2p, besides data->ignore_vif.
+ * data->station_vif will point to one bound vif of type station, if exists.
+ */
+static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac,
+                                        struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_active_iface_iterator_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (vif == data->ignore_vif || !mvmvif->phy_ctxt ||
+           vif->type == NL80211_IFTYPE_P2P_DEVICE)
+               return;
+
+       data->num_active_macs++;
+
+       if (vif->type == NL80211_IFTYPE_STATION) {
+               data->sta_vif_ap_sta_id = mvmvif->ap_sta_id;
+               if (vif->bss_conf.assoc)
+                       data->sta_vif_state = SF_FULL_ON;
+               else
+                       data->sta_vif_state = SF_INIT_OFF;
+       }
+}
+
+/*
+ * Aging and idle timeouts for the different possible scenarios
+ * in SF_FULL_ON state.
+ */
+static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = {
+       {
+               cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER),
+               cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER),
+               cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_MCAST_AGING_TIMER),
+               cpu_to_le32(SF_MCAST_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_BA_AGING_TIMER),
+               cpu_to_le32(SF_BA_IDLE_TIMER)
+       },
+       {
+               cpu_to_le32(SF_TX_RE_AGING_TIMER),
+               cpu_to_le32(SF_TX_RE_IDLE_TIMER)
+       },
+};
+
+static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd,
+                                   struct ieee80211_sta *sta)
+{
+       int i, j, watermark;
+
+       sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN);
+
+       /*
+        * If we are in association flow - check antenna configuration
+        * capabilities of the AP station, and choose the watermark accordingly.
+        */
+       if (sta) {
+               if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) {
+                       switch (sta->rx_nss) {
+                       case 1:
+                               watermark = SF_W_MARK_SISO;
+                               break;
+                       case 2:
+                               watermark = SF_W_MARK_MIMO2;
+                               break;
+                       default:
+                               watermark = SF_W_MARK_MIMO3;
+                               break;
+                       }
+               } else {
+                       watermark = SF_W_MARK_LEGACY;
+               }
+       /* default watermark value for unassociated mode. */
+       } else {
+               watermark = SF_W_MARK_MIMO2;
+       }
+       sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark);
+
+       for (i = 0; i < SF_NUM_SCENARIO; i++) {
+               for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) {
+                       sf_cmd->long_delay_timeouts[i][j] =
+                                       cpu_to_le32(SF_LONG_DELAY_AGING_TIMER);
+               }
+       }
+       BUILD_BUG_ON(sizeof(sf_full_timeout) !=
+                    sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES);
+
+       memcpy(sf_cmd->full_on_timeouts, sf_full_timeout,
+              sizeof(sf_full_timeout));
+}
+
+static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id,
+                            enum iwl_sf_state new_state)
+{
+       struct iwl_sf_cfg_cmd sf_cmd = {
+               .state = new_state,
+       };
+       struct ieee80211_sta *sta;
+       int ret = 0;
+
+       /*
+        * If an associated AP sta changed its antenna configuration, the state
+        * will remain FULL_ON but SF parameters need to be reconsidered.
+        */
+       if (new_state != SF_FULL_ON && mvm->sf_state == new_state)
+               return 0;
+
+       switch (new_state) {
+       case SF_UNINIT:
+               break;
+       case SF_FULL_ON:
+               if (sta_id == IWL_MVM_STATION_COUNT) {
+                       IWL_ERR(mvm,
+                               "No station: Cannot switch SF to FULL_ON\n");
+                       return -EINVAL;
+               }
+               rcu_read_lock();
+               sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]);
+               if (IS_ERR_OR_NULL(sta)) {
+                       IWL_ERR(mvm, "Invalid station id\n");
+                       rcu_read_unlock();
+                       return -EINVAL;
+               }
+               iwl_mvm_fill_sf_command(&sf_cmd, sta);
+               rcu_read_unlock();
+               break;
+       case SF_INIT_OFF:
+               iwl_mvm_fill_sf_command(&sf_cmd, NULL);
+               break;
+       default:
+               WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n",
+                         new_state);
+               return -EINVAL;
+       }
+
+       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC,
+                                  sizeof(sf_cmd), &sf_cmd);
+       if (!ret)
+               mvm->sf_state = new_state;
+
+       return ret;
+}
+
+/*
+ * Update Smart fifo:
+ * Count bound interfaces that are not to be removed, ignoring p2p devices,
+ * and set new state accordingly.
+ */
+int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif,
+                     bool remove_vif)
+{
+       enum iwl_sf_state new_state;
+       u8 sta_id = IWL_MVM_STATION_COUNT;
+       struct iwl_mvm_vif *mvmvif = NULL;
+       struct iwl_mvm_active_iface_iterator_data data = {
+               .ignore_vif = changed_vif,
+               .sta_vif_state = SF_UNINIT,
+               .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT,
+       };
+
+       if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8)
+               return 0;
+
+       /*
+        * Ignore the call if we are in HW Restart flow, or if the handled
+        * vif is a p2p device.
+        */
+       if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) ||
+           (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE))
+               return 0;
+
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                                  IEEE80211_IFACE_ITER_NORMAL,
+                                                  iwl_mvm_bound_iface_iterator,
+                                                  &data);
+
+       /* If changed_vif exists and is not to be removed, add to the count */
+       if (changed_vif && !remove_vif)
+               data.num_active_macs++;
+
+       switch (data.num_active_macs) {
+       case 0:
+               /* If there are no active macs - change state to SF_INIT_OFF */
+               new_state = SF_INIT_OFF;
+               break;
+       case 1:
+               if (remove_vif) {
+                       /* The one active mac left is of type station
+                        * and we filled the relevant data during iteration
+                        */
+                       new_state = data.sta_vif_state;
+                       sta_id = data.sta_vif_ap_sta_id;
+               } else {
+                       if (WARN_ON(!changed_vif))
+                               return -EINVAL;
+                       if (changed_vif->type != NL80211_IFTYPE_STATION) {
+                               new_state = SF_UNINIT;
+                       } else if (changed_vif->bss_conf.assoc) {
+                               mvmvif = iwl_mvm_vif_from_mac80211(changed_vif);
+                               sta_id = mvmvif->ap_sta_id;
+                               new_state = SF_FULL_ON;
+                       } else {
+                               new_state = SF_INIT_OFF;
+                       }
+               }
+               break;
+       default:
+               /* If there are multiple active macs - change to SF_UNINIT */
+               new_state = SF_UNINIT;
+       }
+       return iwl_mvm_sf_config(mvm, sta_id, new_state);
+}
index 026c2e0..ec18121 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -939,19 +939,6 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n",
                     sta->addr, tid);
 
-       if (mvm->cfg->ht_params->use_rts_for_aggregation) {
-               /*
-                * switch to RTS/CTS if it is the prefer protection
-                * method for HT traffic
-                * this function also sends the LQ command
-                */
-               return iwl_mvm_tx_protection(mvm, mvmsta, true);
-               /*
-                * TODO: remove the TLC_RTS flag when we tear down the last
-                * AGG session (agg_tids_count in DVM)
-                */
-       }
-
        return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false);
 }
 
@@ -1130,8 +1117,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm,
                memcpy(cmd.key, keyconf->key, keyconf->keylen);
                break;
        default:
-               WARN_ON(1);
-               return -EINVAL;
+               key_flags |= cpu_to_le16(STA_KEY_FLG_EXT);
+               memcpy(cmd.key, keyconf->key, keyconf->keylen);
        }
 
        if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE))
@@ -1295,8 +1282,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm,
                                           0, NULL, CMD_SYNC);
                break;
        default:
-               IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher);
-               ret = -EINVAL;
+               ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf,
+                                          sta_id, 0, NULL, CMD_SYNC);
        }
 
        if (ret)
index b349411..4968d02 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index eb74391..0241665 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 95ce4b6..50f3d7f 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index d9c8d6c..4a61c8c 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 18be04d..3afa6b6 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2013 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -340,7 +340,7 @@ static void check_exit_ctkill(struct work_struct *work)
 
        iwl_trans_start_hw(mvm->trans);
        temp = check_nic_temperature(mvm);
-       iwl_trans_stop_hw(mvm->trans, false);
+       iwl_trans_stop_device(mvm->trans);
 
        if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) {
                IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n");
index d87649a..3c575a3 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -253,8 +253,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm,
                memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen);
                break;
        default:
-               IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher);
-               break;
+               tx_cmd->sec_ctl |= TX_CMD_SEC_EXT;
        }
 }
 
index 56cf819..487d61b 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2012 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -518,6 +518,11 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        int i;
 
        lockdep_assert_held(&mvm->mutex);
+
+       /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */
+       if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1)
+               return;
+
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
        mvmvif->smps_requests[req_type] = smps_request;
        for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) {
index 2e97a39..e58b8af 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
index 051268c..e851f26 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -256,13 +256,13 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
  * @hw_base: pci hardware address support
  * @ucode_write_complete: indicates that the ucode has been copied.
  * @ucode_write_waitq: wait queue for uCode load
- * @status - transport specific status flags
  * @cmd_queue - command queue number
  * @rx_buf_size_8k: 8 kB RX buffer size
  * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes)
  * @rx_page_order: page order for receive buffer size
  * @wd_timeout: queue watchdog timeout (jiffies)
  * @reg_lock: protect hw register access
+ * @cmd_in_flight: true when we have a host command in flight
  */
 struct iwl_trans_pcie {
        struct iwl_rxq rxq;
@@ -274,7 +274,6 @@ struct iwl_trans_pcie {
        __le32 *ict_tbl;
        dma_addr_t ict_tbl_dma;
        int ict_index;
-       u32 inta;
        bool use_ict;
        struct isr_statistics isr_stats;
 
@@ -296,7 +295,6 @@ struct iwl_trans_pcie {
        wait_queue_head_t ucode_write_waitq;
        wait_queue_head_t wait_command_queue;
 
-       unsigned long status;
        u8 cmd_queue;
        u8 cmd_fifo;
        u8 n_no_reclaim_cmds;
@@ -313,24 +311,7 @@ struct iwl_trans_pcie {
 
        /*protect hw register */
        spinlock_t reg_lock;
-};
-
-/**
- * enum iwl_pcie_status: status of the PCIe transport
- * @STATUS_HCMD_ACTIVE: a SYNC command is being processed
- * @STATUS_DEVICE_ENABLED: APM is enabled
- * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up)
- * @STATUS_INT_ENABLED: interrupts are enabled
- * @STATUS_RFKILL: the HW RFkill switch is in KILL position
- * @STATUS_FW_ERROR: the fw is in error state
- */
-enum iwl_pcie_status {
-       STATUS_HCMD_ACTIVE,
-       STATUS_DEVICE_ENABLED,
-       STATUS_TPOWER_PMI,
-       STATUS_INT_ENABLED,
-       STATUS_RFKILL,
-       STATUS_FW_ERROR,
+       bool cmd_in_flight;
 };
 
 #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
@@ -363,7 +344,7 @@ void iwl_pcie_rx_free(struct iwl_trans *trans);
 /*****************************************************
 * ICT - interrupt handling
 ******************************************************/
-irqreturn_t iwl_pcie_isr_ict(int irq, void *data);
+irqreturn_t iwl_pcie_isr(int irq, void *data);
 int iwl_pcie_alloc_ict(struct iwl_trans *trans);
 void iwl_pcie_free_ict(struct iwl_trans *trans);
 void iwl_pcie_reset_ict(struct iwl_trans *trans);
@@ -399,8 +380,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans);
 ******************************************************/
 static inline void iwl_disable_interrupts(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       clear_bit(STATUS_INT_ENABLED, &trans_pcie->status);
+       clear_bit(STATUS_INT_ENABLED, &trans->status);
 
        /* disable interrupts from uCode/NIC to host */
        iwl_write32(trans, CSR_INT_MASK, 0x00000000);
@@ -417,14 +397,18 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
        IWL_DEBUG_ISR(trans, "Enabling interrupts\n");
-       set_bit(STATUS_INT_ENABLED, &trans_pcie->status);
+       set_bit(STATUS_INT_ENABLED, &trans->status);
+       trans_pcie->inta_mask = CSR_INI_SET_MASK;
        iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
 }
 
 static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
 {
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
        IWL_DEBUG_ISR(trans, "Enabling rfkill interrupt\n");
-       iwl_write32(trans, CSR_INT_MASK, CSR_INT_BIT_RF_KILL);
+       trans_pcie->inta_mask = CSR_INT_BIT_RF_KILL;
+       iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask);
 }
 
 static inline void iwl_wake_queue(struct iwl_trans *trans,
@@ -477,12 +461,31 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
                CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW);
 }
 
-static inline void iwl_nic_error(struct iwl_trans *trans)
+static inline void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
+                                                 u32 reg, u32 mask, u32 value)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 v;
+
+#ifdef CONFIG_IWLWIFI_DEBUG
+       WARN_ON_ONCE(value & ~mask);
+#endif
+
+       v = iwl_read32(trans, reg);
+       v &= ~mask;
+       v |= value;
+       iwl_write32(trans, reg, v);
+}
 
-       set_bit(STATUS_FW_ERROR, &trans_pcie->status);
-       iwl_op_mode_nic_error(trans->op_mode);
+static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
+                                             u32 reg, u32 mask)
+{
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
+}
+
+static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
+                                           u32 reg, u32 mask)
+{
+       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
 }
 
 #endif /* __iwl_trans_int_pcie_h__ */
index 1d6bf7b..1890ea2 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -148,10 +148,9 @@ int iwl_pcie_rx_stop(struct iwl_trans *trans)
 static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
                                    struct iwl_rxq *rxq)
 {
-       unsigned long flags;
        u32 reg;
 
-       spin_lock_irqsave(&rxq->lock, flags);
+       spin_lock(&rxq->lock);
 
        if (rxq->need_update == 0)
                goto exit_unlock;
@@ -162,11 +161,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
                rxq->write_actual = (rxq->write & ~0x7);
                iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual);
        } else {
-               struct iwl_trans_pcie *trans_pcie =
-                       IWL_TRANS_GET_PCIE_TRANS(trans);
-
                /* If power-saving is in use, make sure device is awake */
-               if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) {
+               if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
                        reg = iwl_read32(trans, CSR_UCODE_DRV_GP1);
 
                        if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) {
@@ -193,7 +189,7 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans,
        rxq->need_update = 0;
 
  exit_unlock:
-       spin_unlock_irqrestore(&rxq->lock, flags);
+       spin_unlock(&rxq->lock);
 }
 
 /*
@@ -212,7 +208,6 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_rxq *rxq = &trans_pcie->rxq;
        struct iwl_rx_mem_buffer *rxb;
-       unsigned long flags;
 
        /*
         * If the device isn't enabled - not need to try to add buffers...
@@ -222,10 +217,10 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
         * stopped, we cannot access the HW (in particular not prph).
         * So don't try to restock if the APM has been already stopped.
         */
-       if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status))
+       if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status))
                return;
 
-       spin_lock_irqsave(&rxq->lock, flags);
+       spin_lock(&rxq->lock);
        while ((iwl_rxq_space(rxq) > 0) && (rxq->free_count)) {
                /* The overwritten rxb must be a used one */
                rxb = rxq->queue[rxq->write];
@@ -242,7 +237,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
                rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
                rxq->free_count--;
        }
-       spin_unlock_irqrestore(&rxq->lock, flags);
+       spin_unlock(&rxq->lock);
        /* If the pre-allocated buffer pool is dropping low, schedule to
         * refill it */
        if (rxq->free_count <= RX_LOW_WATERMARK)
@@ -251,9 +246,9 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans)
        /* If we've added more space for the firmware to place data, tell it.
         * Increment device's write pointer in multiples of 8. */
        if (rxq->write_actual != (rxq->write & ~0x7)) {
-               spin_lock_irqsave(&rxq->lock, flags);
+               spin_lock(&rxq->lock);
                rxq->need_update = 1;
-               spin_unlock_irqrestore(&rxq->lock, flags);
+               spin_unlock(&rxq->lock);
                iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
        }
 }
@@ -273,16 +268,15 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority)
        struct iwl_rxq *rxq = &trans_pcie->rxq;
        struct iwl_rx_mem_buffer *rxb;
        struct page *page;
-       unsigned long flags;
        gfp_t gfp_mask = priority;
 
        while (1) {
-               spin_lock_irqsave(&rxq->lock, flags);
+               spin_lock(&rxq->lock);
                if (list_empty(&rxq->rx_used)) {
-                       spin_unlock_irqrestore(&rxq->lock, flags);
+                       spin_unlock(&rxq->lock);
                        return;
                }
-               spin_unlock_irqrestore(&rxq->lock, flags);
+               spin_unlock(&rxq->lock);
 
                if (rxq->free_count > RX_LOW_WATERMARK)
                        gfp_mask |= __GFP_NOWARN;
@@ -311,17 +305,17 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority)
                        return;
                }
 
-               spin_lock_irqsave(&rxq->lock, flags);
+               spin_lock(&rxq->lock);
 
                if (list_empty(&rxq->rx_used)) {
-                       spin_unlock_irqrestore(&rxq->lock, flags);
+                       spin_unlock(&rxq->lock);
                        __free_pages(page, trans_pcie->rx_page_order);
                        return;
                }
                rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer,
                                       list);
                list_del(&rxb->list);
-               spin_unlock_irqrestore(&rxq->lock, flags);
+               spin_unlock(&rxq->lock);
 
                BUG_ON(rxb->page);
                rxb->page = page;
@@ -332,9 +326,9 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority)
                                     DMA_FROM_DEVICE);
                if (dma_mapping_error(trans->dev, rxb->page_dma)) {
                        rxb->page = NULL;
-                       spin_lock_irqsave(&rxq->lock, flags);
+                       spin_lock(&rxq->lock);
                        list_add(&rxb->list, &rxq->rx_used);
-                       spin_unlock_irqrestore(&rxq->lock, flags);
+                       spin_unlock(&rxq->lock);
                        __free_pages(page, trans_pcie->rx_page_order);
                        return;
                }
@@ -343,12 +337,12 @@ static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority)
                /* and also 256 byte aligned! */
                BUG_ON(rxb->page_dma & DMA_BIT_MASK(8));
 
-               spin_lock_irqsave(&rxq->lock, flags);
+               spin_lock(&rxq->lock);
 
                list_add_tail(&rxb->list, &rxq->rx_free);
                rxq->free_count++;
 
-               spin_unlock_irqrestore(&rxq->lock, flags);
+               spin_unlock(&rxq->lock);
        }
 }
 
@@ -382,13 +376,12 @@ static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans)
 static void iwl_pcie_rx_replenish(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       unsigned long flags;
 
        iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL);
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        iwl_pcie_rxq_restock(trans);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 }
 
 static void iwl_pcie_rx_replenish_now(struct iwl_trans *trans)
@@ -514,7 +507,6 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_rxq *rxq = &trans_pcie->rxq;
        int i, err;
-       unsigned long flags;
 
        if (!rxq->bd) {
                err = iwl_pcie_rx_alloc(trans);
@@ -522,7 +514,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
                        return err;
        }
 
-       spin_lock_irqsave(&rxq->lock, flags);
+       spin_lock(&rxq->lock);
 
        INIT_WORK(&trans_pcie->rx_replenish, iwl_pcie_rx_replenish_work);
 
@@ -538,16 +530,16 @@ int iwl_pcie_rx_init(struct iwl_trans *trans)
        rxq->read = rxq->write = 0;
        rxq->write_actual = 0;
        memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts));
-       spin_unlock_irqrestore(&rxq->lock, flags);
+       spin_unlock(&rxq->lock);
 
        iwl_pcie_rx_replenish(trans);
 
        iwl_pcie_rx_hw_init(trans, rxq);
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        rxq->need_update = 1;
        iwl_pcie_rxq_inc_wr_ptr(trans, rxq);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        return 0;
 }
@@ -556,7 +548,6 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_rxq *rxq = &trans_pcie->rxq;
-       unsigned long flags;
 
        /*if rxq->bd is NULL, it means that nothing has been allocated,
         * exit now */
@@ -567,9 +558,9 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
 
        cancel_work_sync(&trans_pcie->rx_replenish);
 
-       spin_lock_irqsave(&rxq->lock, flags);
+       spin_lock(&rxq->lock);
        iwl_pcie_rxq_free_rbs(trans);
-       spin_unlock_irqrestore(&rxq->lock, flags);
+       spin_unlock(&rxq->lock);
 
        dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE,
                          rxq->bd, rxq->bd_dma);
@@ -592,7 +583,6 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_rxq *rxq = &trans_pcie->rxq;
        struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
-       unsigned long flags;
        bool page_stolen = false;
        int max_len = PAGE_SIZE << trans_pcie->rx_page_order;
        u32 offset = 0;
@@ -694,7 +684,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
        /* Reuse the page if possible. For notification packets and
         * SKBs that fail to Rx correctly, add them back into the
         * rx_free list for reuse later. */
-       spin_lock_irqsave(&rxq->lock, flags);
+       spin_lock(&rxq->lock);
        if (rxb->page != NULL) {
                rxb->page_dma =
                        dma_map_page(trans->dev, rxb->page, 0,
@@ -715,7 +705,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans,
                }
        } else
                list_add_tail(&rxb->list, &rxq->rx_used);
-       spin_unlock_irqrestore(&rxq->lock, flags);
+       spin_unlock(&rxq->lock);
 }
 
 /*
@@ -791,7 +781,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
                             APMS_CLK_VAL_MRB_FUNC_MODE) ||
             (iwl_read_prph(trans, APMG_PS_CTRL_REG) &
                            APMG_PS_CTRL_VAL_RESET_REQ))) {
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                iwl_op_mode_wimax_active(trans->op_mode);
                wake_up(&trans_pcie->wait_command_queue);
                return;
@@ -800,14 +790,95 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans)
        iwl_pcie_dump_csr(trans);
        iwl_dump_fh(trans, NULL);
 
-       /* set the ERROR bit before we wake up the caller */
-       set_bit(STATUS_FW_ERROR, &trans_pcie->status);
-       clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
-       wake_up(&trans_pcie->wait_command_queue);
-
        local_bh_disable();
-       iwl_nic_error(trans);
+       /* The STATUS_FW_ERROR bit is set in this function. This must happen
+        * before we wake up the command caller, to ensure a proper cleanup. */
+       iwl_trans_fw_error(trans);
        local_bh_enable();
+
+       clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+       wake_up(&trans_pcie->wait_command_queue);
+}
+
+static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 inta;
+
+       lockdep_assert_held(&trans_pcie->irq_lock);
+
+       trace_iwlwifi_dev_irq(trans->dev);
+
+       /* Discover which interrupts are active/pending */
+       inta = iwl_read32(trans, CSR_INT);
+
+       /* the thread will service interrupts and re-enable them */
+       return inta;
+}
+
+/* a device (PCI-E) page is 4096 bytes long */
+#define ICT_SHIFT      12
+#define ICT_SIZE       (1 << ICT_SHIFT)
+#define ICT_COUNT      (ICT_SIZE / sizeof(u32))
+
+/* interrupt handler using ict table, with this interrupt driver will
+ * stop using INTA register to get device's interrupt, reading this register
+ * is expensive, device will write interrupts in ICT dram table, increment
+ * index then will fire interrupt to driver, driver will OR all ICT table
+ * entries from current index up to table entry with 0 value. the result is
+ * the interrupt we need to service, driver will set the entries back to 0 and
+ * set index.
+ */
+static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
+{
+       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+       u32 inta;
+       u32 val = 0;
+       u32 read;
+
+       trace_iwlwifi_dev_irq(trans->dev);
+
+       /* Ignore interrupt if there's nothing in NIC to service.
+        * This may be due to IRQ shared with another device,
+        * or due to sporadic interrupts thrown from our NIC. */
+       read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
+       trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read);
+       if (!read)
+               return 0;
+
+       /*
+        * Collect all entries up to the first 0, starting from ict_index;
+        * note we already read at ict_index.
+        */
+       do {
+               val |= read;
+               IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n",
+                               trans_pcie->ict_index, read);
+               trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
+               trans_pcie->ict_index =
+                       iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT);
+
+               read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
+               trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
+                                          read);
+       } while (read);
+
+       /* We should not get this value, just ignore it. */
+       if (val == 0xffffffff)
+               val = 0;
+
+       /*
+        * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
+        * (bit 15 before shifting it to 31) to clear when using interrupt
+        * coalescing. fortunately, bits 18 and 19 stay set when this happens
+        * so we use them to decide on the real state of the Rx bit.
+        * In order words, bit 15 is set if bit 18 or bit 19 are set.
+        */
+       if (val & 0xC0000)
+               val |= 0x8000;
+
+       inta = (0xff & val) | ((0xff00 & val) << 16);
+       return inta;
 }
 
 irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
@@ -817,12 +888,61 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
        struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
        u32 inta = 0;
        u32 handled = 0;
-       unsigned long flags;
        u32 i;
 
        lock_map_acquire(&trans->sync_cmd_lockdep_map);
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
+
+       /* dram interrupt table not set yet,
+        * use legacy interrupt.
+        */
+       if (likely(trans_pcie->use_ict))
+               inta = iwl_pcie_int_cause_ict(trans);
+       else
+               inta = iwl_pcie_int_cause_non_ict(trans);
+
+       if (iwl_have_debug_level(IWL_DL_ISR)) {
+               IWL_DEBUG_ISR(trans,
+                             "ISR inta 0x%08x, enabled 0x%08x(sw), enabled(hw) 0x%08x, fh 0x%08x\n",
+                             inta, trans_pcie->inta_mask,
+                             iwl_read32(trans, CSR_INT_MASK),
+                             iwl_read32(trans, CSR_FH_INT_STATUS));
+               if (inta & (~trans_pcie->inta_mask))
+                       IWL_DEBUG_ISR(trans,
+                                     "We got a masked interrupt (0x%08x)\n",
+                                     inta & (~trans_pcie->inta_mask));
+       }
+
+       inta &= trans_pcie->inta_mask;
+
+       /*
+        * Ignore interrupt if there's nothing in NIC to service.
+        * This may be due to IRQ shared with another device,
+        * or due to sporadic interrupts thrown from our NIC.
+        */
+       if (unlikely(!inta)) {
+               IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
+               /*
+                * Re-enable interrupts here since we don't
+                * have anything to service
+                */
+               if (test_bit(STATUS_INT_ENABLED, &trans->status))
+                       iwl_enable_interrupts(trans);
+               spin_unlock(&trans_pcie->irq_lock);
+               lock_map_release(&trans->sync_cmd_lockdep_map);
+               return IRQ_NONE;
+       }
+
+       if (unlikely(inta == 0xFFFFFFFF || (inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
+               /*
+                * Hardware disappeared. It might have
+                * already raised an interrupt.
+                */
+               IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
+               spin_unlock(&trans_pcie->irq_lock);
+               goto out;
+       }
 
        /* Ack/clear/reset pending uCode interrupts.
         * Note:  Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS,
@@ -835,19 +955,13 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
         * hardware bugs here by ACKing all the possible interrupts so that
         * interrupt coalescing can still be achieved.
         */
-       iwl_write32(trans, CSR_INT,
-                   trans_pcie->inta | ~trans_pcie->inta_mask);
-
-       inta = trans_pcie->inta;
+       iwl_write32(trans, CSR_INT, inta | ~trans_pcie->inta_mask);
 
        if (iwl_have_debug_level(IWL_DL_ISR))
                IWL_DEBUG_ISR(trans, "inta 0x%08x, enabled 0x%08x\n",
                              inta, iwl_read32(trans, CSR_INT_MASK));
 
-       /* saved interrupt in inta variable now we can reset trans_pcie->inta */
-       trans_pcie->inta = 0;
-
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        /* Now service all interrupt bits discovered above. */
        if (inta & CSR_INT_BIT_HW_ERR) {
@@ -894,14 +1008,14 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
                iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
                if (hw_rfkill) {
-                       set_bit(STATUS_RFKILL, &trans_pcie->status);
-                       if (test_and_clear_bit(STATUS_HCMD_ACTIVE,
-                                              &trans_pcie->status))
+                       set_bit(STATUS_RFKILL, &trans->status);
+                       if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE,
+                                              &trans->status))
                                IWL_DEBUG_RF_KILL(trans,
                                                  "Rfkill while SYNC HCMD in flight\n");
                        wake_up(&trans_pcie->wait_command_queue);
                } else {
-                       clear_bit(STATUS_RFKILL, &trans_pcie->status);
+                       clear_bit(STATUS_RFKILL, &trans->status);
                }
 
                handled |= CSR_INT_BIT_RF_KILL;
@@ -1005,7 +1119,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
        /* Re-enable all interrupts */
        /* only Re-enable if disabled by irq */
-       if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status))
+       if (test_bit(STATUS_INT_ENABLED, &trans->status))
                iwl_enable_interrupts(trans);
        /* Re-enable RF_KILL if it occurred */
        else if (handled & CSR_INT_BIT_RF_KILL)
@@ -1022,11 +1136,6 @@ out:
  *
  ******************************************************************************/
 
-/* a device (PCI-E) page is 4096 bytes long */
-#define ICT_SHIFT      12
-#define ICT_SIZE       (1 << ICT_SHIFT)
-#define ICT_COUNT      (ICT_SIZE / sizeof(u32))
-
 /* Free dram table */
 void iwl_pcie_free_ict(struct iwl_trans *trans)
 {
@@ -1051,7 +1160,7 @@ int iwl_pcie_alloc_ict(struct iwl_trans *trans)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
        trans_pcie->ict_tbl =
-               dma_alloc_coherent(trans->dev, ICT_SIZE,
+               dma_zalloc_coherent(trans->dev, ICT_SIZE,
                                   &trans_pcie->ict_tbl_dma,
                                   GFP_KERNEL);
        if (!trans_pcie->ict_tbl)
@@ -1063,17 +1172,10 @@ int iwl_pcie_alloc_ict(struct iwl_trans *trans)
                return -EINVAL;
        }
 
-       IWL_DEBUG_ISR(trans, "ict dma addr %Lx\n",
-                     (unsigned long long)trans_pcie->ict_tbl_dma);
-
-       IWL_DEBUG_ISR(trans, "ict vir addr %p\n", trans_pcie->ict_tbl);
-
-       /* reset table and index to all 0 */
-       memset(trans_pcie->ict_tbl, 0, ICT_SIZE);
-       trans_pcie->ict_index = 0;
+       IWL_DEBUG_ISR(trans, "ict dma addr %Lx ict vir addr %p\n",
+                     (unsigned long long)trans_pcie->ict_tbl_dma,
+                     trans_pcie->ict_tbl);
 
-       /* add periodic RX interrupt */
-       trans_pcie->inta_mask |= CSR_INT_BIT_RX_PERIODIC;
        return 0;
 }
 
@@ -1084,12 +1186,11 @@ void iwl_pcie_reset_ict(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        u32 val;
-       unsigned long flags;
 
        if (!trans_pcie->ict_tbl)
                return;
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
 
        memset(trans_pcie->ict_tbl, 0, ICT_SIZE);
@@ -1106,120 +1207,26 @@ void iwl_pcie_reset_ict(struct iwl_trans *trans)
        trans_pcie->ict_index = 0;
        iwl_write32(trans, CSR_INT, trans_pcie->inta_mask);
        iwl_enable_interrupts(trans);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 }
 
 /* Device is going down disable ict interrupt usage */
 void iwl_pcie_disable_ict(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       unsigned long flags;
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        trans_pcie->use_ict = false;
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 }
 
-/* legacy (non-ICT) ISR. Assumes that trans_pcie->irq_lock is held */
-static irqreturn_t iwl_pcie_isr(int irq, void *data)
+irqreturn_t iwl_pcie_isr(int irq, void *data)
 {
        struct iwl_trans *trans = data;
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       u32 inta, inta_mask;
-
-       lockdep_assert_held(&trans_pcie->irq_lock);
-
-       trace_iwlwifi_dev_irq(trans->dev);
-
-       /* Disable (but don't clear!) interrupts here to avoid
-        *    back-to-back ISRs and sporadic interrupts from our NIC.
-        * If we have something to service, the irq thread will re-enable ints.
-        * If we *don't* have something, we'll re-enable before leaving here. */
-       inta_mask = iwl_read32(trans, CSR_INT_MASK);
-       iwl_write32(trans, CSR_INT_MASK, 0x00000000);
-
-       /* Discover which interrupts are active/pending */
-       inta = iwl_read32(trans, CSR_INT);
-
-       if (inta & (~inta_mask)) {
-               IWL_DEBUG_ISR(trans,
-                             "We got a masked interrupt (0x%08x)...Ack and ignore\n",
-                             inta & (~inta_mask));
-               iwl_write32(trans, CSR_INT, inta & (~inta_mask));
-               inta &= inta_mask;
-       }
-
-       /* Ignore interrupt if there's nothing in NIC to service.
-        * This may be due to IRQ shared with another device,
-        * or due to sporadic interrupts thrown from our NIC. */
-       if (!inta) {
-               IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
-               /*
-                * Re-enable interrupts here since we don't have anything to
-                * service, but only in case the handler won't run. Note that
-                * the handler can be scheduled because of a previous
-                * interrupt.
-                */
-               if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
-                   !trans_pcie->inta)
-                       iwl_enable_interrupts(trans);
-               return IRQ_NONE;
-       }
-
-       if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) {
-               /* Hardware disappeared. It might have already raised
-                * an interrupt */
-               IWL_WARN(trans, "HARDWARE GONE?? INTA == 0x%08x\n", inta);
-               return IRQ_HANDLED;
-       }
-
-       if (iwl_have_debug_level(IWL_DL_ISR))
-               IWL_DEBUG_ISR(trans,
-                             "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n",
-                             inta, inta_mask,
-                             iwl_read32(trans, CSR_FH_INT_STATUS));
-
-       trans_pcie->inta |= inta;
-       /* the thread will service interrupts and re-enable them */
-       return IRQ_WAKE_THREAD;
-}
-
-/* interrupt handler using ict table, with this interrupt driver will
- * stop using INTA register to get device's interrupt, reading this register
- * is expensive, device will write interrupts in ICT dram table, increment
- * index then will fire interrupt to driver, driver will OR all ICT table
- * entries from current index up to table entry with 0 value. the result is
- * the interrupt we need to service, driver will set the entries back to 0 and
- * set index.
- */
-irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
-{
-       struct iwl_trans *trans = data;
-       struct iwl_trans_pcie *trans_pcie;
-       u32 inta;
-       u32 val = 0;
-       u32 read;
-       unsigned long flags;
-       irqreturn_t ret = IRQ_NONE;
 
        if (!trans)
                return IRQ_NONE;
 
-       trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
-
-       /* dram interrupt table not set yet,
-        * use legacy interrupt.
-        */
-       if (unlikely(!trans_pcie->use_ict)) {
-               ret = iwl_pcie_isr(irq, data);
-               spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-               return ret;
-       }
-
-       trace_iwlwifi_dev_irq(trans->dev);
-
        /* Disable (but don't clear!) interrupts here to avoid
         * back-to-back ISRs and sporadic interrupts from our NIC.
         * If we have something to service, the tasklet will re-enable ints.
@@ -1227,73 +1234,5 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data)
         */
        iwl_write32(trans, CSR_INT_MASK, 0x00000000);
 
-       /* Ignore interrupt if there's nothing in NIC to service.
-        * This may be due to IRQ shared with another device,
-        * or due to sporadic interrupts thrown from our NIC. */
-       read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
-       trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index, read);
-       if (!read) {
-               IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n");
-               goto none;
-       }
-
-       /*
-        * Collect all entries up to the first 0, starting from ict_index;
-        * note we already read at ict_index.
-        */
-       do {
-               val |= read;
-               IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n",
-                               trans_pcie->ict_index, read);
-               trans_pcie->ict_tbl[trans_pcie->ict_index] = 0;
-               trans_pcie->ict_index =
-                       iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT);
-
-               read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]);
-               trace_iwlwifi_dev_ict_read(trans->dev, trans_pcie->ict_index,
-                                          read);
-       } while (read);
-
-       /* We should not get this value, just ignore it. */
-       if (val == 0xffffffff)
-               val = 0;
-
-       /*
-        * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit
-        * (bit 15 before shifting it to 31) to clear when using interrupt
-        * coalescing. fortunately, bits 18 and 19 stay set when this happens
-        * so we use them to decide on the real state of the Rx bit.
-        * In order words, bit 15 is set if bit 18 or bit 19 are set.
-        */
-       if (val & 0xC0000)
-               val |= 0x8000;
-
-       inta = (0xff & val) | ((0xff00 & val) << 16);
-       IWL_DEBUG_ISR(trans, "ISR inta 0x%08x, enabled(sw) 0x%08x ict 0x%08x\n",
-                     inta, trans_pcie->inta_mask, val);
-       if (iwl_have_debug_level(IWL_DL_ISR))
-               IWL_DEBUG_ISR(trans, "enabled(hw) 0x%08x\n",
-                             iwl_read32(trans, CSR_INT_MASK));
-
-       inta &= trans_pcie->inta_mask;
-       trans_pcie->inta |= inta;
-
-       /* iwl_pcie_tasklet() will service interrupts and re-enable them */
-       if (likely(inta)) {
-               spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-               return IRQ_WAKE_THREAD;
-       }
-
-       ret = IRQ_HANDLED;
-
- none:
-       /* re-enable interrupts here since we don't have anything to service.
-        * only Re-enable if disabled by irq.
-        */
-       if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&
-           !trans_pcie->inta)
-               iwl_enable_interrupts(trans);
-
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-       return ret;
+       return IRQ_WAKE_THREAD;
 }
index cde9c16..16f66c1 100644 (file)
@@ -5,7 +5,7 @@
  *
  * GPL LICENSE SUMMARY
  *
- * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -30,7 +30,7 @@
  *
  * BSD LICENSE
  *
- * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include "iwl-agn-hw.h"
 #include "internal.h"
 
-static void __iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans,
-                                                 u32 reg, u32 mask, u32 value)
-{
-       u32 v;
-
-#ifdef CONFIG_IWLWIFI_DEBUG
-       WARN_ON_ONCE(value & ~mask);
-#endif
-
-       v = iwl_read32(trans, reg);
-       v &= ~mask;
-       v |= value;
-       iwl_write32(trans, reg, v);
-}
-
-static inline void __iwl_trans_pcie_clear_bit(struct iwl_trans *trans,
-                                             u32 reg, u32 mask)
-{
-       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, 0);
-}
-
-static inline void __iwl_trans_pcie_set_bit(struct iwl_trans *trans,
-                                           u32 reg, u32 mask)
-{
-       __iwl_trans_pcie_set_bits_mask(trans, reg, mask, mask);
-}
-
 static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux)
 {
        if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold))
@@ -150,7 +123,6 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans)
  */
 static int iwl_pcie_apm_init(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret = 0;
        IWL_DEBUG_INFO(trans, "Init card's basic functions\n");
 
@@ -223,7 +195,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans)
        /* Clear the interrupt in APMG if the NIC is in RFKILL */
        iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL);
 
-       set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
+       set_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
 out:
        return ret;
@@ -249,10 +221,9 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans)
 
 static void iwl_pcie_apm_stop(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n");
 
-       clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
+       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
 
        /* Stop device's DMA activity */
        iwl_pcie_apm_stop_master(trans);
@@ -273,13 +244,12 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans)
 static int iwl_pcie_nic_init(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       unsigned long flags;
 
        /* nic_init */
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        iwl_pcie_apm_init(trans);
 
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        iwl_pcie_set_pwr(trans, false);
 
@@ -582,7 +552,6 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
 static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                                   const struct fw_img *fw, bool run_in_rfkill)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
        bool hw_rfkill;
 
@@ -592,16 +561,14 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans,
                return -EIO;
        }
 
-       clear_bit(STATUS_FW_ERROR, &trans_pcie->status);
-
        iwl_enable_rfkill_int(trans);
 
        /* If platform's RF_KILL switch is NOT set to KILL */
        hw_rfkill = iwl_is_rfkill_set(trans);
        if (hw_rfkill)
-               set_bit(STATUS_RFKILL, &trans_pcie->status);
+               set_bit(STATUS_RFKILL, &trans->status);
        else
-               clear_bit(STATUS_RFKILL, &trans_pcie->status);
+               clear_bit(STATUS_RFKILL, &trans->status);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
        if (hw_rfkill && !run_in_rfkill)
                return -ERFKILL;
@@ -640,12 +607,14 @@ static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr)
 static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       unsigned long flags;
+       bool hw_rfkill, was_hw_rfkill;
+
+       was_hw_rfkill = iwl_is_rfkill_set(trans);
 
        /* tell the device to stop sending interrupts */
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        /* device going down, Stop using ICT table */
        iwl_pcie_disable_ict(trans);
@@ -657,7 +626,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
         * restart. So don't process again if the device is
         * already dead.
         */
-       if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) {
+       if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
                iwl_pcie_tx_stop(trans);
                iwl_pcie_rx_stop(trans);
 
@@ -677,21 +646,45 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
        /* Upon stop, the APM issues an interrupt if HW RF kill is set.
         * Clean again the interrupt here
         */
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
-
-       iwl_enable_rfkill_int(trans);
+       spin_unlock(&trans_pcie->irq_lock);
 
        /* stop and reset the on-board processor */
        iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
 
        /* clear all status bits */
-       clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
-       clear_bit(STATUS_INT_ENABLED, &trans_pcie->status);
-       clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status);
-       clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
-       clear_bit(STATUS_RFKILL, &trans_pcie->status);
+       clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
+       clear_bit(STATUS_INT_ENABLED, &trans->status);
+       clear_bit(STATUS_DEVICE_ENABLED, &trans->status);
+       clear_bit(STATUS_TPOWER_PMI, &trans->status);
+       clear_bit(STATUS_RFKILL, &trans->status);
+
+       /*
+        * Even if we stop the HW, we still want the RF kill
+        * interrupt
+        */
+       iwl_enable_rfkill_int(trans);
+
+       /*
+        * Check again since the RF kill state may have changed while
+        * all the interrupts were disabled, in this case we couldn't
+        * receive the RF kill interrupt and update the state in the
+        * op_mode.
+        * Don't call the op_mode if the rkfill state hasn't changed.
+        * This allows the op_mode to call stop_device from the rfkill
+        * notification without endless recursion. Under very rare
+        * circumstances, we might have a small recursion if the rfkill
+        * state changed exactly now while we were called from stop_device.
+        * This is very unlikely but can happen and is supported.
+        */
+       hw_rfkill = iwl_is_rfkill_set(trans);
+       if (hw_rfkill)
+               set_bit(STATUS_RFKILL, &trans->status);
+       else
+               clear_bit(STATUS_RFKILL, &trans->status);
+       if (hw_rfkill != was_hw_rfkill)
+               iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
 }
 
 static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
@@ -776,7 +769,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
 
 static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        bool hw_rfkill;
        int err;
 
@@ -787,7 +779,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
        }
 
        /* Reset the entire device */
-       iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
+       iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET);
 
        usleep_range(10, 15);
 
@@ -798,53 +790,30 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
 
        hw_rfkill = iwl_is_rfkill_set(trans);
        if (hw_rfkill)
-               set_bit(STATUS_RFKILL, &trans_pcie->status);
+               set_bit(STATUS_RFKILL, &trans->status);
        else
-               clear_bit(STATUS_RFKILL, &trans_pcie->status);
+               clear_bit(STATUS_RFKILL, &trans->status);
        iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
 
        return 0;
 }
 
-static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans,
-                                  bool op_mode_leaving)
+static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       bool hw_rfkill;
-       unsigned long flags;
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       /* disable interrupts - don't enable HW RF kill interrupt */
+       spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        iwl_pcie_apm_stop(trans);
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
        iwl_disable_interrupts(trans);
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        iwl_pcie_disable_ict(trans);
-
-       if (!op_mode_leaving) {
-               /*
-                * Even if we stop the HW, we still want the RF kill
-                * interrupt
-                */
-               iwl_enable_rfkill_int(trans);
-
-               /*
-                * Check again since the RF kill state may have changed while
-                * all the interrupts were disabled, in this case we couldn't
-                * receive the RF kill interrupt and update the state in the
-                * op_mode.
-                */
-               hw_rfkill = iwl_is_rfkill_set(trans);
-               if (hw_rfkill)
-                       set_bit(STATUS_RFKILL, &trans_pcie->status);
-               else
-                       clear_bit(STATUS_RFKILL, &trans_pcie->status);
-               iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
-       }
 }
 
 static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val)
@@ -928,12 +897,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
 
 static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
        if (state)
-               set_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
+               set_bit(STATUS_TPOWER_PMI, &trans->status);
        else
-               clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status);
+               clear_bit(STATUS_TPOWER_PMI, &trans->status);
 }
 
 static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
@@ -944,6 +911,9 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
 
        spin_lock_irqsave(&trans_pcie->reg_lock, *flags);
 
+       if (trans_pcie->cmd_in_flight)
+               goto out;
+
        /* this bit wakes up the NIC */
        __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
                                 CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
@@ -983,6 +953,7 @@ static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent,
                }
        }
 
+out:
        /*
         * Fool sparse by faking we release the lock - sparse will
         * track nic_access anyway.
@@ -1004,6 +975,9 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
         */
        __acquire(&trans_pcie->reg_lock);
 
+       if (trans_pcie->cmd_in_flight)
+               goto out;
+
        __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
                                   CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
        /*
@@ -1013,6 +987,7 @@ static void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans,
         * scheduled on different CPUs (after we drop reg_lock).
         */
        mmiowb();
+out:
        spin_unlock_irqrestore(&trans_pcie->reg_lock, *flags);
 }
 
@@ -1457,7 +1432,7 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
 
 static const struct iwl_trans_ops trans_ops_pcie = {
        .start_hw = iwl_trans_pcie_start_hw,
-       .stop_hw = iwl_trans_pcie_stop_hw,
+       .op_mode_leave = iwl_trans_pcie_op_mode_leave,
        .fw_alive = iwl_trans_pcie_fw_alive,
        .start_fw = iwl_trans_pcie_start_fw,
        .stop_device = iwl_trans_pcie_stop_device,
@@ -1609,7 +1584,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        if (iwl_pcie_alloc_ict(trans))
                goto out_free_cmd_pool;
 
-       err = request_threaded_irq(pdev->irq, iwl_pcie_isr_ict,
+       err = request_threaded_irq(pdev->irq, iwl_pcie_isr,
                                   iwl_pcie_irq_handler,
                                   IRQF_SHARED, DRV_NAME, trans);
        if (err) {
index a4ef5cc..3b14fa8 100644 (file)
@@ -1,6 +1,6 @@
 /******************************************************************************
  *
- * Copyright(c) 2003 - 2013 Intel Corporation. All rights reserved.
+ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
  *
  * Portions of this file are derived from the ipw3945 project, as well
  * as portions of the ieee80211 subsystem header files.
@@ -207,7 +207,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data)
                IWL_ERR(trans, "scratch %d = 0x%08x\n", i,
                        le32_to_cpu(txq->scratchbufs[i].scratch));
 
-       iwl_nic_error(trans);
+       iwl_trans_fw_error(trans);
 }
 
 /*
@@ -300,10 +300,8 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq)
                iwl_write32(trans, HBUS_TARG_WRPTR,
                            txq->q.write_ptr | (txq_id << 8));
        } else {
-               struct iwl_trans_pcie *trans_pcie =
-                       IWL_TRANS_GET_PCIE_TRANS(trans);
                /* if we're trying to save power */
-               if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) {
+               if (test_bit(STATUS_TPOWER_PMI, &trans->status)) {
                        /* wake up nic if it's powered down ...
                         * uCode will wake up, and interrupt us again, so next
                         * time we'll skip this part. */
@@ -739,10 +737,9 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ch, txq_id, ret;
-       unsigned long flags;
 
        /* Turn off all Tx DMA fifos */
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
 
        iwl_pcie_txq_set_sched(trans, 0);
 
@@ -759,13 +756,19 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans)
                                iwl_read_direct32(trans,
                                                  FH_TSSR_TX_STATUS_REG));
        }
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
-       if (!trans_pcie->txq) {
-               IWL_WARN(trans,
-                        "Stopping tx queues that aren't allocated...\n");
+       /*
+        * This function can be called before the op_mode disabled the
+        * queues. This happens when we have an rfkill interrupt.
+        * Since we stop Tx altogether - mark the queues as stopped.
+        */
+       memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped));
+       memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used));
+
+       /* This can happen: start_hw, stop_device */
+       if (!trans_pcie->txq)
                return 0;
-       }
 
        /* Unmap DMA from host system and free skb's */
        for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
@@ -867,7 +870,6 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;
        int txq_id, slots_num;
-       unsigned long flags;
        bool alloc = false;
 
        if (!trans_pcie->txq) {
@@ -877,7 +879,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
                alloc = true;
        }
 
-       spin_lock_irqsave(&trans_pcie->irq_lock, flags);
+       spin_lock(&trans_pcie->irq_lock);
 
        /* Turn off all Tx DMA fifos */
        iwl_write_prph(trans, SCD_TXFACT, 0);
@@ -886,7 +888,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans)
        iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG,
                           trans_pcie->kw.dma >> 4);
 
-       spin_unlock_irqrestore(&trans_pcie->irq_lock, flags);
+       spin_unlock(&trans_pcie->irq_lock);
 
        /* Alloc and init all Tx queues, including the command queue (#4/#9) */
        for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues;
@@ -1005,6 +1007,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        struct iwl_txq *txq = &trans_pcie->txq[txq_id];
        struct iwl_queue *q = &txq->q;
+       unsigned long flags;
        int nfreed = 0;
 
        lockdep_assert_held(&txq->lock);
@@ -1023,10 +1026,20 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx)
                if (nfreed++ > 0) {
                        IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n",
                                idx, q->write_ptr, q->read_ptr);
-                       iwl_nic_error(trans);
+                       iwl_trans_fw_error(trans);
                }
        }
 
+       if (q->read_ptr == q->write_ptr) {
+               spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+               WARN_ON(!trans_pcie->cmd_in_flight);
+               trans_pcie->cmd_in_flight = false;
+               __iwl_trans_pcie_clear_bit(trans,
+                                          CSR_GP_CNTRL,
+                                          CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+               spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+       }
+
        iwl_pcie_txq_progress(trans_pcie, txq);
 }
 
@@ -1143,8 +1156,15 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id)
                        SCD_TX_STTS_QUEUE_OFFSET(txq_id);
        static const u32 zero_val[4] = {};
 
+       /*
+        * Upon HW Rfkill - we stop the device, and then stop the queues
+        * in the op_mode. Just for the sake of the simplicity of the op_mode,
+        * allow the op_mode to call txq_disable after it already called
+        * stop_device.
+        */
        if (!test_and_clear_bit(txq_id, trans_pcie->queue_used)) {
-               WARN_ONCE(1, "queue %d not used", txq_id);
+               WARN_ONCE(test_bit(STATUS_DEVICE_ENABLED, &trans->status),
+                         "queue %d not used", txq_id);
                return;
        }
 
@@ -1178,12 +1198,13 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        struct iwl_queue *q = &txq->q;
        struct iwl_device_cmd *out_cmd;
        struct iwl_cmd_meta *out_meta;
+       unsigned long flags;
        void *dup_buf = NULL;
        dma_addr_t phys_addr;
        int idx;
        u16 copy_size, cmd_size, scratch_size;
        bool had_nocopy = false;
-       int i;
+       int i, ret;
        u32 cmd_pos;
        const u8 *cmddata[IWL_MAX_CMD_TBS_PER_TFD];
        u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD];
@@ -1381,10 +1402,38 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
        if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout)
                mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout);
 
+       spin_lock_irqsave(&trans_pcie->reg_lock, flags);
+
+       /*
+        * wake up the NIC to make sure that the firmware will see the host
+        * command - we will let the NIC sleep once all the host commands
+        * returned.
+        */
+       if (!trans_pcie->cmd_in_flight) {
+               trans_pcie->cmd_in_flight = true;
+               __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL,
+                                        CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+               ret = iwl_poll_bit(trans, CSR_GP_CNTRL,
+                                  CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN,
+                                  (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY |
+                                   CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP),
+                                  15000);
+               if (ret < 0) {
+                       __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL,
+                                  CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
+                       spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+                       trans_pcie->cmd_in_flight = false;
+                       idx = -EIO;
+                       goto out;
+               }
+       }
+
        /* Increment and update queue's write index */
        q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd);
        iwl_pcie_txq_inc_wr_ptr(trans, txq);
 
+       spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
+
  out:
        spin_unlock_bh(&txq->lock);
  free_dup_buf:
@@ -1449,12 +1498,12 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
        iwl_pcie_cmdq_reclaim(trans, txq_id, index);
 
        if (!(meta->flags & CMD_ASYNC)) {
-               if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) {
+               if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) {
                        IWL_WARN(trans,
                                 "HCMD_ACTIVE already clear for command %s\n",
                                 get_cmd_string(trans_pcie, cmd->hdr.cmd));
                }
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
                               get_cmd_string(trans_pcie, cmd->hdr.cmd));
                wake_up(&trans_pcie->wait_command_queue);
@@ -1466,7 +1515,6 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans,
 }
 
 #define HOST_COMPLETE_TIMEOUT  (2 * HZ)
-#define COMMAND_POKE_TIMEOUT   (HZ / 10)
 
 static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans,
                                    struct iwl_host_cmd *cmd)
@@ -1494,13 +1542,12 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int cmd_idx;
        int ret;
-       int timeout = HOST_COMPLETE_TIMEOUT;
 
        IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n",
                       get_cmd_string(trans_pcie, cmd->id));
 
-       if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE,
-                                 &trans_pcie->status),
+       if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE,
+                                 &trans->status),
                 "Command %s: a command is already active!\n",
                 get_cmd_string(trans_pcie, cmd->id)))
                return -EIO;
@@ -1511,36 +1558,17 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd);
        if (cmd_idx < 0) {
                ret = cmd_idx;
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                IWL_ERR(trans,
                        "Error sending %s: enqueue_hcmd failed: %d\n",
                        get_cmd_string(trans_pcie, cmd->id), ret);
                return ret;
        }
 
-       while (timeout > 0) {
-               unsigned long flags;
-
-               timeout -= COMMAND_POKE_TIMEOUT;
-               ret = wait_event_timeout(trans_pcie->wait_command_queue,
-                                        !test_bit(STATUS_HCMD_ACTIVE,
-                                                  &trans_pcie->status),
-                                        COMMAND_POKE_TIMEOUT);
-               if (ret)
-                       break;
-               /* poke the device - it may have lost the command */
-               if (iwl_trans_grab_nic_access(trans, true, &flags)) {
-                       iwl_trans_release_nic_access(trans, &flags);
-                       IWL_DEBUG_INFO(trans,
-                                      "Tried to wake NIC for command %s\n",
-                                      get_cmd_string(trans_pcie, cmd->id));
-               } else {
-                       IWL_ERR(trans, "Failed to poke NIC for command %s\n",
-                               get_cmd_string(trans_pcie, cmd->id));
-                       break;
-               }
-       }
-
+       ret = wait_event_timeout(trans_pcie->wait_command_queue,
+                                !test_bit(STATUS_SYNC_HCMD_ACTIVE,
+                                          &trans->status),
+                                HOST_COMPLETE_TIMEOUT);
        if (!ret) {
                struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
                struct iwl_queue *q = &txq->q;
@@ -1552,17 +1580,17 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
                IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n",
                        q->read_ptr, q->write_ptr);
 
-               clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status);
+               clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status);
                IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n",
                               get_cmd_string(trans_pcie, cmd->id));
                ret = -ETIMEDOUT;
 
-               iwl_nic_error(trans);
+               iwl_trans_fw_error(trans);
 
                goto cancel;
        }
 
-       if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) {
+       if (test_bit(STATUS_FW_ERROR, &trans->status)) {
                IWL_ERR(trans, "FW error in SYNC CMD %s\n",
                        get_cmd_string(trans_pcie, cmd->id));
                dump_stack();
@@ -1571,7 +1599,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
        }
 
        if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
-           test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+           test_bit(STATUS_RFKILL, &trans->status)) {
                IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n");
                ret = -ERFKILL;
                goto cancel;
@@ -1608,13 +1636,8 @@ cancel:
 
 int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
 {
-       struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-
-       if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
-               return -EIO;
-
        if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
-           test_bit(STATUS_RFKILL, &trans_pcie->status)) {
+           test_bit(STATUS_RFKILL, &trans->status)) {
                IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
                                  cmd->id);
                return -ERFKILL;
index 3ff28c2..e7c81ab 100644 (file)
@@ -538,23 +538,33 @@ static void mwifiex_reg_notifier(struct wiphy *wiphy,
                                 struct regulatory_request *request)
 {
        struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy);
+       struct mwifiex_private *priv = mwifiex_get_priv(adapter,
+                                                       MWIFIEX_BSS_ROLE_ANY);
 
        wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n",
                  request->alpha2[0], request->alpha2[1]);
 
-       memcpy(adapter->country_code, request->alpha2, sizeof(request->alpha2));
-
        switch (request->initiator) {
        case NL80211_REGDOM_SET_BY_DRIVER:
        case NL80211_REGDOM_SET_BY_CORE:
        case NL80211_REGDOM_SET_BY_USER:
-               break;
-               /* Todo: apply driver specific changes in channel flags based
-                  on the request initiator if necessary. */
        case NL80211_REGDOM_SET_BY_COUNTRY_IE:
                break;
+       default:
+               wiphy_err(wiphy, "unknown regdom initiator: %d\n",
+                         request->initiator);
+               return;
+       }
+
+       /* Don't send world or same regdom info to firmware */
+       if (strncmp(request->alpha2, "00", 2) &&
+           strncmp(request->alpha2, adapter->country_code,
+                   sizeof(request->alpha2))) {
+               memcpy(adapter->country_code, request->alpha2,
+                      sizeof(request->alpha2));
+               mwifiex_send_domain_info_cmd_fw(wiphy);
+               mwifiex_dnld_txpwr_table(priv);
        }
-       mwifiex_send_domain_info_cmd_fw(wiphy);
 }
 
 /*
index 8fcb500..4cee6ce 100644 (file)
@@ -468,8 +468,6 @@ enum P2P_MODES {
 #define MWIFIEX_CRITERIA_UNICAST       BIT(1)
 #define MWIFIEX_CRITERIA_MULTICAST     BIT(3)
 
-#define CFG_DATA_TYPE_CAL              2
-
 struct mwifiex_ie_types_header {
        __le16 type;
        __le16 len;
@@ -1592,12 +1590,6 @@ struct mwifiex_ie_list {
        struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX];
 } __packed;
 
-struct host_cmd_ds_802_11_cfg_data {
-       __le16 action;
-       __le16 type;
-       __le16 data_len;
-} __packed;
-
 struct coalesce_filt_field_param {
        u8 operation;
        u8 operand_len;
@@ -1678,7 +1670,6 @@ struct host_cmd_ds_command {
                struct host_cmd_ds_sys_config uap_sys_config;
                struct host_cmd_ds_sta_deauth sta_deauth;
                struct host_cmd_11ac_vht_cfg vht_cfg;
-               struct host_cmd_ds_802_11_cfg_data cfg_data;
                struct host_cmd_ds_coalesce_cfg coalesce_cfg;
        } params;
 } __packed;
index dc34457..d8ad554 100644 (file)
@@ -32,6 +32,7 @@
 #include <net/lib80211.h>
 #include <linux/firmware.h>
 #include <linux/ctype.h>
+#include <linux/of.h>
 
 #include "decl.h"
 #include "ioctl.h"
@@ -739,6 +740,7 @@ struct mwifiex_adapter {
        u8 scan_delay_cnt;
        u8 empty_tx_q_cnt;
        const struct firmware *cal_data;
+       struct device_node *dt_node;
 
        /* 11AC */
        u32 is_hw_11ac_capable;
@@ -1151,6 +1153,9 @@ void mwifiex_uap_del_sta_data(struct mwifiex_private *priv,
 void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer,
                              struct mwifiex_bssdescriptor *bss_desc);
 int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv);
+int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
+                           struct device_node *node, const char *prefix);
+void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv);
 
 extern const struct ethtool_ops mwifiex_ethtool_ops;
 
index 1efa43e..9208a88 100644 (file)
@@ -1156,30 +1156,62 @@ static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst)
        return d - dst;
 }
 
+int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv,
+                           struct device_node *node, const char *prefix)
+{
+#ifdef CONFIG_OF
+       struct property *prop;
+       size_t len = strlen(prefix);
+       int ret;
+
+       /* look for all matching property names */
+       for_each_property_of_node(node, prop) {
+               if (len > strlen(prop->name) ||
+                   strncmp(prop->name, prefix, len))
+                       continue;
+
+               /* property header is 6 bytes, data must fit in cmd buffer */
+               if (prop && prop->value && prop->length > 6 &&
+                   prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) {
+                       ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
+                                                   HostCmd_ACT_GEN_SET, 0,
+                                                   prop);
+                       if (ret)
+                               return ret;
+               }
+       }
+#endif
+       return 0;
+}
+
 /* This function prepares command of set_cfg_data. */
 static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv,
-                               struct host_cmd_ds_command *cmd,
-                               u16 cmd_action)
+                               struct host_cmd_ds_command *cmd, void *data_buf)
 {
-       struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data;
        struct mwifiex_adapter *adapter = priv->adapter;
-       u32 len, cal_data_offset;
-       u8 *tmp_cmd = (u8 *)cmd;
+       struct property *prop = data_buf;
+       u32 len;
+       u8 *data = (u8 *)cmd + S_DS_GEN;
+       int ret;
 
-       cal_data_offset = S_DS_GEN + sizeof(*cfg_data);
-       if ((adapter->cal_data->data) && (adapter->cal_data->size > 0))
+       if (prop) {
+               len = prop->length;
+               ret = of_property_read_u8_array(adapter->dt_node, prop->name,
+                                               data, len);
+               if (ret)
+                       return ret;
+               dev_dbg(adapter->dev,
+                       "download cfg_data from device tree: %s\n", prop->name);
+       } else if (adapter->cal_data->data && adapter->cal_data->size > 0) {
                len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data,
-                                           adapter->cal_data->size,
-                                           (u8 *)(tmp_cmd + cal_data_offset));
-       else
+                                           adapter->cal_data->size, data);
+               dev_dbg(adapter->dev, "download cfg_data from config file\n");
+       } else {
                return -1;
-
-       cfg_data->action = cpu_to_le16(cmd_action);
-       cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL);
-       cfg_data->data_len = cpu_to_le16(len);
+       }
 
        cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA);
-       cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len);
+       cmd->size = cpu_to_le16(S_DS_GEN + len);
 
        return 0;
 }
@@ -1267,7 +1299,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no,
                ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr);
                break;
        case HostCmd_CMD_CFG_DATA:
-               ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action);
+               ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf);
                break;
        case HostCmd_CMD_MAC_CONTROL:
                ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action,
@@ -1527,7 +1559,19 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta)
                if (ret)
                        return -1;
 
-               /* Download calibration data to firmware */
+               /* Download calibration data to firmware.
+                * The cal-data can be read from device tree and/or
+                * a configuration file and downloaded to firmware.
+                */
+               adapter->dt_node =
+                               of_find_node_by_name(NULL, "marvell_cfgdata");
+               if (adapter->dt_node) {
+                       ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node,
+                                                     "marvell,caldata");
+                       if (ret)
+                               return -1;
+               }
+
                if (adapter->cal_data) {
                        ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA,
                                                HostCmd_ACT_GEN_SET, 0, NULL);
index a09398f..c5cb2ed 100644 (file)
@@ -184,6 +184,16 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv,
        return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc);
 }
 
+void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv)
+{
+       if (priv->adapter->dt_node) {
+               char txpwr[] = {"marvell,00_txpwrlimit"};
+
+               memcpy(&txpwr[8], priv->adapter->country_code, 2);
+               mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr);
+       }
+}
+
 static int mwifiex_process_country_ie(struct mwifiex_private *priv,
                                      struct cfg80211_bss *bss)
 {
@@ -205,6 +215,14 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv,
                return 0;
        }
 
+       if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) {
+               rcu_read_unlock();
+               wiphy_dbg(priv->wdev->wiphy,
+                         "11D: skip setting domain info in FW\n");
+               return 0;
+       }
+       memcpy(priv->adapter->country_code, &country_ie[2], 2);
+
        domain_info->country_code[0] = country_ie[2];
        domain_info->country_code[1] = country_ie[3];
        domain_info->country_code[2] = ' ';
@@ -226,6 +244,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv,
                return -1;
        }
 
+       mwifiex_dnld_txpwr_table(priv);
+
        return 0;
 }
 
index 721c738..4987c3f 100644 (file)
@@ -9,7 +9,6 @@
  * warranty of any kind, whether express or implied.
  */
 
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -1258,7 +1257,7 @@ mwl8k_capture_bssid(struct mwl8k_priv *priv, struct ieee80211_hdr *wh)
 {
        return priv->capture_beacon &&
                ieee80211_is_beacon(wh->frame_control) &&
-               ether_addr_equal(wh->addr3, priv->capture_bssid);
+               ether_addr_equal_64bits(wh->addr3, priv->capture_bssid);
 }
 
 static inline void mwl8k_save_beacon(struct ieee80211_hw *hw,
index 75c15bc..43790fb 100644 (file)
@@ -40,7 +40,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/delay.h>
 
 #include "hermes.h"
index d21d959..c0a2737 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/delay.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
index bdfe637..f9805c9 100644 (file)
@@ -52,7 +52,6 @@
 #include <linux/signal.h>
 #include <linux/errno.h>
 #include <linux/poll.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/fcntl.h>
 #include <linux/spinlock.h>
index e2264bc..b60048c 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <linux/module.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/delay.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cisreg.h>
index d43e374..0fe67d2 100644 (file)
@@ -16,7 +16,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
 #include <linux/sort.h>
index b3879fb..bc065e8 100644 (file)
@@ -16,7 +16,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
index 3837e1e..1f6fd5f 100644 (file)
@@ -16,7 +16,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
 
index b61fefa..eede90b 100644 (file)
@@ -16,7 +16,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
index f9a07b0..d411de4 100644 (file)
@@ -13,7 +13,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/firmware.h>
index e328d30..6e635cf 100644 (file)
@@ -12,7 +12,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
index f95de0d..9c96831 100644 (file)
@@ -17,7 +17,6 @@
  */
 
 #include <linux/export.h>
-#include <linux/init.h>
 #include <linux/firmware.h>
 #include <linux/etherdevice.h>
 #include <asm/div64.h>
@@ -308,7 +307,7 @@ static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
                return;
 
        /* only consider beacons from the associated BSSID */
-       if (!ether_addr_equal(hdr->addr3, priv->bssid))
+       if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid))
                return;
 
        tim = p54_find_ie(skb, WLAN_EID_TIM);
index c3cdda1..5028557 100644 (file)
@@ -26,7 +26,6 @@
 // #define     VERBOSE                 // more; success messages
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
index 4ad0de9..4ccfef5 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
index 4f61ffb..abc5f56 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/pci.h>
index 1bb7693..9f16824 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
index 49ff178..5c5c490 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/usb.h>
index 00c3fae..2bde672 100644 (file)
@@ -565,10 +565,10 @@ static void rt2x00lib_rxdone_check_ba(struct rt2x00_dev *rt2x00dev,
 
 #undef TID_CHECK
 
-               if (!ether_addr_equal(ba->ra, entry->ta))
+               if (!ether_addr_equal_64bits(ba->ra, entry->ta))
                        continue;
 
-               if (!ether_addr_equal(ba->ta, entry->ra))
+               if (!ether_addr_equal_64bits(ba->ta, entry->ra))
                        continue;
 
                /* Mark BAR since we received the according BA */
index b76f604..2440298 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/crc-itu-t.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
index ade88d7..a140170 100644 (file)
@@ -25,7 +25,6 @@
 #include <linux/crc-itu-t.h>
 #include <linux/delay.h>
 #include <linux/etherdevice.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
index a91506b..8ec17aa 100644 (file)
@@ -15,7 +15,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
index dc84569..b1bfee7 100644 (file)
@@ -19,7 +19,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <net/mac80211.h>
index a63c443..eebf239 100644 (file)
@@ -18,7 +18,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <net/mac80211.h>
index ee638d0..d60a5f3 100644 (file)
@@ -15,7 +15,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <net/mac80211.h>
index 7614d9c..959b049 100644 (file)
@@ -19,7 +19,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <net/mac80211.h>
index ec9aa5b..fd78df8 100644 (file)
@@ -20,7 +20,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/usb.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
index a26193a..5ecf18e 100644 (file)
@@ -16,7 +16,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
 #include <linux/usb.h>
 #include <net/mac80211.h>
 
index 2cfb6d4..93bb384 100644 (file)
@@ -1292,7 +1292,7 @@ void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb)
                return;
 
        /* and only beacons from the associated BSSID, please */
-       if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
+       if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
                return;
 
        rtlpriv->link_info.bcn_rx_inperiod++;
@@ -1780,7 +1780,7 @@ void rtl_recognize_peer(struct ieee80211_hw *hw, u8 *data, unsigned int len)
                return;
 
        /* and only beacons from the associated BSSID, please */
-       if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
+       if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
                return;
 
        if (rtl_find_221_ie(hw, data, len))
index 8707d1a..d7aa165 100644 (file)
@@ -738,6 +738,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
        };
        int index = rtlpci->rx_ring[rx_queue_idx].idx;
 
+       if (rtlpci->driver_is_goingto_unload)
+               return;
        /*RX NORMAL PKT */
        while (count--) {
                /*rx descriptor */
@@ -1634,6 +1636,7 @@ static void rtl_pci_stop(struct ieee80211_hw *hw)
         */
        set_hal_stop(rtlhal);
 
+       rtlpci->driver_is_goingto_unload = true;
        rtlpriv->cfg->ops->disable_interrupt(hw);
        cancel_work_sync(&rtlpriv->works.lps_change_work);
 
@@ -1651,7 +1654,6 @@ static void rtl_pci_stop(struct ieee80211_hw *hw)
        ppsc->rfchange_inprogress = true;
        spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flags);
 
-       rtlpci->driver_is_goingto_unload = true;
        rtlpriv->cfg->ops->hw_disable(hw);
        /* some things are not needed if firmware not available */
        if (!rtlpriv->max_fw_size)
index 0d81f76..deedae3 100644 (file)
@@ -478,7 +478,7 @@ void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
                return;
 
        /* and only beacons from the associated BSSID, please */
-       if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
+       if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
                return;
 
        rtlpriv->psc.last_beacon = jiffies;
@@ -923,7 +923,7 @@ void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
                return;
 
        /* and only beacons from the associated BSSID, please */
-       if (!ether_addr_equal(hdr->addr3, rtlpriv->mac80211.bssid))
+       if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
                return;
 
        /* check if this really is a beacon */
index 374268d..5a4ec56 100644 (file)
@@ -194,7 +194,7 @@ out:
        return ret;
 }
 
-int wl1251_acx_feature_cfg(struct wl1251 *wl)
+int wl1251_acx_feature_cfg(struct wl1251 *wl, u32 data_flow_options)
 {
        struct acx_feature_config *feature;
        int ret;
@@ -205,8 +205,8 @@ int wl1251_acx_feature_cfg(struct wl1251 *wl)
        if (!feature)
                return -ENOMEM;
 
-       /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE are disabled */
-       feature->data_flow_options = 0;
+       /* DF_ENCRYPTION_DISABLE and DF_SNIFF_MODE_ENABLE can be set */
+       feature->data_flow_options = data_flow_options;
        feature->options = 0;
 
        ret = wl1251_cmd_configure(wl, ACX_FEATURE_CFG,
@@ -381,7 +381,8 @@ out:
        return ret;
 }
 
-int wl1251_acx_group_address_tbl(struct wl1251 *wl)
+int wl1251_acx_group_address_tbl(struct wl1251 *wl, bool enable,
+                                void *mc_list, u32 mc_list_len)
 {
        struct acx_dot11_grp_addr_tbl *acx;
        int ret;
@@ -393,9 +394,9 @@ int wl1251_acx_group_address_tbl(struct wl1251 *wl)
                return -ENOMEM;
 
        /* MAC filtering */
-       acx->enabled = 0;
-       acx->num_groups = 0;
-       memset(acx->mac_table, 0, ADDRESS_GROUP_MAX_LEN);
+       acx->enabled = enable;
+       acx->num_groups = mc_list_len;
+       memcpy(acx->mac_table, mc_list, mc_list_len * ETH_ALEN);
 
        ret = wl1251_cmd_configure(wl, DOT11_GROUP_ADDRESS_TBL,
                                   acx, sizeof(*acx));
@@ -846,12 +847,18 @@ int wl1251_acx_rate_policies(struct wl1251 *wl)
                return -ENOMEM;
 
        /* configure one default (one-size-fits-all) rate class */
-       acx->rate_class_cnt = 1;
+       acx->rate_class_cnt = 2;
        acx->rate_class[0].enabled_rates = ACX_RATE_MASK_UNSPECIFIED;
        acx->rate_class[0].short_retry_limit = ACX_RATE_RETRY_LIMIT;
        acx->rate_class[0].long_retry_limit = ACX_RATE_RETRY_LIMIT;
        acx->rate_class[0].aflags = 0;
 
+       /* no-retry rate class */
+       acx->rate_class[1].enabled_rates = ACX_RATE_MASK_UNSPECIFIED;
+       acx->rate_class[1].short_retry_limit = 0;
+       acx->rate_class[1].long_retry_limit = 0;
+       acx->rate_class[1].aflags = 0;
+
        ret = wl1251_cmd_configure(wl, ACX_RATE_POLICY, acx, sizeof(*acx));
        if (ret < 0) {
                wl1251_warning("Setting of rate policies failed: %d", ret);
@@ -960,6 +967,32 @@ out:
        return ret;
 }
 
+int wl1251_acx_arp_ip_filter(struct wl1251 *wl, bool enable, __be32 address)
+{
+       struct wl1251_acx_arp_filter *acx;
+       int ret;
+
+       wl1251_debug(DEBUG_ACX, "acx arp ip filter, enable: %d", enable);
+
+       acx = kzalloc(sizeof(*acx), GFP_KERNEL);
+       if (!acx)
+               return -ENOMEM;
+
+       acx->version = ACX_IPV4_VERSION;
+       acx->enable = enable;
+
+       if (enable)
+               memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE);
+
+       ret = wl1251_cmd_configure(wl, ACX_ARP_IP_FILTER,
+                                  acx, sizeof(*acx));
+       if (ret < 0)
+               wl1251_warning("failed to set arp ip filter: %d", ret);
+
+       kfree(acx);
+       return ret;
+}
+
 int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max,
                      u8 aifs, u16 txop)
 {
index c2ba100..2bdec38 100644 (file)
@@ -350,8 +350,8 @@ struct acx_slot {
 } __packed;
 
 
-#define ADDRESS_GROUP_MAX      (8)
-#define ADDRESS_GROUP_MAX_LEN  (ETH_ALEN * ADDRESS_GROUP_MAX)
+#define ACX_MC_ADDRESS_GROUP_MAX       (8)
+#define ACX_MC_ADDRESS_GROUP_MAX_LEN   (ETH_ALEN * ACX_MC_ADDRESS_GROUP_MAX)
 
 struct acx_dot11_grp_addr_tbl {
        struct acx_header header;
@@ -359,7 +359,7 @@ struct acx_dot11_grp_addr_tbl {
        u8 enabled;
        u8 num_groups;
        u8 pad[2];
-       u8 mac_table[ADDRESS_GROUP_MAX_LEN];
+       u8 mac_table[ACX_MC_ADDRESS_GROUP_MAX_LEN];
 } __packed;
 
 
@@ -1232,6 +1232,20 @@ struct wl1251_acx_bet_enable {
        u8 padding[2];
 } __packed;
 
+#define ACX_IPV4_VERSION 4
+#define ACX_IPV6_VERSION 6
+#define ACX_IPV4_ADDR_SIZE 4
+struct wl1251_acx_arp_filter {
+       struct acx_header header;
+       u8 version;     /* The IP version: 4 - IPv4, 6 - IPv6.*/
+       u8 enable;      /* 1 - ARP filtering is enabled, 0 - disabled */
+       u8 padding[2];
+       u8 address[16]; /* The IP address used to filter ARP packets.
+                          ARP packets that do not match this address are
+                          dropped. When the IP Version is 4, the last 12
+                          bytes of the the address are ignored. */
+} __attribute__((packed));
+
 struct wl1251_acx_ac_cfg {
        struct acx_header header;
 
@@ -1440,7 +1454,7 @@ int wl1251_acx_wake_up_conditions(struct wl1251 *wl, u8 wake_up_event,
 int wl1251_acx_sleep_auth(struct wl1251 *wl, u8 sleep_auth);
 int wl1251_acx_fw_version(struct wl1251 *wl, char *buf, size_t len);
 int wl1251_acx_tx_power(struct wl1251 *wl, int power);
-int wl1251_acx_feature_cfg(struct wl1251 *wl);
+int wl1251_acx_feature_cfg(struct wl1251 *wl, u32 data_flow_options);
 int wl1251_acx_mem_map(struct wl1251 *wl,
                       struct acx_header *mem_map, size_t len);
 int wl1251_acx_data_path_params(struct wl1251 *wl,
@@ -1449,7 +1463,8 @@ int wl1251_acx_rx_msdu_life_time(struct wl1251 *wl, u32 life_time);
 int wl1251_acx_rx_config(struct wl1251 *wl, u32 config, u32 filter);
 int wl1251_acx_pd_threshold(struct wl1251 *wl);
 int wl1251_acx_slot(struct wl1251 *wl, enum acx_slot_type slot_time);
-int wl1251_acx_group_address_tbl(struct wl1251 *wl);
+int wl1251_acx_group_address_tbl(struct wl1251 *wl, bool enable,
+                                void *mc_list, u32 mc_list_len);
 int wl1251_acx_service_period_timeout(struct wl1251 *wl);
 int wl1251_acx_rts_threshold(struct wl1251 *wl, u16 rts_threshold);
 int wl1251_acx_beacon_filter_opt(struct wl1251 *wl, bool enable_filter);
@@ -1473,6 +1488,7 @@ int wl1251_acx_mem_cfg(struct wl1251 *wl);
 int wl1251_acx_wr_tbtt_and_dtim(struct wl1251 *wl, u16 tbtt, u8 dtim);
 int wl1251_acx_bet_enable(struct wl1251 *wl, enum wl1251_acx_bet_mode mode,
                          u8 max_consecutive);
+int wl1251_acx_arp_ip_filter(struct wl1251 *wl, bool enable, __be32 address);
 int wl1251_acx_ac_cfg(struct wl1251 *wl, u8 ac, u8 cw_min, u16 cw_max,
                      u8 aifs, u16 txop);
 int wl1251_acx_tid_cfg(struct wl1251 *wl, u8 queue,
index a2e5241..2000cd5 100644 (file)
@@ -299,7 +299,8 @@ int wl1251_boot_run_firmware(struct wl1251 *wl)
                ROAMING_TRIGGER_LOW_RSSI_EVENT_ID |
                ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID |
                REGAINED_BSS_EVENT_ID | BT_PTA_SENSE_EVENT_ID |
-               BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID;
+               BT_PTA_PREDICTION_EVENT_ID | JOIN_EVENT_COMPLETE_ID |
+               PS_REPORT_EVENT_ID;
 
        ret = wl1251_event_unmask(wl);
        if (ret < 0) {
index 6822b84..223649b 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/crc7.h>
+#include <linux/etherdevice.h>
 
 #include "wl1251.h"
 #include "reg.h"
@@ -203,11 +204,11 @@ out:
        return ret;
 }
 
-int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable)
+int wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable)
 {
        struct cmd_enabledisable_path *cmd;
        int ret;
-       u16 cmd_rx, cmd_tx;
+       u16 cmd_rx;
 
        wl1251_debug(DEBUG_CMD, "cmd data path");
 
@@ -219,13 +220,10 @@ int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable)
 
        cmd->channel = channel;
 
-       if (enable) {
+       if (enable)
                cmd_rx = CMD_ENABLE_RX;
-               cmd_tx = CMD_ENABLE_TX;
-       } else {
+       else
                cmd_rx = CMD_DISABLE_RX;
-               cmd_tx = CMD_DISABLE_TX;
-       }
 
        ret = wl1251_cmd_send(wl, cmd_rx, cmd, sizeof(*cmd));
        if (ret < 0) {
@@ -237,17 +235,38 @@ int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable)
        wl1251_debug(DEBUG_BOOT, "rx %s cmd channel %d",
                     enable ? "start" : "stop", channel);
 
+out:
+       kfree(cmd);
+       return ret;
+}
+
+int wl1251_cmd_data_path_tx(struct wl1251 *wl, u8 channel, bool enable)
+{
+       struct cmd_enabledisable_path *cmd;
+       int ret;
+       u16 cmd_tx;
+
+       wl1251_debug(DEBUG_CMD, "cmd data path");
+
+       cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       cmd->channel = channel;
+
+       if (enable)
+               cmd_tx = CMD_ENABLE_TX;
+       else
+               cmd_tx = CMD_DISABLE_TX;
+
        ret = wl1251_cmd_send(wl, cmd_tx, cmd, sizeof(*cmd));
-       if (ret < 0) {
+       if (ret < 0)
                wl1251_error("tx %s cmd for channel %d failed",
                             enable ? "start" : "stop", channel);
-               goto out;
-       }
-
-       wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d",
-                    enable ? "start" : "stop", channel);
+       else
+               wl1251_debug(DEBUG_BOOT, "tx %s cmd channel %d",
+                            enable ? "start" : "stop", channel);
 
-out:
        kfree(cmd);
        return ret;
 }
@@ -410,7 +429,9 @@ int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len,
        struct wl1251_cmd_scan *cmd;
        int i, ret = 0;
 
-       wl1251_debug(DEBUG_CMD, "cmd scan");
+       wl1251_debug(DEBUG_CMD, "cmd scan channels %d", n_channels);
+
+       WARN_ON(n_channels > SCAN_MAX_NUM_OF_CHANNELS);
 
        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
        if (!cmd)
@@ -421,6 +442,13 @@ int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len,
                                                    CFG_RX_MGMT_EN |
                                                    CFG_RX_BCN_EN);
        cmd->params.scan_options = 0;
+       /*
+        * Use high priority scan when not associated to prevent fw issue
+        * causing never-ending scans (sometimes 20+ minutes).
+        * Note: This bug may be caused by the fw's DTIM handling.
+        */
+       if (is_zero_ether_addr(wl->bssid))
+               cmd->params.scan_options |= WL1251_SCAN_OPT_PRIORITY_HIGH;
        cmd->params.num_channels = n_channels;
        cmd->params.num_probe_requests = n_probes;
        cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */
index ee4f2b3..d824ff9 100644 (file)
@@ -35,7 +35,8 @@ int wl1251_cmd_interrogate(struct wl1251 *wl, u16 id, void *buf, size_t len);
 int wl1251_cmd_configure(struct wl1251 *wl, u16 id, void *buf, size_t len);
 int wl1251_cmd_vbm(struct wl1251 *wl, u8 identity,
                   void *bitmap, u16 bitmap_len, u8 bitmap_control);
-int wl1251_cmd_data_path(struct wl1251 *wl, u8 channel, bool enable);
+int wl1251_cmd_data_path_rx(struct wl1251 *wl, u8 channel, bool enable);
+int wl1251_cmd_data_path_tx(struct wl1251 *wl, u8 channel, bool enable);
 int wl1251_cmd_join(struct wl1251 *wl, u8 bss_type, u8 channel,
                    u16 beacon_interval, u8 dtim_interval);
 int wl1251_cmd_ps_mode(struct wl1251 *wl, u8 ps_mode);
@@ -167,6 +168,11 @@ struct cmd_read_write_memory {
 #define CMDMBOX_HEADER_LEN 4
 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4
 
+#define WL1251_SCAN_OPT_PASSIVE                1
+#define WL1251_SCAN_OPT_5GHZ_BAND      2
+#define WL1251_SCAN_OPT_TRIGGERD_SCAN  4
+#define WL1251_SCAN_OPT_PRIORITY_HIGH  8
+
 #define WL1251_SCAN_MIN_DURATION 30000
 #define WL1251_SCAN_MAX_DURATION 60000
 
index 74ae8e1..db01053 100644 (file)
@@ -46,6 +46,43 @@ static int wl1251_event_scan_complete(struct wl1251 *wl,
        return ret;
 }
 
+#define WL1251_PSM_ENTRY_RETRIES  3
+static int wl1251_event_ps_report(struct wl1251 *wl,
+                                 struct event_mailbox *mbox)
+{
+       int ret = 0;
+
+       wl1251_debug(DEBUG_EVENT, "ps status: %x", mbox->ps_status);
+
+       switch (mbox->ps_status) {
+       case EVENT_ENTER_POWER_SAVE_FAIL:
+               wl1251_debug(DEBUG_PSM, "PSM entry failed");
+
+               if (wl->station_mode != STATION_POWER_SAVE_MODE) {
+                       /* remain in active mode */
+                       wl->psm_entry_retry = 0;
+                       break;
+               }
+
+               if (wl->psm_entry_retry < WL1251_PSM_ENTRY_RETRIES) {
+                       wl->psm_entry_retry++;
+                       ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
+               } else {
+                       wl1251_error("Power save entry failed, giving up");
+                       wl->psm_entry_retry = 0;
+               }
+               break;
+       case EVENT_ENTER_POWER_SAVE_SUCCESS:
+       case EVENT_EXIT_POWER_SAVE_FAIL:
+       case EVENT_EXIT_POWER_SAVE_SUCCESS:
+       default:
+               wl->psm_entry_retry = 0;
+               break;
+       }
+
+       return 0;
+}
+
 static void wl1251_event_mbox_dump(struct event_mailbox *mbox)
 {
        wl1251_debug(DEBUG_EVENT, "MBOX DUMP:");
@@ -80,7 +117,14 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
                }
        }
 
-       if (vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
+       if (vector & PS_REPORT_EVENT_ID) {
+               wl1251_debug(DEBUG_EVENT, "PS_REPORT_EVENT");
+               ret = wl1251_event_ps_report(wl, mbox);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (wl->vif && vector & SYNCHRONIZATION_TIMEOUT_EVENT_ID) {
                wl1251_debug(DEBUG_EVENT, "SYNCHRONIZATION_TIMEOUT_EVENT");
 
                /* indicate to the stack, that beacons have been lost */
index 30eb5d1..88570a5 100644 (file)
@@ -112,6 +112,13 @@ struct event_mailbox {
        u8 padding[19];
 } __packed;
 
+enum {
+       EVENT_ENTER_POWER_SAVE_FAIL = 0,
+       EVENT_ENTER_POWER_SAVE_SUCCESS,
+       EVENT_EXIT_POWER_SAVE_FAIL,
+       EVENT_EXIT_POWER_SAVE_SUCCESS,
+};
+
 int wl1251_event_unmask(struct wl1251 *wl);
 void wl1251_event_mbox_config(struct wl1251 *wl);
 int wl1251_event_handle(struct wl1251 *wl, u8 mbox);
index 89b43d3..1d799bf 100644 (file)
@@ -33,7 +33,7 @@ int wl1251_hw_init_hwenc_config(struct wl1251 *wl)
 {
        int ret;
 
-       ret = wl1251_acx_feature_cfg(wl);
+       ret = wl1251_acx_feature_cfg(wl, 0);
        if (ret < 0) {
                wl1251_warning("couldn't set feature config");
                return ret;
@@ -127,7 +127,7 @@ int wl1251_hw_init_phy_config(struct wl1251 *wl)
        if (ret < 0)
                return ret;
 
-       ret = wl1251_acx_group_address_tbl(wl);
+       ret = wl1251_acx_group_address_tbl(wl, true, NULL, 0);
        if (ret < 0)
                return ret;
 
@@ -394,8 +394,13 @@ int wl1251_hw_init(struct wl1251 *wl)
        if (ret < 0)
                goto out_free_data_path;
 
-       /* Enable data path */
-       ret = wl1251_cmd_data_path(wl, wl->channel, 1);
+       /* Enable rx data path */
+       ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
+       if (ret < 0)
+               goto out_free_data_path;
+
+       /* Enable tx data path */
+       ret = wl1251_cmd_data_path_tx(wl, wl->channel, 1);
        if (ret < 0)
                goto out_free_data_path;
 
index ee4ec4e..fa3909a 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/etherdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
+#include <linux/netdevice.h>
 
 #include "wl1251.h"
 #include "wl12xx_80211.h"
@@ -479,10 +480,13 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
        wl->next_tx_complete = 0;
        wl->elp = false;
        wl->station_mode = STATION_ACTIVE_MODE;
+       wl->psm_entry_retry = 0;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
        wl->rssi_thold = 0;
        wl->channel = WL1251_DEFAULT_CHANNEL;
+       wl->monitor_present = false;
+       wl->joined = false;
 
        wl1251_debugfs_reset(wl);
 
@@ -542,6 +546,7 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw,
        mutex_lock(&wl->mutex);
        wl1251_debug(DEBUG_MAC80211, "mac80211 remove interface");
        wl->vif = NULL;
+       memset(wl->bssid, 0, ETH_ALEN);
        mutex_unlock(&wl->mutex);
 }
 
@@ -566,6 +571,11 @@ static int wl1251_build_qos_null_data(struct wl1251 *wl)
                                       sizeof(template));
 }
 
+static bool wl1251_can_do_pm(struct ieee80211_conf *conf, struct wl1251 *wl)
+{
+       return (conf->flags & IEEE80211_CONF_PS) && !wl->monitor_present;
+}
+
 static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
 {
        struct wl1251 *wl = hw->priv;
@@ -575,8 +585,10 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
        channel = ieee80211_frequency_to_channel(
                        conf->chandef.chan->center_freq);
 
-       wl1251_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d",
+       wl1251_debug(DEBUG_MAC80211,
+                    "mac80211 config ch %d monitor %s psm %s power %d",
                     channel,
+                    conf->flags & IEEE80211_CONF_MONITOR ? "on" : "off",
                     conf->flags & IEEE80211_CONF_PS ? "on" : "off",
                     conf->power_level);
 
@@ -586,16 +598,44 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
        if (ret < 0)
                goto out;
 
+       if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
+               u32 mode;
+
+               if (conf->flags & IEEE80211_CONF_MONITOR) {
+                       wl->monitor_present = true;
+                       mode = DF_SNIFF_MODE_ENABLE | DF_ENCRYPTION_DISABLE;
+               } else {
+                       wl->monitor_present = false;
+                       mode = 0;
+               }
+
+               ret = wl1251_acx_feature_cfg(wl, mode);
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
        if (channel != wl->channel) {
                wl->channel = channel;
 
-               ret = wl1251_join(wl, wl->bss_type, wl->channel,
-                                 wl->beacon_int, wl->dtim_period);
+               /*
+                * Use ENABLE_RX command for channel switching when no
+                * interface is present (monitor mode only).
+                * This leaves the tx path disabled in firmware, whereas
+                * the usual JOIN command seems to transmit some frames
+                * at firmware level.
+                */
+               if (wl->vif == NULL) {
+                       wl->joined = false;
+                       ret = wl1251_cmd_data_path_rx(wl, wl->channel, 1);
+               } else {
+                       ret = wl1251_join(wl, wl->bss_type, wl->channel,
+                                         wl->beacon_int, wl->dtim_period);
+               }
                if (ret < 0)
                        goto out_sleep;
        }
 
-       if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) {
+       if (wl1251_can_do_pm(conf, wl) && !wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm enabled");
 
                wl->psm_requested = true;
@@ -611,8 +651,7 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                ret = wl1251_ps_set_mode(wl, STATION_POWER_SAVE_MODE);
                if (ret < 0)
                        goto out_sleep;
-       } else if (!(conf->flags & IEEE80211_CONF_PS) &&
-                  wl->psm_requested) {
+       } else if (!wl1251_can_do_pm(conf, wl) && wl->psm_requested) {
                wl1251_debug(DEBUG_PSM, "psm disabled");
 
                wl->psm_requested = false;
@@ -648,6 +687,16 @@ static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed)
                wl->power_level = conf->power_level;
        }
 
+       /*
+        * Tell stack that connection is lost because hw encryption isn't
+        * supported in monitor mode.
+        * This requires temporary enabling of the hw connection monitor flag
+        */
+       if ((changed & IEEE80211_CONF_CHANGE_MONITOR) && wl->vif) {
+               wl->hw->flags |= IEEE80211_HW_CONNECTION_MONITOR;
+               ieee80211_connection_loss(wl->vif);
+       }
+
 out_sleep:
        wl1251_ps_elp_sleep(wl);
 
@@ -657,6 +706,44 @@ out:
        return ret;
 }
 
+struct wl1251_filter_params {
+       bool enabled;
+       int mc_list_length;
+       u8 mc_list[ACX_MC_ADDRESS_GROUP_MAX][ETH_ALEN];
+};
+
+static u64 wl1251_op_prepare_multicast(struct ieee80211_hw *hw,
+                                      struct netdev_hw_addr_list *mc_list)
+{
+       struct wl1251_filter_params *fp;
+       struct netdev_hw_addr *ha;
+       struct wl1251 *wl = hw->priv;
+
+       if (unlikely(wl->state == WL1251_STATE_OFF))
+               return 0;
+
+       fp = kzalloc(sizeof(*fp), GFP_ATOMIC);
+       if (!fp) {
+               wl1251_error("Out of memory setting filters.");
+               return 0;
+       }
+
+       /* update multicast filtering parameters */
+       fp->mc_list_length = 0;
+       if (netdev_hw_addr_list_count(mc_list) > ACX_MC_ADDRESS_GROUP_MAX) {
+               fp->enabled = false;
+       } else {
+               fp->enabled = true;
+               netdev_hw_addr_list_for_each(ha, mc_list) {
+                       memcpy(fp->mc_list[fp->mc_list_length],
+                                       ha->addr, ETH_ALEN);
+                       fp->mc_list_length++;
+               }
+       }
+
+       return (u64)(unsigned long)fp;
+}
+
 #define WL1251_SUPPORTED_FILTERS (FIF_PROMISC_IN_BSS | \
                                  FIF_ALLMULTI | \
                                  FIF_FCSFAIL | \
@@ -667,8 +754,9 @@ out:
 
 static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
                                       unsigned int changed,
-                                      unsigned int *total,u64 multicast)
+                                      unsigned int *total, u64 multicast)
 {
+       struct wl1251_filter_params *fp = (void *)(unsigned long)multicast;
        struct wl1251 *wl = hw->priv;
        int ret;
 
@@ -677,9 +765,11 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
        *total &= WL1251_SUPPORTED_FILTERS;
        changed &= WL1251_SUPPORTED_FILTERS;
 
-       if (changed == 0)
+       if (changed == 0) {
                /* no filters which we support changed */
+               kfree(fp);
                return;
+       }
 
        mutex_lock(&wl->mutex);
 
@@ -716,6 +806,15 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
+       if (*total & FIF_ALLMULTI || *total & FIF_PROMISC_IN_BSS)
+               ret = wl1251_acx_group_address_tbl(wl, false, NULL, 0);
+       else if (fp)
+               ret = wl1251_acx_group_address_tbl(wl, fp->enabled,
+                                                  fp->mc_list,
+                                                  fp->mc_list_length);
+       if (ret < 0)
+               goto out;
+
        /* send filters to firmware */
        wl1251_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
 
@@ -723,6 +822,7 @@ static void wl1251_op_configure_filter(struct ieee80211_hw *hw,
 
 out:
        mutex_unlock(&wl->mutex);
+       kfree(fp);
 }
 
 /* HW encryption */
@@ -802,12 +902,12 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 
        mutex_lock(&wl->mutex);
 
-       ret = wl1251_ps_elp_wakeup(wl);
-       if (ret < 0)
-               goto out_unlock;
-
        switch (cmd) {
        case SET_KEY:
+               if (wl->monitor_present) {
+                       ret = -EOPNOTSUPP;
+                       goto out_unlock;
+               }
                wl_cmd->key_action = KEY_ADD_OR_REPLACE;
                break;
        case DISABLE_KEY:
@@ -818,6 +918,10 @@ static int wl1251_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                break;
        }
 
+       ret = wl1251_ps_elp_wakeup(wl);
+       if (ret < 0)
+               goto out_unlock;
+
        ret = wl1251_set_key_type(wl, wl_cmd, cmd, key, addr);
        if (ret < 0) {
                wl1251_error("Set KEY type failed");
@@ -930,6 +1034,7 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
        ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels,
                              req->n_channels, WL1251_SCAN_NUM_PROBES);
        if (ret < 0) {
+               wl1251_debug(DEBUG_SCAN, "scan failed %d", ret);
                wl->scanning = false;
                goto out_idle;
        }
@@ -977,6 +1082,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
 {
        struct wl1251 *wl = hw->priv;
        struct sk_buff *beacon, *skb;
+       bool enable;
        int ret;
 
        wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed");
@@ -1023,6 +1129,9 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
        }
 
        if (changed & BSS_CHANGED_ASSOC) {
+               /* Disable temporary enabled hw connection monitor flag */
+               wl->hw->flags &= ~IEEE80211_HW_CONNECTION_MONITOR;
+
                if (bss_conf->assoc) {
                        wl->beacon_int = bss_conf->beacon_int;
 
@@ -1075,6 +1184,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
                }
        }
 
+       if (changed & BSS_CHANGED_ARP_FILTER) {
+               __be32 addr = bss_conf->arp_addr_list[0];
+               WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
+
+               enable = bss_conf->arp_addr_cnt == 1 && bss_conf->assoc;
+               wl1251_acx_arp_ip_filter(wl, enable, addr);
+
+               if (ret < 0)
+                       goto out_sleep;
+       }
+
        if (changed & BSS_CHANGED_BEACON) {
                beacon = ieee80211_beacon_get(hw, vif);
                if (!beacon)
@@ -1245,6 +1365,7 @@ static const struct ieee80211_ops wl1251_ops = {
        .add_interface = wl1251_op_add_interface,
        .remove_interface = wl1251_op_remove_interface,
        .config = wl1251_op_config,
+       .prepare_multicast = wl1251_op_prepare_multicast,
        .configure_filter = wl1251_op_configure_filter,
        .tx = wl1251_op_tx,
        .set_key = wl1251_op_set_key,
@@ -1400,7 +1521,10 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
 
        INIT_DELAYED_WORK(&wl->elp_work, wl1251_elp_work);
        wl->channel = WL1251_DEFAULT_CHANNEL;
+       wl->monitor_present = false;
+       wl->joined = false;
        wl->scanning = false;
+       wl->bss_type = MAX_BSS_TYPE;
        wl->default_key = 0;
        wl->listen_int = 1;
        wl->rx_counter = 0;
@@ -1412,6 +1536,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
        wl->elp = false;
        wl->station_mode = STATION_ACTIVE_MODE;
        wl->psm_requested = false;
+       wl->psm_entry_retry = 0;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
        wl->rssi_thold = 0;
@@ -1477,3 +1602,4 @@ MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Kalle Valo <kvalo@adurom.com>");
 MODULE_FIRMWARE(WL1251_FW_NAME);
+MODULE_FIRMWARE(WL1251_NVS_NAME);
index 23289d4..123c4bb 100644 (file)
@@ -83,7 +83,7 @@ static void wl1251_rx_status(struct wl1251 *wl,
 
        status->flag |= RX_FLAG_MACTIME_START;
 
-       if (desc->flags & RX_DESC_ENCRYPTION_MASK) {
+       if (!wl->monitor_present && (desc->flags & RX_DESC_ENCRYPTION_MASK)) {
                status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED;
 
                if (likely(!(desc->flags & RX_DESC_DECRYPT_FAIL)))
index 28121c5..81de83c 100644 (file)
@@ -28,6 +28,7 @@
 #include "tx.h"
 #include "ps.h"
 #include "io.h"
+#include "event.h"
 
 static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count)
 {
@@ -89,8 +90,12 @@ static void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr,
        /* 802.11 packets */
        tx_hdr->control.packet_type = 0;
 
-       if (control->flags & IEEE80211_TX_CTL_NO_ACK)
+       /* Also disable retry and ACK policy for injected packets */
+       if ((control->flags & IEEE80211_TX_CTL_NO_ACK) ||
+           (control->flags & IEEE80211_TX_CTL_INJECTED)) {
+               tx_hdr->control.rate_policy = 1;
                tx_hdr->control.ack_policy = 1;
+       }
 
        tx_hdr->control.tx_complete = 1;
 
@@ -277,6 +282,26 @@ static void wl1251_tx_trigger(struct wl1251 *wl)
                TX_STATUS_DATA_OUT_COUNT_MASK;
 }
 
+static void enable_tx_for_packet_injection(struct wl1251 *wl)
+{
+       int ret;
+
+       ret = wl1251_cmd_join(wl, BSS_TYPE_STA_BSS, wl->channel,
+                             wl->beacon_int, wl->dtim_period);
+       if (ret < 0) {
+               wl1251_warning("join failed");
+               return;
+       }
+
+       ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100);
+       if (ret < 0) {
+               wl1251_warning("join timeout");
+               return;
+       }
+
+       wl->joined = true;
+}
+
 /* caller must hold wl->mutex */
 static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb)
 {
@@ -287,6 +312,9 @@ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb)
        info = IEEE80211_SKB_CB(skb);
 
        if (info->control.hw_key) {
+               if (unlikely(wl->monitor_present))
+                       return -EINVAL;
+
                idx = info->control.hw_key->hw_key_idx;
                if (unlikely(wl->default_key != idx)) {
                        ret = wl1251_acx_default_key(wl, idx);
@@ -295,6 +323,10 @@ static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb)
                }
        }
 
+       /* Enable tx path in monitor mode for packet injection */
+       if ((wl->vif == NULL) && !wl->joined)
+               enable_tx_for_packet_injection(wl);
+
        ret = wl1251_tx_path_status(wl);
        if (ret < 0)
                return ret;
@@ -394,6 +426,7 @@ static void wl1251_tx_packet_cb(struct wl1251 *wl,
        info = IEEE80211_SKB_CB(skb);
 
        if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
+           !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
            (result->status == TX_SUCCESS))
                info->flags |= IEEE80211_TX_STAT_ACK;
 
index 2c3bd1b..235617a 100644 (file)
@@ -93,6 +93,7 @@ enum {
        } while (0)
 
 #define WL1251_DEFAULT_RX_CONFIG (CFG_UNI_FILTER_EN |  \
+                                 CFG_MC_FILTER_EN |    \
                                  CFG_BSSID_FILTER_EN)
 
 #define WL1251_DEFAULT_RX_FILTER (CFG_RX_PRSP_EN |  \
@@ -303,6 +304,8 @@ struct wl1251 {
        u8 bss_type;
        u8 listen_int;
        int channel;
+       bool monitor_present;
+       bool joined;
 
        void *target_mem_map;
        struct acx_data_path_params_resp *data_path;
@@ -368,6 +371,9 @@ struct wl1251 {
        /* PSM mode requested */
        bool psm_requested;
 
+       /* retry counter for PSM entries */
+       u8 psm_entry_retry;
+
        u16 beacon_int;
        u8 dtim_period;
 
index 38d2089..1f5987d 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <linux/delay.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/kernel.h>
index c1fb206..fe20e1c 100644 (file)
@@ -58,5 +58,6 @@ config NFC_PORT100
 
 source "drivers/nfc/pn544/Kconfig"
 source "drivers/nfc/microread/Kconfig"
+source "drivers/nfc/nfcmrvl/Kconfig"
 
 endmenu
index c715fe8..56ab822 100644 (file)
@@ -9,5 +9,6 @@ obj-$(CONFIG_NFC_WILINK)        += nfcwilink.o
 obj-$(CONFIG_NFC_MEI_PHY)      += mei_phy.o
 obj-$(CONFIG_NFC_SIM)          += nfcsim.o
 obj-$(CONFIG_NFC_PORT100)      += port100.o
+obj-$(CONFIG_NFC_MRVL)         += nfcmrvl/
 
 ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG
index 1d78605..11c7cbd 100644 (file)
@@ -127,7 +127,7 @@ void nfc_mei_event_cb(struct mei_cl_device *device, u32 events, void *context)
 
                reply_size = mei_cl_recv(device, skb->data, MEI_NFC_MAX_READ);
                if (reply_size < MEI_NFC_HEADER_SIZE) {
-                       kfree(skb);
+                       kfree_skb(skb);
                        return;
                }
 
diff --git a/drivers/nfc/nfcmrvl/Kconfig b/drivers/nfc/nfcmrvl/Kconfig
new file mode 100644 (file)
index 0000000..5e18afd
--- /dev/null
@@ -0,0 +1,23 @@
+config NFC_MRVL
+       tristate "Marvell NFC driver support"
+       depends on NFC_NCI
+       help
+         The core driver to support Marvell NFC devices.
+
+         This driver is required if you want to support
+         Marvell NFC device 8897.
+
+         Say Y here to compile Marvell NFC driver into the kernel or
+         say M to compile it as module.
+
+config NFC_MRVL_USB
+       tristate "Marvell NFC-over-USB driver"
+       depends on NFC_MRVL && USB
+       help
+         Marvell NFC-over-USB driver.
+
+         This driver provides support for Marvell NFC-over-USB devices:
+          8897.
+
+         Say Y here to compile support for Marvell NFC-over-USB driver
+         into the kernel or say M to compile it as module.
diff --git a/drivers/nfc/nfcmrvl/Makefile b/drivers/nfc/nfcmrvl/Makefile
new file mode 100644 (file)
index 0000000..97a0de7
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for NFCMRVL NCI based NFC driver
+#
+
+nfcmrvl-y += main.o
+obj-$(CONFIG_NFC_MRVL) += nfcmrvl.o
+
+nfcmrvl_usb-y += usb.o
+obj-$(CONFIG_NFC_MRVL_USB) += nfcmrvl_usb.o
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
new file mode 100644 (file)
index 0000000..85e8bcf
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Marvell NFC driver: major functions
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ */
+
+#include <linux/module.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+#define VERSION "1.0"
+
+static int nfcmrvl_nci_open(struct nci_dev *ndev)
+{
+       struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+       int err;
+
+       if (test_and_set_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+               return 0;
+
+       err = priv->if_ops->nci_open(priv);
+
+       if (err)
+               clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags);
+
+       return err;
+}
+
+static int nfcmrvl_nci_close(struct nci_dev *ndev)
+{
+       struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+
+       if (!test_and_clear_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+               return 0;
+
+       priv->if_ops->nci_close(priv);
+
+       return 0;
+}
+
+static int nfcmrvl_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
+{
+       struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+
+       nfc_info(priv->dev, "send entry, len %d\n", skb->len);
+
+       skb->dev = (void *)ndev;
+
+       if (!test_bit(NFCMRVL_NCI_RUNNING, &priv->flags))
+               return -EBUSY;
+
+       return priv->if_ops->nci_send(priv, skb);
+}
+
+static int nfcmrvl_nci_setup(struct nci_dev *ndev)
+{
+       __u8 val;
+
+       val = NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED;
+       nci_set_config(ndev, NFCMRVL_NOT_ALLOWED_ID, 1, &val);
+       val = NFCMRVL_GPIO_PIN_NFC_ACTIVE;
+       nci_set_config(ndev, NFCMRVL_ACTIVE_ID, 1, &val);
+       val = NFCMRVL_EXT_COEX_ENABLE;
+       nci_set_config(ndev, NFCMRVL_EXT_COEX_ID, 1, &val);
+
+       return 0;
+}
+
+static struct nci_ops nfcmrvl_nci_ops = {
+       .open = nfcmrvl_nci_open,
+       .close = nfcmrvl_nci_close,
+       .send = nfcmrvl_nci_send,
+       .setup = nfcmrvl_nci_setup,
+};
+
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
+                                                struct nfcmrvl_if_ops *ops,
+                                                struct device *dev)
+{
+       struct nfcmrvl_private *priv;
+       int rc;
+       u32 protocols;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return ERR_PTR(-ENOMEM);
+
+       priv->drv_data = drv_data;
+       priv->if_ops = ops;
+       priv->dev = dev;
+
+       protocols = NFC_PROTO_JEWEL_MASK
+               | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK
+               | NFC_PROTO_ISO14443_MASK
+               | NFC_PROTO_ISO14443_B_MASK
+               | NFC_PROTO_NFC_DEP_MASK;
+
+       priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0);
+       if (!priv->ndev) {
+               nfc_err(dev, "nci_allocate_device failed");
+               rc = -ENOMEM;
+               goto error;
+       }
+
+       nci_set_drvdata(priv->ndev, priv);
+
+       rc = nci_register_device(priv->ndev);
+       if (rc) {
+               nfc_err(dev, "nci_register_device failed %d", rc);
+               nci_free_device(priv->ndev);
+               goto error;
+       }
+
+       nfc_info(dev, "registered with nci successfully\n");
+       return priv;
+
+error:
+       kfree(priv);
+       return ERR_PTR(rc);
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_register_dev);
+
+void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
+{
+       struct nci_dev *ndev = priv->ndev;
+
+       nci_unregister_device(ndev);
+       nci_free_device(ndev);
+       kfree(priv);
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_unregister_dev);
+
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count)
+{
+       struct sk_buff *skb;
+
+       skb = nci_skb_alloc(priv->ndev, count, GFP_ATOMIC);
+       if (!skb)
+               return -ENOMEM;
+
+       memcpy(skb_put(skb, count), data, count);
+       nci_recv_frame(priv->ndev, skb);
+
+       return count;
+}
+EXPORT_SYMBOL_GPL(nfcmrvl_nci_recv_frame);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/nfc/nfcmrvl/nfcmrvl.h b/drivers/nfc/nfcmrvl/nfcmrvl.h
new file mode 100644 (file)
index 0000000..54c4a95
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+ * Marvell NFC driver
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+/* Define private flags: */
+#define NFCMRVL_NCI_RUNNING                    1
+
+#define NFCMRVL_EXT_COEX_ID                    0xE0
+#define NFCMRVL_NOT_ALLOWED_ID                 0xE1
+#define NFCMRVL_ACTIVE_ID                      0xE2
+#define NFCMRVL_EXT_COEX_ENABLE                        1
+#define NFCMRVL_GPIO_PIN_NFC_NOT_ALLOWED       0xA
+#define NFCMRVL_GPIO_PIN_NFC_ACTIVE            0xB
+#define NFCMRVL_NCI_MAX_EVENT_SIZE             260
+
+struct nfcmrvl_private {
+       struct nci_dev *ndev;
+       unsigned long flags;
+       void *drv_data;
+       struct device *dev;
+       struct nfcmrvl_if_ops *if_ops;
+};
+
+struct nfcmrvl_if_ops {
+       int (*nci_open) (struct nfcmrvl_private *priv);
+       int (*nci_close) (struct nfcmrvl_private *priv);
+       int (*nci_send) (struct nfcmrvl_private *priv, struct sk_buff *skb);
+};
+
+void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv);
+int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, void *data, int count);
+struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data,
+                                                struct nfcmrvl_if_ops *ops,
+                                                struct device *dev);
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c
new file mode 100644 (file)
index 0000000..3221ca3
--- /dev/null
@@ -0,0 +1,459 @@
+/**
+ * Marvell NFC-over-USB driver: USB interface related functions
+ *
+ * Copyright (C) 2014, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License").  You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available on the worldwide web at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ * this warranty disclaimer.
+ **/
+
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/nfc.h>
+#include <net/nfc/nci.h>
+#include <net/nfc/nci_core.h>
+#include "nfcmrvl.h"
+
+#define VERSION "1.0"
+
+static struct usb_device_id nfcmrvl_table[] = {
+       { USB_DEVICE_INTERFACE_CLASS(0x1286, 0x2046, 0xff) },
+       { }     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, nfcmrvl_table);
+
+#define NFCMRVL_USB_BULK_RUNNING       1
+#define NFCMRVL_USB_SUSPENDING         2
+
+struct nfcmrvl_usb_drv_data {
+       struct usb_device *udev;
+       struct usb_interface *intf;
+       unsigned long flags;
+       struct work_struct waker;
+       struct usb_anchor tx_anchor;
+       struct usb_anchor bulk_anchor;
+       struct usb_anchor deferred;
+       int tx_in_flight;
+       /* protects tx_in_flight */
+       spinlock_t txlock;
+       struct usb_endpoint_descriptor *bulk_tx_ep;
+       struct usb_endpoint_descriptor *bulk_rx_ep;
+       int suspend_count;
+       struct nfcmrvl_private *priv;
+};
+
+static int nfcmrvl_inc_tx(struct nfcmrvl_usb_drv_data *drv_data)
+{
+       unsigned long flags;
+       int rv;
+
+       spin_lock_irqsave(&drv_data->txlock, flags);
+       rv = test_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+       if (!rv)
+               drv_data->tx_in_flight++;
+       spin_unlock_irqrestore(&drv_data->txlock, flags);
+
+       return rv;
+}
+
+static void nfcmrvl_bulk_complete(struct urb *urb)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = urb->context;
+       int err;
+
+       dev_dbg(&drv_data->udev->dev, "urb %p status %d count %d",
+               urb, urb->status, urb->actual_length);
+
+       if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
+               return;
+
+       if (!urb->status) {
+               if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer,
+                                          urb->actual_length) < 0)
+                       nfc_err(&drv_data->udev->dev, "corrupted Rx packet");
+       }
+
+       if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags))
+               return;
+
+       usb_anchor_urb(urb, &drv_data->bulk_anchor);
+       usb_mark_last_busy(drv_data->udev);
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err) {
+               /* -EPERM: urb is being killed;
+                * -ENODEV: device got disconnected
+                */
+               if (err != -EPERM && err != -ENODEV)
+                       nfc_err(&drv_data->udev->dev,
+                               "urb %p failed to resubmit (%d)", urb, -err);
+               usb_unanchor_urb(urb);
+       }
+}
+
+static int
+nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags)
+{
+       struct urb *urb;
+       unsigned char *buf;
+       unsigned int pipe;
+       int err, size = NFCMRVL_NCI_MAX_EVENT_SIZE;
+
+       if (!drv_data->bulk_rx_ep)
+               return -ENODEV;
+
+       urb = usb_alloc_urb(0, mem_flags);
+       if (!urb)
+               return -ENOMEM;
+
+       buf = kmalloc(size, mem_flags);
+       if (!buf) {
+               usb_free_urb(urb);
+               return -ENOMEM;
+       }
+
+       pipe = usb_rcvbulkpipe(drv_data->udev,
+                              drv_data->bulk_rx_ep->bEndpointAddress);
+
+       usb_fill_bulk_urb(urb, drv_data->udev, pipe, buf, size,
+                         nfcmrvl_bulk_complete, drv_data);
+
+       urb->transfer_flags |= URB_FREE_BUFFER;
+
+       usb_mark_last_busy(drv_data->udev);
+       usb_anchor_urb(urb, &drv_data->bulk_anchor);
+
+       err = usb_submit_urb(urb, mem_flags);
+       if (err) {
+               if (err != -EPERM && err != -ENODEV)
+                       nfc_err(&drv_data->udev->dev,
+                               "urb %p submission failed (%d)", urb, -err);
+               usb_unanchor_urb(urb);
+       }
+
+       usb_free_urb(urb);
+
+       return err;
+}
+
+static void nfcmrvl_tx_complete(struct urb *urb)
+{
+       struct sk_buff *skb = urb->context;
+       struct nci_dev *ndev = (struct nci_dev *)skb->dev;
+       struct nfcmrvl_private *priv = nci_get_drvdata(ndev);
+       struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+
+       nfc_info(priv->dev, "urb %p status %d count %d",
+                urb, urb->status, urb->actual_length);
+
+       spin_lock(&drv_data->txlock);
+       drv_data->tx_in_flight--;
+       spin_unlock(&drv_data->txlock);
+
+       kfree(urb->setup_packet);
+       kfree_skb(skb);
+}
+
+static int nfcmrvl_usb_nci_open(struct nfcmrvl_private *priv)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+       int err;
+
+       err = usb_autopm_get_interface(drv_data->intf);
+       if (err)
+               return err;
+
+       drv_data->intf->needs_remote_wakeup = 1;
+
+       err = nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
+       if (err)
+               goto failed;
+
+       set_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+       nfcmrvl_submit_bulk_urb(drv_data, GFP_KERNEL);
+
+       usb_autopm_put_interface(drv_data->intf);
+       return 0;
+
+failed:
+       usb_autopm_put_interface(drv_data->intf);
+       return err;
+}
+
+static void nfcmrvl_usb_stop_traffic(struct nfcmrvl_usb_drv_data *drv_data)
+{
+       usb_kill_anchored_urbs(&drv_data->bulk_anchor);
+}
+
+static int nfcmrvl_usb_nci_close(struct nfcmrvl_private *priv)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+       int err;
+
+       cancel_work_sync(&drv_data->waker);
+
+       clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+
+       nfcmrvl_usb_stop_traffic(drv_data);
+       usb_kill_anchored_urbs(&drv_data->tx_anchor);
+       err = usb_autopm_get_interface(drv_data->intf);
+       if (err)
+               goto failed;
+
+       drv_data->intf->needs_remote_wakeup = 0;
+       usb_autopm_put_interface(drv_data->intf);
+
+failed:
+       usb_scuttle_anchored_urbs(&drv_data->deferred);
+       return 0;
+}
+
+static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv,
+                               struct sk_buff *skb)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data;
+       struct urb *urb;
+       unsigned int pipe;
+       int err;
+
+       if (!drv_data->bulk_tx_ep)
+               return -ENODEV;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb)
+               return -ENOMEM;
+
+       pipe = usb_sndbulkpipe(drv_data->udev,
+                               drv_data->bulk_tx_ep->bEndpointAddress);
+
+       usb_fill_bulk_urb(urb, drv_data->udev, pipe, skb->data, skb->len,
+                         nfcmrvl_tx_complete, skb);
+
+       err = nfcmrvl_inc_tx(drv_data);
+       if (err) {
+               usb_anchor_urb(urb, &drv_data->deferred);
+               schedule_work(&drv_data->waker);
+               err = 0;
+               goto done;
+       }
+
+       usb_anchor_urb(urb, &drv_data->tx_anchor);
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err) {
+               if (err != -EPERM && err != -ENODEV)
+                       nfc_err(&drv_data->udev->dev,
+                               "urb %p submission failed (%d)", urb, -err);
+               kfree(urb->setup_packet);
+               usb_unanchor_urb(urb);
+       } else {
+               usb_mark_last_busy(drv_data->udev);
+       }
+
+done:
+       usb_free_urb(urb);
+       return err;
+}
+
+static struct nfcmrvl_if_ops usb_ops = {
+       .nci_open = nfcmrvl_usb_nci_open,
+       .nci_close = nfcmrvl_usb_nci_close,
+       .nci_send = nfcmrvl_usb_nci_send,
+};
+
+static void nfcmrvl_waker(struct work_struct *work)
+{
+       struct nfcmrvl_usb_drv_data *drv_data =
+                       container_of(work, struct nfcmrvl_usb_drv_data, waker);
+       int err;
+
+       err = usb_autopm_get_interface(drv_data->intf);
+       if (err)
+               return;
+
+       usb_autopm_put_interface(drv_data->intf);
+}
+
+static int nfcmrvl_probe(struct usb_interface *intf,
+                        const struct usb_device_id *id)
+{
+       struct usb_endpoint_descriptor *ep_desc;
+       struct nfcmrvl_usb_drv_data *drv_data;
+       struct nfcmrvl_private *priv;
+       int i;
+       struct usb_device *udev = interface_to_usbdev(intf);
+
+       nfc_info(&udev->dev, "intf %p id %p", intf, id);
+
+       drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL);
+       if (!drv_data)
+               return -ENOMEM;
+
+       for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+               ep_desc = &intf->cur_altsetting->endpoint[i].desc;
+
+               if (!drv_data->bulk_tx_ep &&
+                   usb_endpoint_is_bulk_out(ep_desc)) {
+                       drv_data->bulk_tx_ep = ep_desc;
+                       continue;
+               }
+
+               if (!drv_data->bulk_rx_ep &&
+                   usb_endpoint_is_bulk_in(ep_desc)) {
+                       drv_data->bulk_rx_ep = ep_desc;
+                       continue;
+               }
+       }
+
+       if (!drv_data->bulk_tx_ep || !drv_data->bulk_rx_ep)
+               return -ENODEV;
+
+       drv_data->udev = udev;
+       drv_data->intf = intf;
+
+       INIT_WORK(&drv_data->waker, nfcmrvl_waker);
+       spin_lock_init(&drv_data->txlock);
+
+       init_usb_anchor(&drv_data->tx_anchor);
+       init_usb_anchor(&drv_data->bulk_anchor);
+       init_usb_anchor(&drv_data->deferred);
+
+       priv = nfcmrvl_nci_register_dev(drv_data, &usb_ops,
+                                       &drv_data->udev->dev);
+       if (IS_ERR(priv))
+               return PTR_ERR(priv);
+
+       drv_data->priv = priv;
+       priv->dev = &drv_data->udev->dev;
+
+       usb_set_intfdata(intf, drv_data);
+
+       return 0;
+}
+
+static void nfcmrvl_disconnect(struct usb_interface *intf)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+
+       if (!drv_data)
+               return;
+
+       nfc_info(&drv_data->udev->dev, "intf %p", intf);
+
+       nfcmrvl_nci_unregister_dev(drv_data->priv);
+
+       usb_set_intfdata(drv_data->intf, NULL);
+}
+
+#ifdef CONFIG_PM
+static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+
+       nfc_info(&drv_data->udev->dev, "intf %p", intf);
+
+       if (drv_data->suspend_count++)
+               return 0;
+
+       spin_lock_irq(&drv_data->txlock);
+       if (!(PMSG_IS_AUTO(message) && drv_data->tx_in_flight)) {
+               set_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+               spin_unlock_irq(&drv_data->txlock);
+       } else {
+               spin_unlock_irq(&drv_data->txlock);
+               drv_data->suspend_count--;
+               return -EBUSY;
+       }
+
+       nfcmrvl_usb_stop_traffic(drv_data);
+       usb_kill_anchored_urbs(&drv_data->tx_anchor);
+
+       return 0;
+}
+
+static void nfcmrvl_play_deferred(struct nfcmrvl_usb_drv_data *drv_data)
+{
+       struct urb *urb;
+       int err;
+
+       while ((urb = usb_get_from_anchor(&drv_data->deferred))) {
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err)
+                       break;
+
+               drv_data->tx_in_flight++;
+       }
+       usb_scuttle_anchored_urbs(&drv_data->deferred);
+}
+
+static int nfcmrvl_resume(struct usb_interface *intf)
+{
+       struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf);
+       int err = 0;
+
+       nfc_info(&drv_data->udev->dev, "intf %p", intf);
+
+       if (--drv_data->suspend_count)
+               return 0;
+
+       if (!test_bit(NFCMRVL_NCI_RUNNING, &drv_data->flags))
+               goto done;
+
+       if (test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) {
+               err = nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
+               if (err) {
+                       clear_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags);
+                       goto failed;
+               }
+
+               nfcmrvl_submit_bulk_urb(drv_data, GFP_NOIO);
+       }
+
+       spin_lock_irq(&drv_data->txlock);
+       nfcmrvl_play_deferred(drv_data);
+       clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+       spin_unlock_irq(&drv_data->txlock);
+
+       return 0;
+
+failed:
+       usb_scuttle_anchored_urbs(&drv_data->deferred);
+done:
+       spin_lock_irq(&drv_data->txlock);
+       clear_bit(NFCMRVL_USB_SUSPENDING, &drv_data->flags);
+       spin_unlock_irq(&drv_data->txlock);
+
+       return err;
+}
+#endif
+
+static struct usb_driver nfcmrvl_usb_driver = {
+       .name           = "nfcmrvl",
+       .probe          = nfcmrvl_probe,
+       .disconnect     = nfcmrvl_disconnect,
+#ifdef CONFIG_PM
+       .suspend        = nfcmrvl_suspend,
+       .resume         = nfcmrvl_resume,
+       .reset_resume   = nfcmrvl_resume,
+#endif
+       .id_table       = nfcmrvl_table,
+       .supports_autosuspend = 1,
+       .disable_hub_initiated_lpm = 1,
+       .soft_unbind = 1,
+};
+module_usb_driver(nfcmrvl_usb_driver);
+
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_DESCRIPTION("Marvell NFC-over-USB driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL v2");
index 3df19e6..cf1a87b 100644 (file)
@@ -521,6 +521,9 @@ static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev)
        if (frame->ccid.type != 0x83)
                return false;
 
+       if (!frame->ccid.datalen)
+               return false;
+
        if (frame->data[frame->ccid.datalen - 2] == 0x63)
                return false;
 
index 51e21a8..3df4a10 100644 (file)
@@ -195,42 +195,42 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev)
 
                {{0x9e, 0xaa}, 0x01},
 
-               {{0x9b, 0xd1}, 0x0d},
-               {{0x9b, 0xd2}, 0x24},
-               {{0x9b, 0xd3}, 0x0a},
-               {{0x9b, 0xd4}, 0x22},
-               {{0x9b, 0xd5}, 0x08},
-               {{0x9b, 0xd6}, 0x1e},
-               {{0x9b, 0xdd}, 0x1c},
-
-               {{0x9b, 0x84}, 0x13},
-               {{0x99, 0x81}, 0x7f},
-               {{0x99, 0x31}, 0x70},
+               {{0x9b, 0xd1}, 0x17},
+               {{0x9b, 0xd2}, 0x58},
+               {{0x9b, 0xd3}, 0x10},
+               {{0x9b, 0xd4}, 0x47},
+               {{0x9b, 0xd5}, 0x0c},
+               {{0x9b, 0xd6}, 0x37},
+               {{0x9b, 0xdd}, 0x33},
+
+               {{0x9b, 0x84}, 0x00},
+               {{0x99, 0x81}, 0x79},
+               {{0x99, 0x31}, 0x79},
 
                {{0x98, 0x00}, 0x3f},
 
-               {{0x9f, 0x09}, 0x00},
+               {{0x9f, 0x09}, 0x02},
 
                {{0x9f, 0x0a}, 0x05},
 
                {{0x9e, 0xd1}, 0xa1},
-               {{0x99, 0x23}, 0x00},
-
-               {{0x9e, 0x74}, 0x80},
+               {{0x99, 0x23}, 0x01},
 
+               {{0x9e, 0x74}, 0x00},
+               {{0x9e, 0x90}, 0x00},
                {{0x9f, 0x28}, 0x10},
 
-               {{0x9f, 0x35}, 0x14},
+               {{0x9f, 0x35}, 0x04},
 
-               {{0x9f, 0x36}, 0x60},
+               {{0x9f, 0x36}, 0x11},
 
                {{0x9c, 0x31}, 0x00},
 
-               {{0x9c, 0x32}, 0xc8},
+               {{0x9c, 0x32}, 0x00},
 
-               {{0x9c, 0x19}, 0x40},
+               {{0x9c, 0x19}, 0x0a},
 
-               {{0x9c, 0x1a}, 0x40},
+               {{0x9c, 0x1a}, 0x0a},
 
                {{0x9c, 0x0c}, 0x00},
 
@@ -240,13 +240,13 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev)
 
                {{0x9c, 0x13}, 0x00},
 
-               {{0x98, 0xa2}, 0x0e},
+               {{0x98, 0xa2}, 0x09},
 
-               {{0x98, 0x93}, 0x40},
+               {{0x98, 0x93}, 0x00},
 
-               {{0x98, 0x7d}, 0x02},
+               {{0x98, 0x7d}, 0x08},
                {{0x98, 0x7e}, 0x00},
-               {{0x9f, 0xc8}, 0x01},
+               {{0x9f, 0xc8}, 0x00},
        };
        struct hw_config *p = hw_config;
        int count = ARRAY_SIZE(hw_config);
index 8a0571e..a8555f8 100644 (file)
@@ -1509,6 +1509,7 @@ static void port100_disconnect(struct usb_interface *interface)
 
        usb_free_urb(dev->in_urb);
        usb_free_urb(dev->out_urb);
+       usb_put_dev(dev->udev);
 
        kfree(dev->cmd);
 
index 50328de..937fc31 100644 (file)
@@ -37,7 +37,7 @@ static const struct ssb_sflash_tbl_e ssb_sflash_st_tbl[] = {
        { "M25P32", 0x15, 0x10000, 64, },
        { "M25P64", 0x16, 0x10000, 128, },
        { "M25FL128", 0x17, 0x10000, 256, },
-       { 0 },
+       { NULL },
 };
 
 static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
@@ -55,7 +55,7 @@ static const struct ssb_sflash_tbl_e ssb_sflash_sst_tbl[] = {
        { "SST25VF016", 0x41, 0x1000, 512, },
        { "SST25VF032", 0x4a, 0x1000, 1024, },
        { "SST25VF064", 0x4b, 0x1000, 2048, },
-       { 0 },
+       { NULL },
 };
 
 static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
@@ -66,7 +66,7 @@ static const struct ssb_sflash_tbl_e ssb_sflash_at_tbl[] = {
        { "AT45DB161", 0x2c, 512, 4096, },
        { "AT45DB321", 0x34, 512, 8192, },
        { "AT45DB642", 0x3c, 1024, 8192, },
-       { 0 },
+       { NULL },
 };
 
 static void ssb_sflash_cmd(struct ssb_chipcommon *cc, u32 opcode)
index 9f03fee..d883662 100644 (file)
 /*
  * Vendors and devices.  Sort key: vendor first, device next.
  */
+#define SDIO_VENDOR_ID_BROADCOM                        0x02d0
+#define SDIO_DEVICE_ID_BROADCOM_43143          43143
+#define SDIO_DEVICE_ID_BROADCOM_43241          0x4324
+#define SDIO_DEVICE_ID_BROADCOM_4329           0x4329
+#define SDIO_DEVICE_ID_BROADCOM_4330           0x4330
+#define SDIO_DEVICE_ID_BROADCOM_4334           0x4334
+#define SDIO_DEVICE_ID_BROADCOM_4335_4339      0x4335
+#define SDIO_DEVICE_ID_BROADCOM_43362          43362
+
 #define SDIO_VENDOR_ID_INTEL                   0x0089
 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX     0x1402
 #define SDIO_DEVICE_ID_INTEL_IWMC3200WIFI      0x1403
index cc2da73..66c1cd8 100644 (file)
@@ -83,7 +83,8 @@
 enum {
        HCI_QUIRK_RESET_ON_CLOSE,
        HCI_QUIRK_RAW_DEVICE,
-       HCI_QUIRK_FIXUP_BUFFER_SIZE
+       HCI_QUIRK_FIXUP_BUFFER_SIZE,
+       HCI_QUIRK_BROKEN_STORED_LINK_KEY,
 };
 
 /* HCI device flags */
@@ -131,6 +132,7 @@ enum {
        HCI_PERIODIC_INQ,
        HCI_FAST_CONNECTABLE,
        HCI_BREDR_ENABLED,
+       HCI_6LOWPAN_ENABLED,
 };
 
 /* A mask for the flags that are supposed to remain when a reset happens
index b796161..f2f0cf5 100644 (file)
@@ -448,6 +448,7 @@ enum {
        HCI_CONN_SSP_ENABLED,
        HCI_CONN_POWER_SAVE,
        HCI_CONN_REMOTE_OOB,
+       HCI_CONN_6LOWPAN,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
index e149e99..dbc4a89 100644 (file)
@@ -136,6 +136,7 @@ struct l2cap_conninfo {
 #define L2CAP_FC_L2CAP         0x02
 #define L2CAP_FC_CONNLESS      0x04
 #define L2CAP_FC_A2MP          0x08
+#define L2CAP_FC_6LOWPAN        0x3e /* reserved and temporary value */
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR                 0xC000
index 36acecd..81af21e 100644 (file)
@@ -122,6 +122,16 @@ typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev,
  *     switch_rf to turn the radio on. A call to in|tg_configure_hw must turn
  *     the device radio on.
  * @abort_cmd: Discard the last sent command.
+ *
+ * Notes: Asynchronous functions have a timeout parameter. It is the driver
+ *     responsibility to call the digital stack back through the
+ *     nfc_digital_cmd_complete_t callback when no RF respsonse has been
+ *     received within the specified time (in milliseconds). In that case the
+ *     driver must set the resp sk_buff to ERR_PTR(-ETIMEDOUT).
+ *     Since the digital stack serializes commands to be sent, it's mandatory
+ *     for the driver to handle the timeout correctly. Otherwise the stack
+ *     would not be able to send new commands, waiting for the reply of the
+ *     current one.
  */
 struct nfc_digital_ops {
        int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type,
index 6126f1f..2b93b77 100644 (file)
@@ -68,6 +68,7 @@ struct nci_ops {
        int (*open)(struct nci_dev *ndev);
        int (*close)(struct nci_dev *ndev);
        int (*send)(struct nci_dev *ndev, struct sk_buff *skb);
+       int (*setup)(struct nci_dev *ndev);
 };
 
 #define NCI_MAX_SUPPORTED_RF_INTERFACES                4
@@ -154,6 +155,7 @@ void nci_free_device(struct nci_dev *ndev);
 int nci_register_device(struct nci_dev *ndev);
 void nci_unregister_device(struct nci_dev *ndev);
 int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb);
+int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val);
 
 static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev,
                                            unsigned int len,
index d7fea34..4d024d7 100644 (file)
@@ -94,6 +94,7 @@
 #define ARPHRD_CAIF    822             /* CAIF media type              */
 #define ARPHRD_IP6GRE  823             /* GRE over IPv6                */
 #define ARPHRD_NETLINK 824             /* Netlink header               */
+#define ARPHRD_6LOWPAN 825             /* IPv6 over LoWPAN             */
 
 #define ARPHRD_VOID      0xFFFF        /* Void type, nothing is known */
 #define ARPHRD_NONE      0xFFFE        /* zero header length */
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
new file mode 100644 (file)
index 0000000..adb3ea0
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+   Copyright (c) 2013 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 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.
+*/
+
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <net/af_ieee802154.h> /* to get the address type */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "6lowpan.h"
+
+#include "../ieee802154/6lowpan.h" /* for the compression support */
+
+#define IFACE_NAME_TEMPLATE "bt%d"
+#define EUI64_ADDR_LEN 8
+
+struct skb_cb {
+       struct in6_addr addr;
+       struct l2cap_conn *conn;
+};
+#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
+
+/* The devices list contains those devices that we are acting
+ * as a proxy. The BT 6LoWPAN device is a virtual device that
+ * connects to the Bluetooth LE device. The real connection to
+ * BT device is done via l2cap layer. There exists one
+ * virtual device / one BT 6LoWPAN network (=hciX device).
+ * The list contains struct lowpan_dev elements.
+ */
+static LIST_HEAD(bt_6lowpan_devices);
+static DEFINE_RWLOCK(devices_lock);
+
+struct lowpan_peer {
+       struct list_head list;
+       struct l2cap_conn *conn;
+
+       /* peer addresses in various formats */
+       unsigned char eui64_addr[EUI64_ADDR_LEN];
+       struct in6_addr peer_addr;
+};
+
+struct lowpan_dev {
+       struct list_head list;
+
+       struct hci_dev *hdev;
+       struct net_device *netdev;
+       struct list_head peers;
+       atomic_t peer_count; /* number of items in peers list */
+
+       struct work_struct delete_netdev;
+       struct delayed_work notify_peers;
+};
+
+static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
+{
+       return netdev_priv(netdev);
+}
+
+static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
+{
+       list_add(&peer->list, &dev->peers);
+       atomic_inc(&dev->peer_count);
+}
+
+static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
+{
+       list_del(&peer->list);
+
+       if (atomic_dec_and_test(&dev->peer_count)) {
+               BT_DBG("last peer");
+               return true;
+       }
+
+       return false;
+}
+
+static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
+                                                bdaddr_t *ba, __u8 type)
+{
+       struct lowpan_peer *peer, *tmp;
+
+       BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
+              ba, type);
+
+       list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+               BT_DBG("addr %pMR type %d",
+                      &peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+
+               if (bacmp(&peer->conn->hcon->dst, ba))
+                       continue;
+
+               if (type == peer->conn->hcon->dst_type)
+                       return peer;
+       }
+
+       return NULL;
+}
+
+static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
+                                                  struct l2cap_conn *conn)
+{
+       struct lowpan_peer *peer, *tmp;
+
+       list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+               if (peer->conn == conn)
+                       return peer;
+       }
+
+       return NULL;
+}
+
+static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
+{
+       struct lowpan_dev *entry, *tmp;
+       struct lowpan_peer *peer = NULL;
+       unsigned long flags;
+
+       read_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+               peer = peer_lookup_conn(entry, conn);
+               if (peer)
+                       break;
+       }
+
+       read_unlock_irqrestore(&devices_lock, flags);
+
+       return peer;
+}
+
+static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
+{
+       struct lowpan_dev *entry, *tmp;
+       struct lowpan_dev *dev = NULL;
+       unsigned long flags;
+
+       read_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+               if (conn->hcon->hdev == entry->hdev) {
+                       dev = entry;
+                       break;
+               }
+       }
+
+       read_unlock_irqrestore(&devices_lock, flags);
+
+       return dev;
+}
+
+static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
+{
+       struct sk_buff *skb_cp;
+       int ret;
+
+       skb_cp = skb_copy(skb, GFP_ATOMIC);
+       if (!skb_cp)
+               return -ENOMEM;
+
+       ret = netif_rx(skb_cp);
+
+       BT_DBG("receive skb %d", ret);
+       if (ret < 0)
+               return NET_RX_DROP;
+
+       return ret;
+}
+
+static int process_data(struct sk_buff *skb, struct net_device *netdev,
+                       struct l2cap_conn *conn)
+{
+       const u8 *saddr, *daddr;
+       u8 iphc0, iphc1;
+       struct lowpan_dev *dev;
+       struct lowpan_peer *peer;
+       unsigned long flags;
+
+       dev = lowpan_dev(netdev);
+
+       read_lock_irqsave(&devices_lock, flags);
+       peer = peer_lookup_conn(dev, conn);
+       read_unlock_irqrestore(&devices_lock, flags);
+       if (!peer)
+               goto drop;
+
+       saddr = peer->eui64_addr;
+       daddr = dev->netdev->dev_addr;
+
+       /* at least two bytes will be used for the encoding */
+       if (skb->len < 2)
+               goto drop;
+
+       if (lowpan_fetch_skb_u8(skb, &iphc0))
+               goto drop;
+
+       if (lowpan_fetch_skb_u8(skb, &iphc1))
+               goto drop;
+
+       return lowpan_process_data(skb, netdev,
+                                  saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+                                  daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+                                  iphc0, iphc1, give_skb_to_upper);
+
+drop:
+       kfree_skb(skb);
+       return -EINVAL;
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
+                   struct l2cap_conn *conn)
+{
+       struct sk_buff *local_skb;
+       int ret;
+
+       if (!netif_running(dev))
+               goto drop;
+
+       if (dev->type != ARPHRD_6LOWPAN)
+               goto drop;
+
+       /* check that it's our buffer */
+       if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
+               /* Copy the packet so that the IPv6 header is
+                * properly aligned.
+                */
+               local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
+                                           skb_tailroom(skb), GFP_ATOMIC);
+               if (!local_skb)
+                       goto drop;
+
+               local_skb->protocol = htons(ETH_P_IPV6);
+               local_skb->pkt_type = PACKET_HOST;
+
+               skb_reset_network_header(local_skb);
+               skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
+
+               if (give_skb_to_upper(local_skb, dev) != NET_RX_SUCCESS) {
+                       kfree_skb(local_skb);
+                       goto drop;
+               }
+
+               dev->stats.rx_bytes += skb->len;
+               dev->stats.rx_packets++;
+
+               kfree_skb(local_skb);
+               kfree_skb(skb);
+       } else {
+               switch (skb->data[0] & 0xe0) {
+               case LOWPAN_DISPATCH_IPHC:      /* ipv6 datagram */
+                       local_skb = skb_clone(skb, GFP_ATOMIC);
+                       if (!local_skb)
+                               goto drop;
+
+                       ret = process_data(local_skb, dev, conn);
+                       if (ret != NET_RX_SUCCESS)
+                               goto drop;
+
+                       dev->stats.rx_bytes += skb->len;
+                       dev->stats.rx_packets++;
+
+                       kfree_skb(skb);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return NET_RX_SUCCESS;
+
+drop:
+       kfree_skb(skb);
+       return NET_RX_DROP;
+}
+
+/* Packet from BT LE device */
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+       struct lowpan_dev *dev;
+       struct lowpan_peer *peer;
+       int err;
+
+       peer = lookup_peer(conn);
+       if (!peer)
+               return -ENOENT;
+
+       dev = lookup_dev(conn);
+       if (!dev || !dev->netdev)
+               return -ENOENT;
+
+       err = recv_pkt(skb, dev->netdev, conn);
+       BT_DBG("recv pkt %d", err);
+
+       return err;
+}
+
+static inline int skbuff_copy(void *msg, int len, int count, int mtu,
+                             struct sk_buff *skb, struct net_device *dev)
+{
+       struct sk_buff **frag;
+       int sent = 0;
+
+       memcpy(skb_put(skb, count), msg, count);
+
+       sent += count;
+       msg  += count;
+       len  -= count;
+
+       dev->stats.tx_bytes += count;
+       dev->stats.tx_packets++;
+
+       raw_dump_table(__func__, "Sending", skb->data, skb->len);
+
+       /* Continuation fragments (no L2CAP header) */
+       frag = &skb_shinfo(skb)->frag_list;
+       while (len > 0) {
+               struct sk_buff *tmp;
+
+               count = min_t(unsigned int, mtu, len);
+
+               tmp = bt_skb_alloc(count, GFP_ATOMIC);
+               if (!tmp)
+                       return -ENOMEM;
+
+               *frag = tmp;
+
+               memcpy(skb_put(*frag, count), msg, count);
+
+               raw_dump_table(__func__, "Sending fragment",
+                              (*frag)->data, count);
+
+               (*frag)->priority = skb->priority;
+
+               sent += count;
+               msg  += count;
+               len  -= count;
+
+               skb->len += (*frag)->len;
+               skb->data_len += (*frag)->len;
+
+               frag = &(*frag)->next;
+
+               dev->stats.tx_bytes += count;
+               dev->stats.tx_packets++;
+       }
+
+       return sent;
+}
+
+static struct sk_buff *create_pdu(struct l2cap_conn *conn, void *msg,
+                                 size_t len, u32 priority,
+                                 struct net_device *dev)
+{
+       struct sk_buff *skb;
+       int err, count;
+       struct l2cap_hdr *lh;
+
+       /* FIXME: This mtu check should be not needed and atm is only used for
+        * testing purposes
+        */
+       if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
+               conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE;
+
+       count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
+
+       BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count);
+
+       skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_ATOMIC);
+       if (!skb)
+               return ERR_PTR(-ENOMEM);
+
+       skb->priority = priority;
+
+       lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE);
+       lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN);
+       lh->len = cpu_to_le16(len);
+
+       err = skbuff_copy(msg, len, count, conn->mtu, skb, dev);
+       if (unlikely(err < 0)) {
+               kfree_skb(skb);
+               BT_DBG("skbuff copy %d failed", err);
+               return ERR_PTR(err);
+       }
+
+       return skb;
+}
+
+static int conn_send(struct l2cap_conn *conn,
+                    void *msg, size_t len, u32 priority,
+                    struct net_device *dev)
+{
+       struct sk_buff *skb;
+
+       skb = create_pdu(conn, msg, len, priority, dev);
+       if (IS_ERR(skb))
+               return -EINVAL;
+
+       BT_DBG("conn %p skb %p len %d priority %u", conn, skb, skb->len,
+              skb->priority);
+
+       hci_send_acl(conn->hchan, skb, ACL_START);
+
+       return 0;
+}
+
+static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
+                           bdaddr_t *addr, u8 *addr_type)
+{
+       u8 *eui64;
+
+       eui64 = ip6_daddr->s6_addr + 8;
+
+       addr->b[0] = eui64[7];
+       addr->b[1] = eui64[6];
+       addr->b[2] = eui64[5];
+       addr->b[3] = eui64[2];
+       addr->b[4] = eui64[1];
+       addr->b[5] = eui64[0];
+
+       addr->b[5] ^= 2;
+
+       /* Set universal/local bit to 0 */
+       if (addr->b[5] & 1) {
+               addr->b[5] &= ~1;
+               *addr_type = ADDR_LE_DEV_PUBLIC;
+       } else {
+               *addr_type = ADDR_LE_DEV_RANDOM;
+       }
+}
+
+static int header_create(struct sk_buff *skb, struct net_device *netdev,
+                        unsigned short type, const void *_daddr,
+                        const void *_saddr, unsigned int len)
+{
+       struct ipv6hdr *hdr;
+       struct lowpan_dev *dev;
+       struct lowpan_peer *peer;
+       bdaddr_t addr, *any = BDADDR_ANY;
+       u8 *saddr, *daddr = any->b;
+       u8 addr_type;
+
+       if (type != ETH_P_IPV6)
+               return -EINVAL;
+
+       hdr = ipv6_hdr(skb);
+
+       dev = lowpan_dev(netdev);
+
+       if (ipv6_addr_is_multicast(&hdr->daddr)) {
+               memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+                      sizeof(struct in6_addr));
+               lowpan_cb(skb)->conn = NULL;
+       } else {
+               unsigned long flags;
+
+               /* Get destination BT device from skb.
+                * If there is no such peer then discard the packet.
+                */
+               get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+
+               BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+
+               read_lock_irqsave(&devices_lock, flags);
+               peer = peer_lookup_ba(dev, &addr, addr_type);
+               read_unlock_irqrestore(&devices_lock, flags);
+
+               if (!peer) {
+                       BT_DBG("no such peer %pMR found", &addr);
+                       return -ENOENT;
+               }
+
+               daddr = peer->eui64_addr;
+
+               memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+                      sizeof(struct in6_addr));
+               lowpan_cb(skb)->conn = peer->conn;
+       }
+
+       saddr = dev->netdev->dev_addr;
+
+       return lowpan_header_compress(skb, netdev, type, daddr, saddr, len);
+}
+
+/* Packet to BT LE device */
+static int send_pkt(struct l2cap_conn *conn, const void *saddr,
+                   const void *daddr, struct sk_buff *skb,
+                   struct net_device *netdev)
+{
+       raw_dump_table(__func__, "raw skb data dump before fragmentation",
+                      skb->data, skb->len);
+
+       return conn_send(conn, skb->data, skb->len, 0, netdev);
+}
+
+static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct sk_buff *local_skb;
+       struct lowpan_dev *entry, *tmp;
+       unsigned long flags;
+
+       read_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+               struct lowpan_peer *pentry, *ptmp;
+               struct lowpan_dev *dev;
+
+               if (entry->netdev != netdev)
+                       continue;
+
+               dev = lowpan_dev(entry->netdev);
+
+               list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
+                       local_skb = skb_clone(skb, GFP_ATOMIC);
+
+                       send_pkt(pentry->conn, netdev->dev_addr,
+                                pentry->eui64_addr, local_skb, netdev);
+
+                       kfree_skb(local_skb);
+               }
+       }
+
+       read_unlock_irqrestore(&devices_lock, flags);
+}
+
+static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       int err = 0;
+       unsigned char *eui64_addr;
+       struct lowpan_dev *dev;
+       struct lowpan_peer *peer;
+       bdaddr_t addr;
+       u8 addr_type;
+
+       if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) {
+               /* We need to send the packet to every device
+                * behind this interface.
+                */
+               send_mcast_pkt(skb, netdev);
+       } else {
+               unsigned long flags;
+
+               get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+               eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
+               dev = lowpan_dev(netdev);
+
+               read_lock_irqsave(&devices_lock, flags);
+               peer = peer_lookup_ba(dev, &addr, addr_type);
+               read_unlock_irqrestore(&devices_lock, flags);
+
+               BT_DBG("xmit from %s to %pMR (%pI6c) peer %p", netdev->name,
+                      &addr, &lowpan_cb(skb)->addr, peer);
+
+               if (peer && peer->conn)
+                       err = send_pkt(peer->conn, netdev->dev_addr,
+                                      eui64_addr, skb, netdev);
+       }
+       dev_kfree_skb(skb);
+
+       if (err)
+               BT_DBG("ERROR: xmit failed (%d)", err);
+
+       return (err < 0) ? NET_XMIT_DROP : err;
+}
+
+static const struct net_device_ops netdev_ops = {
+       .ndo_start_xmit         = bt_xmit,
+};
+
+static struct header_ops header_ops = {
+       .create = header_create,
+};
+
+static void netdev_setup(struct net_device *dev)
+{
+       dev->addr_len           = EUI64_ADDR_LEN;
+       dev->type               = ARPHRD_6LOWPAN;
+
+       dev->hard_header_len    = 0;
+       dev->needed_tailroom    = 0;
+       dev->mtu                = IPV6_MIN_MTU;
+       dev->tx_queue_len       = 0;
+       dev->flags              = IFF_RUNNING | IFF_POINTOPOINT;
+       dev->watchdog_timeo     = 0;
+
+       dev->netdev_ops         = &netdev_ops;
+       dev->header_ops         = &header_ops;
+       dev->destructor         = free_netdev;
+}
+
+static struct device_type bt_type = {
+       .name   = "bluetooth",
+};
+
+static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
+{
+       /* addr is the BT address in little-endian format */
+       eui[0] = addr[5];
+       eui[1] = addr[4];
+       eui[2] = addr[3];
+       eui[3] = 0xFF;
+       eui[4] = 0xFE;
+       eui[5] = addr[2];
+       eui[6] = addr[1];
+       eui[7] = addr[0];
+
+       eui[0] ^= 2;
+
+       /* Universal/local bit set, RFC 4291 */
+       if (addr_type == ADDR_LE_DEV_PUBLIC)
+               eui[0] |= 1;
+       else
+               eui[0] &= ~1;
+}
+
+static void set_dev_addr(struct net_device *netdev, bdaddr_t *addr,
+                        u8 addr_type)
+{
+       netdev->addr_assign_type = NET_ADDR_PERM;
+       set_addr(netdev->dev_addr, addr->b, addr_type);
+       netdev->dev_addr[0] ^= 2;
+}
+
+static void ifup(struct net_device *netdev)
+{
+       int err;
+
+       rtnl_lock();
+       err = dev_open(netdev);
+       if (err < 0)
+               BT_INFO("iface %s cannot be opened (%d)", netdev->name, err);
+       rtnl_unlock();
+}
+
+static void do_notify_peers(struct work_struct *work)
+{
+       struct lowpan_dev *dev = container_of(work, struct lowpan_dev,
+                                             notify_peers.work);
+
+       netdev_notify_peers(dev->netdev); /* send neighbour adv at startup */
+}
+
+static bool is_bt_6lowpan(struct hci_conn *hcon)
+{
+       if (hcon->type != LE_LINK)
+               return false;
+
+       return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
+}
+
+static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev)
+{
+       struct lowpan_peer *peer;
+       unsigned long flags;
+
+       peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
+       if (!peer)
+               return -ENOMEM;
+
+       peer->conn = conn;
+       memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
+
+       /* RFC 2464 ch. 5 */
+       peer->peer_addr.s6_addr[0] = 0xFE;
+       peer->peer_addr.s6_addr[1] = 0x80;
+       set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
+                conn->hcon->dst_type);
+
+       memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
+              EUI64_ADDR_LEN);
+       peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
+                                  * is done according RFC2464
+                                  */
+
+       raw_dump_inline(__func__, "peer IPv6 address",
+                       (unsigned char *)&peer->peer_addr, 16);
+       raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
+
+       write_lock_irqsave(&devices_lock, flags);
+       INIT_LIST_HEAD(&peer->list);
+       peer_add(dev, peer);
+       write_unlock_irqrestore(&devices_lock, flags);
+
+       /* Notifying peers about us needs to be done without locks held */
+       INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
+       schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
+
+       return 0;
+}
+
+/* This gets called when BT LE 6LoWPAN device is connected. We then
+ * create network device that acts as a proxy between BT LE device
+ * and kernel network stack.
+ */
+int bt_6lowpan_add_conn(struct l2cap_conn *conn)
+{
+       struct lowpan_peer *peer = NULL;
+       struct lowpan_dev *dev;
+       struct net_device *netdev;
+       int err = 0;
+       unsigned long flags;
+
+       if (!is_bt_6lowpan(conn->hcon))
+               return 0;
+
+       peer = lookup_peer(conn);
+       if (peer)
+               return -EEXIST;
+
+       dev = lookup_dev(conn);
+       if (dev)
+               return add_peer_conn(conn, dev);
+
+       netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup);
+       if (!netdev)
+               return -ENOMEM;
+
+       set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type);
+
+       netdev->netdev_ops = &netdev_ops;
+       SET_NETDEV_DEV(netdev, &conn->hcon->dev);
+       SET_NETDEV_DEVTYPE(netdev, &bt_type);
+
+       err = register_netdev(netdev);
+       if (err < 0) {
+               BT_INFO("register_netdev failed %d", err);
+               free_netdev(netdev);
+               goto out;
+       }
+
+       BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
+              netdev->ifindex, &conn->hcon->dst, &conn->hcon->src);
+       set_bit(__LINK_STATE_PRESENT, &netdev->state);
+
+       dev = netdev_priv(netdev);
+       dev->netdev = netdev;
+       dev->hdev = conn->hcon->hdev;
+       INIT_LIST_HEAD(&dev->peers);
+
+       write_lock_irqsave(&devices_lock, flags);
+       INIT_LIST_HEAD(&dev->list);
+       list_add(&dev->list, &bt_6lowpan_devices);
+       write_unlock_irqrestore(&devices_lock, flags);
+
+       ifup(netdev);
+
+       return add_peer_conn(conn, dev);
+
+out:
+       return err;
+}
+
+static void delete_netdev(struct work_struct *work)
+{
+       struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
+                                               delete_netdev);
+
+       unregister_netdev(entry->netdev);
+
+       /* The entry pointer is deleted in device_event() */
+}
+
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+       struct lowpan_dev *entry, *tmp;
+       struct lowpan_dev *dev = NULL;
+       struct lowpan_peer *peer;
+       int err = -ENOENT;
+       unsigned long flags;
+       bool last = false;
+
+       if (!conn || !is_bt_6lowpan(conn->hcon))
+               return 0;
+
+       write_lock_irqsave(&devices_lock, flags);
+
+       list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+               dev = lowpan_dev(entry->netdev);
+               peer = peer_lookup_conn(dev, conn);
+               if (peer) {
+                       last = peer_del(dev, peer);
+                       err = 0;
+                       break;
+               }
+       }
+
+       if (!err && last && dev && !atomic_read(&dev->peer_count)) {
+               write_unlock_irqrestore(&devices_lock, flags);
+
+               cancel_delayed_work_sync(&dev->notify_peers);
+
+               /* bt_6lowpan_del_conn() is called with hci dev lock held which
+                * means that we must delete the netdevice in worker thread.
+                */
+               INIT_WORK(&entry->delete_netdev, delete_netdev);
+               schedule_work(&entry->delete_netdev);
+       } else {
+               write_unlock_irqrestore(&devices_lock, flags);
+       }
+
+       return err;
+}
+
+static int device_event(struct notifier_block *unused,
+                       unsigned long event, void *ptr)
+{
+       struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+       struct lowpan_dev *entry, *tmp;
+       unsigned long flags;
+
+       if (netdev->type != ARPHRD_6LOWPAN)
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case NETDEV_UNREGISTER:
+               write_lock_irqsave(&devices_lock, flags);
+               list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
+                                        list) {
+                       if (entry->netdev == netdev) {
+                               list_del(&entry->list);
+                               kfree(entry);
+                               break;
+                       }
+               }
+               write_unlock_irqrestore(&devices_lock, flags);
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block bt_6lowpan_dev_notifier = {
+       .notifier_call = device_event,
+};
+
+int bt_6lowpan_init(void)
+{
+       return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
+
+void bt_6lowpan_cleanup(void)
+{
+       unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
new file mode 100644 (file)
index 0000000..680eac8
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+   Copyright (c) 2013 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 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.
+*/
+
+#ifndef __6LOWPAN_H
+#define __6LOWPAN_H
+
+#include <linux/skbuff.h>
+#include <net/bluetooth/l2cap.h>
+
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
+int bt_6lowpan_add_conn(struct l2cap_conn *conn);
+int bt_6lowpan_del_conn(struct l2cap_conn *conn);
+int bt_6lowpan_init(void);
+void bt_6lowpan_cleanup(void);
+
+#endif /* __6LOWPAN_H */
index 6a791e7..cc6827e 100644 (file)
@@ -10,6 +10,10 @@ obj-$(CONFIG_BT_HIDP)        += hidp/
 
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
        hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
-       a2mp.o amp.o
+       a2mp.o amp.o 6lowpan.o
+
+ifeq ($(CONFIG_IEEE802154_6LOWPAN),)
+  bluetooth-y +=  ../ieee802154/6lowpan_iphc.o
+endif
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
index 8b8b5f8..5e8663c 100644 (file)
@@ -636,6 +636,49 @@ static int conn_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
                        conn_max_interval_set, "%llu\n");
 
+static ssize_t lowpan_read(struct file *file, char __user *user_buf,
+                          size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y' : 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
+                           size_t count, loff_t *position)
+{
+       struct hci_dev *hdev = fp->private_data;
+       bool enable;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+
+       if (copy_from_user(buf, user_buffer, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+
+       if (strtobool(buf, &enable) < 0)
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
+               return -EALREADY;
+
+       change_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+
+       return count;
+}
+
+static const struct file_operations lowpan_debugfs_fops = {
+       .open           = simple_open,
+       .read           = lowpan_read,
+       .write          = lowpan_write,
+       .llseek         = default_llseek,
+};
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -1261,8 +1304,13 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
         * 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.
+        *
+        * Some controllers indicate that they support handling deleting
+        * stored link keys, but they don't. The quirk lets a driver
+        * just disable this command.
         */
-       if (hdev->commands[6] & 0x80) {
+       if (hdev->commands[6] & 0x80 &&
+           !test_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks)) {
                struct hci_cp_delete_stored_link_key cp;
 
                bacpy(&cp.bdaddr, BDADDR_ANY);
@@ -1406,6 +1454,8 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &conn_min_interval_fops);
                debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
                                    hdev, &conn_max_interval_fops);
+               debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
+                                   &lowpan_debugfs_fops);
        }
 
        return 0;
index 5fb3df6..5f81245 100644 (file)
@@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
        conn->handle = __le16_to_cpu(ev->handle);
        conn->state = BT_CONNECTED;
 
+       if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
+               set_bit(HCI_CONN_6LOWPAN, &conn->flags);
+
        hci_conn_add_sysfs(conn);
 
        hci_proto_connect_cfm(conn, ev->status);
index 6a6c8bb..7552f9e 100644 (file)
@@ -940,8 +940,22 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
        bt_cb(skb)->pkt_type = *((unsigned char *) skb->data);
        skb_pull(skb, 1);
 
-       if (hci_pi(sk)->channel == HCI_CHANNEL_RAW &&
-           bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
+       if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
+               /* No permission check is needed for user channel
+                * since that gets enforced when binding the socket.
+                *
+                * However check that the packet type is valid.
+                */
+               if (bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
+                   bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+                   bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+                       err = -EINVAL;
+                       goto drop;
+               }
+
+               skb_queue_tail(&hdev->raw_q, skb);
+               queue_work(hdev->workqueue, &hdev->tx_work);
+       } else if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) {
                u16 opcode = get_unaligned_le16(skb->data);
                u16 ogf = hci_opcode_ogf(opcode);
                u16 ocf = hci_opcode_ocf(opcode);
@@ -972,14 +986,6 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
                        goto drop;
                }
 
-               if (hci_pi(sk)->channel == HCI_CHANNEL_USER &&
-                   bt_cb(skb)->pkt_type != HCI_COMMAND_PKT &&
-                   bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
-                   bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
-                       err = -EINVAL;
-                       goto drop;
-               }
-
                skb_queue_tail(&hdev->raw_q, skb);
                queue_work(hdev->workqueue, &hdev->tx_work);
        }
index b6bca64..b0ad2c7 100644 (file)
@@ -40,6 +40,7 @@
 #include "smp.h"
 #include "a2mp.h"
 #include "amp.h"
+#include "6lowpan.h"
 
 bool disable_ertm;
 
@@ -1468,6 +1469,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
        BT_DBG("");
 
+       bt_6lowpan_add_conn(conn);
+
        /* Check if we have socket listening on cid */
        pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
                                          &hcon->src, &hcon->dst);
@@ -7119,6 +7122,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                        l2cap_conn_del(conn->hcon, EACCES);
                break;
 
+       case L2CAP_FC_6LOWPAN:
+               bt_6lowpan_recv(conn, skb);
+               break;
+
        default:
                l2cap_data_channel(conn, cid, skb);
                break;
@@ -7186,6 +7193,8 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
        BT_DBG("hcon %p reason %d", hcon, reason);
 
+       bt_6lowpan_del_conn(hcon->l2cap_data);
+
        l2cap_conn_del(hcon, bt_to_errno(reason));
 }
 
@@ -7467,11 +7476,14 @@ int __init l2cap_init(void)
        debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
                           &le_default_mps);
 
+       bt_6lowpan_init();
+
        return 0;
 }
 
 void l2cap_exit(void)
 {
+       bt_6lowpan_cleanup();
        debugfs_remove(l2cap_debugfs);
        l2cap_cleanup_sockets();
 }
index e7806e6..20ef748 100644 (file)
@@ -147,6 +147,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
                    __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM)
                        chan->sec_level = BT_SECURITY_SDP;
                break;
+       case L2CAP_CHAN_RAW:
+               chan->sec_level = BT_SECURITY_SDP;
+               break;
        }
 
        bacpy(&chan->src, &la.l2_bdaddr);
index 84fcf9f..f9c0980 100644 (file)
@@ -58,6 +58,7 @@ struct rfcomm_dev {
        uint                    modem_status;
 
        struct rfcomm_dlc       *dlc;
+       wait_queue_head_t       conn_wait;
 
        struct device           *tty_dev;
 
@@ -103,20 +104,60 @@ static void rfcomm_dev_destruct(struct tty_port *port)
        module_put(THIS_MODULE);
 }
 
-/* device-specific initialization: open the dlc */
-static int rfcomm_dev_activate(struct tty_port *port, struct tty_struct *tty)
+static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
 {
-       struct rfcomm_dev *dev = container_of(port, struct rfcomm_dev, port);
+       struct hci_dev *hdev;
+       struct hci_conn *conn;
 
-       return rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
+       hdev = hci_get_route(&dev->dst, &dev->src);
+       if (!hdev)
+               return NULL;
+
+       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
+
+       hci_dev_put(hdev);
+
+       return conn ? &conn->dev : NULL;
 }
 
-/* we block the open until the dlc->state becomes BT_CONNECTED */
-static int rfcomm_dev_carrier_raised(struct tty_port *port)
+/* 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);
+       DEFINE_WAIT(wait);
+       int err;
+
+       err = rfcomm_dlc_open(dev->dlc, &dev->src, &dev->dst, dev->channel);
+       if (err)
+               return err;
+
+       while (1) {
+               prepare_to_wait(&dev->conn_wait, &wait, TASK_INTERRUPTIBLE);
+
+               if (dev->dlc->state == BT_CLOSED) {
+                       err = -dev->err;
+                       break;
+               }
+
+               if (dev->dlc->state == BT_CONNECTED)
+                       break;
+
+               if (signal_pending(current)) {
+                       err = -ERESTARTSYS;
+                       break;
+               }
+
+               tty_unlock(tty);
+               schedule();
+               tty_lock(tty);
+       }
+       finish_wait(&dev->conn_wait, &wait);
+
+       if (!err)
+               device_move(dev->tty_dev, rfcomm_get_device(dev),
+                           DPM_ORDER_DEV_AFTER_PARENT);
 
-       return (dev->dlc->state == BT_CONNECTED);
+       return err;
 }
 
 /* device-specific cleanup: close the dlc */
@@ -135,7 +176,6 @@ 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)
@@ -169,22 +209,6 @@ static struct rfcomm_dev *rfcomm_dev_get(int id)
        return dev;
 }
 
-static struct device *rfcomm_get_device(struct rfcomm_dev *dev)
-{
-       struct hci_dev *hdev;
-       struct hci_conn *conn;
-
-       hdev = hci_get_route(&dev->dst, &dev->src);
-       if (!hdev)
-               return NULL;
-
-       conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &dev->dst);
-
-       hci_dev_put(hdev);
-
-       return conn ? &conn->dev : NULL;
-}
-
 static ssize_t show_address(struct device *tty_dev, struct device_attribute *attr, char *buf)
 {
        struct rfcomm_dev *dev = dev_get_drvdata(tty_dev);
@@ -258,6 +282,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
 
        tty_port_init(&dev->port);
        dev->port.ops = &rfcomm_port_ops;
+       init_waitqueue_head(&dev->conn_wait);
 
        skb_queue_head_init(&dev->pending);
 
@@ -437,7 +462,8 @@ static int rfcomm_release_dev(void __user *arg)
                tty_kref_put(tty);
        }
 
-       if (!test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
+       if (!test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags) &&
+           !test_and_set_bit(RFCOMM_TTY_RELEASED, &dev->flags))
                tty_port_put(&dev->port);
 
        tty_port_put(&dev->port);
@@ -575,12 +601,9 @@ static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
        BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
 
        dev->err = err;
-       if (dlc->state == BT_CONNECTED) {
-               device_move(dev->tty_dev, rfcomm_get_device(dev),
-                           DPM_ORDER_DEV_AFTER_PARENT);
+       wake_up_interruptible(&dev->conn_wait);
 
-               wake_up_interruptible(&dev->port.open_wait);
-       } else if (dlc->state == BT_CLOSED)
+       if (dlc->state == BT_CLOSED)
                tty_port_tty_hangup(&dev->port, false);
 }
 
@@ -670,10 +693,20 @@ static int rfcomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
 
        /* install the tty_port */
        err = tty_port_install(&dev->port, driver, tty);
-       if (err)
+       if (err) {
                rfcomm_tty_cleanup(tty);
+               return err;
+       }
 
-       return err;
+       /* take over the tty_port reference if the port was created with the
+        * flag RFCOMM_RELEASE_ONHUP. This will force the release of the port
+        * when the last process closes the tty. The behaviour is expected by
+        * userspace.
+        */
+       if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+               tty_port_put(&dev->port);
+
+       return 0;
 }
 
 static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
@@ -1010,10 +1043,6 @@ static void rfcomm_tty_hangup(struct tty_struct *tty)
        BT_DBG("tty %p dev %p", tty, dev);
 
        tty_port_hangup(&dev->port);
-
-       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)
@@ -1096,7 +1125,7 @@ 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;
+       rfcomm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON;
        tty_set_operations(rfcomm_tty_driver, &rfcomm_ops);
 
index 459e200..48b25c0 100644 (file)
@@ -62,9 +62,6 @@
 
 #include "6lowpan.h"
 
-/* TTL uncompression values */
-static const u8 lowpan_ttl_values[] = {0, 1, 64, 255};
-
 static LIST_HEAD(lowpan_devices);
 
 /* private device info */
@@ -104,378 +101,14 @@ static inline void lowpan_address_flip(u8 *src, u8 *dest)
                (dest)[IEEE802154_ADDR_LEN - i - 1] = (src)[i];
 }
 
-/* list of all 6lowpan devices, uses for package delivering */
-/* print data in line */
-static inline void lowpan_raw_dump_inline(const char *caller, char *msg,
-                                  unsigned char *buf, int len)
-{
-#ifdef DEBUG
-       if (msg)
-               pr_debug("(%s) %s: ", caller, msg);
-       print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE,
-                      16, 1, buf, len, false);
-#endif /* DEBUG */
-}
-
-/*
- * print data in a table format:
- *
- * addr: xx xx xx xx xx xx
- * addr: xx xx xx xx xx xx
- * ...
- */
-static inline void lowpan_raw_dump_table(const char *caller, char *msg,
-                                  unsigned char *buf, int len)
-{
-#ifdef DEBUG
-       if (msg)
-               pr_debug("(%s) %s:\n", caller, msg);
-       print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET,
-                      16, 1, buf, len, false);
-#endif /* DEBUG */
-}
-
-static u8
-lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift, const struct in6_addr *ipaddr,
-                const unsigned char *lladdr)
-{
-       u8 val = 0;
-
-       if (is_addr_mac_addr_based(ipaddr, lladdr))
-               val = 3; /* 0-bits */
-       else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
-               /* compress IID to 16 bits xxxx::XXXX */
-               memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
-               *hc06_ptr += 2;
-               val = 2; /* 16-bits */
-       } else {
-               /* do not compress IID => xxxx::IID */
-               memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
-               *hc06_ptr += 8;
-               val = 1; /* 64-bits */
-       }
-
-       return rol8(val, shift);
-}
-
-/*
- * Uncompress address function for source and
- * destination address(non-multicast).
- *
- * address_mode is sam value or dam value.
- */
-static int
-lowpan_uncompress_addr(struct sk_buff *skb,
-               struct in6_addr *ipaddr,
-               const u8 address_mode,
-               const struct ieee802154_addr *lladdr)
-{
-       bool fail;
-
-       switch (address_mode) {
-       case LOWPAN_IPHC_ADDR_00:
-               /* for global link addresses */
-               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
-               break;
-       case LOWPAN_IPHC_ADDR_01:
-               /* fe:80::XXXX:XXXX:XXXX:XXXX */
-               ipaddr->s6_addr[0] = 0xFE;
-               ipaddr->s6_addr[1] = 0x80;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
-               break;
-       case LOWPAN_IPHC_ADDR_02:
-               /* fe:80::ff:fe00:XXXX */
-               ipaddr->s6_addr[0] = 0xFE;
-               ipaddr->s6_addr[1] = 0x80;
-               ipaddr->s6_addr[11] = 0xFF;
-               ipaddr->s6_addr[12] = 0xFE;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
-               break;
-       case LOWPAN_IPHC_ADDR_03:
-               fail = false;
-               switch (lladdr->addr_type) {
-               case IEEE802154_ADDR_LONG:
-                       /* fe:80::XXXX:XXXX:XXXX:XXXX
-                        *        \_________________/
-                        *              hwaddr
-                        */
-                       ipaddr->s6_addr[0] = 0xFE;
-                       ipaddr->s6_addr[1] = 0x80;
-                       memcpy(&ipaddr->s6_addr[8], lladdr->hwaddr,
-                                       IEEE802154_ADDR_LEN);
-                       /* second bit-flip (Universe/Local)
-                        * is done according RFC2464
-                        */
-                       ipaddr->s6_addr[8] ^= 0x02;
-                       break;
-               case IEEE802154_ADDR_SHORT:
-                       /* fe:80::ff:fe00:XXXX
-                        *                \__/
-                        *             short_addr
-                        *
-                        * Universe/Local bit is zero.
-                        */
-                       ipaddr->s6_addr[0] = 0xFE;
-                       ipaddr->s6_addr[1] = 0x80;
-                       ipaddr->s6_addr[11] = 0xFF;
-                       ipaddr->s6_addr[12] = 0xFE;
-                       ipaddr->s6_addr16[7] = htons(lladdr->short_addr);
-                       break;
-               default:
-                       pr_debug("Invalid addr_type set\n");
-                       return -EINVAL;
-               }
-               break;
-       default:
-               pr_debug("Invalid address mode value: 0x%x\n", address_mode);
-               return -EINVAL;
-       }
-
-       if (fail) {
-               pr_debug("Failed to fetch skb data\n");
-               return -EIO;
-       }
-
-       lowpan_raw_dump_inline(NULL, "Reconstructed ipv6 addr is:\n",
-                       ipaddr->s6_addr, 16);
-
-       return 0;
-}
-
-/* Uncompress address function for source context
- * based address(non-multicast).
- */
-static int
-lowpan_uncompress_context_based_src_addr(struct sk_buff *skb,
-               struct in6_addr *ipaddr,
-               const u8 sam)
-{
-       switch (sam) {
-       case LOWPAN_IPHC_ADDR_00:
-               /* unspec address ::
-                * Do nothing, address is already ::
-                */
-               break;
-       case LOWPAN_IPHC_ADDR_01:
-               /* TODO */
-       case LOWPAN_IPHC_ADDR_02:
-               /* TODO */
-       case LOWPAN_IPHC_ADDR_03:
-               /* TODO */
-               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
-               return -EINVAL;
-       default:
-               pr_debug("Invalid sam value: 0x%x\n", sam);
-               return -EINVAL;
-       }
-
-       lowpan_raw_dump_inline(NULL,
-                       "Reconstructed context based ipv6 src addr is:\n",
-                       ipaddr->s6_addr, 16);
-
-       return 0;
-}
-
-/* Uncompress function for multicast destination address,
- * when M bit is set.
- */
-static int
-lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
-               struct in6_addr *ipaddr,
-               const u8 dam)
-{
-       bool fail;
-
-       switch (dam) {
-       case LOWPAN_IPHC_DAM_00:
-               /* 00:  128 bits.  The full address
-                * is carried in-line.
-                */
-               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
-               break;
-       case LOWPAN_IPHC_DAM_01:
-               /* 01:  48 bits.  The address takes
-                * the form ffXX::00XX:XXXX:XXXX.
-                */
-               ipaddr->s6_addr[0] = 0xFF;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
-               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[11], 5);
-               break;
-       case LOWPAN_IPHC_DAM_10:
-               /* 10:  32 bits.  The address takes
-                * the form ffXX::00XX:XXXX.
-                */
-               ipaddr->s6_addr[0] = 0xFF;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
-               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[13], 3);
-               break;
-       case LOWPAN_IPHC_DAM_11:
-               /* 11:  8 bits.  The address takes
-                * the form ff02::00XX.
-                */
-               ipaddr->s6_addr[0] = 0xFF;
-               ipaddr->s6_addr[1] = 0x02;
-               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
-               break;
-       default:
-               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
-               return -EINVAL;
-       }
-
-       if (fail) {
-               pr_debug("Failed to fetch skb data\n");
-               return -EIO;
-       }
-
-       lowpan_raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is:\n",
-                       ipaddr->s6_addr, 16);
-
-       return 0;
-}
-
-static void
-lowpan_compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
-{
-       struct udphdr *uh = udp_hdr(skb);
-
-       if (((uh->source & LOWPAN_NHC_UDP_4BIT_MASK) ==
-                               LOWPAN_NHC_UDP_4BIT_PORT) &&
-           ((uh->dest & LOWPAN_NHC_UDP_4BIT_MASK) ==
-                               LOWPAN_NHC_UDP_4BIT_PORT)) {
-               pr_debug("UDP header: both ports compression to 4 bits\n");
-               **hc06_ptr = LOWPAN_NHC_UDP_CS_P_11;
-               **(hc06_ptr + 1) = /* subtraction is faster */
-                  (u8)((uh->dest - LOWPAN_NHC_UDP_4BIT_PORT) +
-                      ((uh->source & LOWPAN_NHC_UDP_4BIT_PORT) << 4));
-               *hc06_ptr += 2;
-       } else if ((uh->dest & LOWPAN_NHC_UDP_8BIT_MASK) ==
-                       LOWPAN_NHC_UDP_8BIT_PORT) {
-               pr_debug("UDP header: remove 8 bits of dest\n");
-               **hc06_ptr = LOWPAN_NHC_UDP_CS_P_01;
-               memcpy(*hc06_ptr + 1, &uh->source, 2);
-               **(hc06_ptr + 3) = (u8)(uh->dest - LOWPAN_NHC_UDP_8BIT_PORT);
-               *hc06_ptr += 4;
-       } else if ((uh->source & LOWPAN_NHC_UDP_8BIT_MASK) ==
-                       LOWPAN_NHC_UDP_8BIT_PORT) {
-               pr_debug("UDP header: remove 8 bits of source\n");
-               **hc06_ptr = LOWPAN_NHC_UDP_CS_P_10;
-               memcpy(*hc06_ptr + 1, &uh->dest, 2);
-               **(hc06_ptr + 3) = (u8)(uh->source - LOWPAN_NHC_UDP_8BIT_PORT);
-               *hc06_ptr += 4;
-       } else {
-               pr_debug("UDP header: can't compress\n");
-               **hc06_ptr = LOWPAN_NHC_UDP_CS_P_00;
-               memcpy(*hc06_ptr + 1, &uh->source, 2);
-               memcpy(*hc06_ptr + 3, &uh->dest, 2);
-               *hc06_ptr += 5;
-       }
-
-       /* checksum is always inline */
-       memcpy(*hc06_ptr, &uh->check, 2);
-       *hc06_ptr += 2;
-
-       /* skip the UDP header */
-       skb_pull(skb, sizeof(struct udphdr));
-}
-
-static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
-{
-       if (unlikely(!pskb_may_pull(skb, 1)))
-               return -EINVAL;
-
-       *val = skb->data[0];
-       skb_pull(skb, 1);
-
-       return 0;
-}
-
-static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
-{
-       if (unlikely(!pskb_may_pull(skb, 2)))
-               return -EINVAL;
-
-       *val = (skb->data[0] << 8) | skb->data[1];
-       skb_pull(skb, 2);
-
-       return 0;
-}
-
-static int
-lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
-{
-       u8 tmp;
-
-       if (!uh)
-               goto err;
-
-       if (lowpan_fetch_skb_u8(skb, &tmp))
-               goto err;
-
-       if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
-               pr_debug("UDP header uncompression\n");
-               switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
-               case LOWPAN_NHC_UDP_CS_P_00:
-                       memcpy(&uh->source, &skb->data[0], 2);
-                       memcpy(&uh->dest, &skb->data[2], 2);
-                       skb_pull(skb, 4);
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_01:
-                       memcpy(&uh->source, &skb->data[0], 2);
-                       uh->dest =
-                          skb->data[2] + LOWPAN_NHC_UDP_8BIT_PORT;
-                       skb_pull(skb, 3);
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_10:
-                       uh->source = skb->data[0] + LOWPAN_NHC_UDP_8BIT_PORT;
-                       memcpy(&uh->dest, &skb->data[1], 2);
-                       skb_pull(skb, 3);
-                       break;
-               case LOWPAN_NHC_UDP_CS_P_11:
-                       uh->source =
-                          LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] >> 4);
-                       uh->dest =
-                          LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] & 0x0f);
-                       skb_pull(skb, 1);
-                       break;
-               default:
-                       pr_debug("ERROR: unknown UDP format\n");
-                       goto err;
-               }
-
-               pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
-                        uh->source, uh->dest);
-
-               /* copy checksum */
-               memcpy(&uh->check, &skb->data[0], 2);
-               skb_pull(skb, 2);
-
-               /*
-                * UDP lenght needs to be infered from the lower layers
-                * here, we obtain the hint from the remaining size of the
-                * frame
-                */
-               uh->len = htons(skb->len + sizeof(struct udphdr));
-               pr_debug("uncompressed UDP length: src = %d", uh->len);
-       } else {
-               pr_debug("ERROR: unsupported NH format\n");
-               goto err;
-       }
-
-       return 0;
-err:
-       return -EINVAL;
-}
-
 static int lowpan_header_create(struct sk_buff *skb,
                           struct net_device *dev,
                           unsigned short type, const void *_daddr,
                           const void *_saddr, unsigned int len)
 {
-       u8 tmp, iphc0, iphc1, *hc06_ptr;
        struct ipv6hdr *hdr;
        const u8 *saddr = _saddr;
        const u8 *daddr = _daddr;
-       u8 head[100];
        struct ieee802154_addr sa, da;
 
        /* TODO:
@@ -485,181 +118,14 @@ static int lowpan_header_create(struct sk_buff *skb,
                return 0;
 
        hdr = ipv6_hdr(skb);
-       hc06_ptr = head + 2;
-
-       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n"
-                "\tnexthdr = 0x%02x\n\thop_lim = %d\n", hdr->version,
-                ntohs(hdr->payload_len), hdr->nexthdr, hdr->hop_limit);
-
-       lowpan_raw_dump_table(__func__, "raw skb network header dump",
-               skb_network_header(skb), sizeof(struct ipv6hdr));
 
        if (!saddr)
                saddr = dev->dev_addr;
 
-       lowpan_raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
-
-       /*
-        * As we copy some bit-length fields, in the IPHC encoding bytes,
-        * we sometimes use |=
-        * If the field is 0, and the current bit value in memory is 1,
-        * this does not work. We therefore reset the IPHC encoding here
-        */
-       iphc0 = LOWPAN_DISPATCH_IPHC;
-       iphc1 = 0;
-
-       /* TODO: context lookup */
+       raw_dump_inline(__func__, "saddr", (unsigned char *)saddr, 8);
+       raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
 
-       lowpan_raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
-
-       /*
-        * Traffic class, flow label
-        * If flow label is 0, compress it. If traffic class is 0, compress it
-        * We have to process both in the same time as the offset of traffic
-        * class depends on the presence of version and flow label
-        */
-
-       /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
-       tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
-       tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
-
-       if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
-            (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
-               /* flow label can be compressed */
-               iphc0 |= LOWPAN_IPHC_FL_C;
-               if ((hdr->priority == 0) &&
-                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress (elide) all */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-               } else {
-                       /* compress only the flow label */
-                       *hc06_ptr = tmp;
-                       hc06_ptr += 1;
-               }
-       } else {
-               /* Flow label cannot be compressed */
-               if ((hdr->priority == 0) &&
-                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
-                       /* compress only traffic class */
-                       iphc0 |= LOWPAN_IPHC_TC_C;
-                       *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
-                       memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
-                       hc06_ptr += 3;
-               } else {
-                       /* compress nothing */
-                       memcpy(hc06_ptr, &hdr, 4);
-                       /* replace the top byte with new ECN | DSCP format */
-                       *hc06_ptr = tmp;
-                       hc06_ptr += 4;
-               }
-       }
-
-       /* NOTE: payload length is always compressed */
-
-       /* Next Header is compress if UDP */
-       if (hdr->nexthdr == UIP_PROTO_UDP)
-               iphc0 |= LOWPAN_IPHC_NH_C;
-
-       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
-               *hc06_ptr = hdr->nexthdr;
-               hc06_ptr += 1;
-       }
-
-       /*
-        * Hop limit
-        * if 1:   compress, encoding is 01
-        * if 64:  compress, encoding is 10
-        * if 255: compress, encoding is 11
-        * else do not compress
-        */
-       switch (hdr->hop_limit) {
-       case 1:
-               iphc0 |= LOWPAN_IPHC_TTL_1;
-               break;
-       case 64:
-               iphc0 |= LOWPAN_IPHC_TTL_64;
-               break;
-       case 255:
-               iphc0 |= LOWPAN_IPHC_TTL_255;
-               break;
-       default:
-               *hc06_ptr = hdr->hop_limit;
-               hc06_ptr += 1;
-               break;
-       }
-
-       /* source address compression */
-       if (is_addr_unspecified(&hdr->saddr)) {
-               pr_debug("source address is unspecified, setting SAC\n");
-               iphc1 |= LOWPAN_IPHC_SAC;
-       /* TODO: context lookup */
-       } else if (is_addr_link_local(&hdr->saddr)) {
-               pr_debug("source address is link-local\n");
-               iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
-                               LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr);
-       } else {
-               pr_debug("send the full source address\n");
-               memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
-               hc06_ptr += 16;
-       }
-
-       /* destination address compression */
-       if (is_addr_mcast(&hdr->daddr)) {
-               pr_debug("destination address is multicast: ");
-               iphc1 |= LOWPAN_IPHC_M;
-               if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
-                       pr_debug("compressed to 1 octet\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_11;
-                       /* use last byte */
-                       *hc06_ptr = hdr->daddr.s6_addr[15];
-                       hc06_ptr += 1;
-               } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
-                       pr_debug("compressed to 4 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_10;
-                       /* second byte + the last three */
-                       *hc06_ptr = hdr->daddr.s6_addr[1];
-                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
-                       hc06_ptr += 4;
-               } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
-                       pr_debug("compressed to 6 octets\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_01;
-                       /* second byte + the last five */
-                       *hc06_ptr = hdr->daddr.s6_addr[1];
-                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
-                       hc06_ptr += 6;
-               } else {
-                       pr_debug("using full address\n");
-                       iphc1 |= LOWPAN_IPHC_DAM_00;
-                       memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
-                       hc06_ptr += 16;
-               }
-       } else {
-               /* TODO: context lookup */
-               if (is_addr_link_local(&hdr->daddr)) {
-                       pr_debug("dest address is unicast and link-local\n");
-                       iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
-                               LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr);
-               } else {
-                       pr_debug("dest address is unicast: using full one\n");
-                       memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
-                       hc06_ptr += 16;
-               }
-       }
-
-       /* UDP header compression */
-       if (hdr->nexthdr == UIP_PROTO_UDP)
-               lowpan_compress_udp_header(&hc06_ptr, skb);
-
-       head[0] = iphc0;
-       head[1] = iphc1;
-
-       skb_pull(skb, sizeof(struct ipv6hdr));
-       skb_reset_transport_header(skb);
-       memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
-       skb_reset_network_header(skb);
-
-       lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data,
-                               skb->len);
+       lowpan_header_compress(skb, dev, type, daddr, saddr, len);
 
        /*
         * NOTE1: I'm still unsure about the fact that compression and WPAN
@@ -671,39 +137,38 @@ static int lowpan_header_create(struct sk_buff *skb,
         * from MAC subif of the 'dev' and 'real_dev' network devices, but
         * this isn't implemented in mainline yet, so currently we assign 0xff
         */
-       {
-               mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
-               mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+       mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
+       mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
 
-               /* prepare wpan address data */
-               sa.addr_type = IEEE802154_ADDR_LONG;
-               sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+       /* prepare wpan address data */
+       sa.addr_type = IEEE802154_ADDR_LONG;
+       sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
 
-               memcpy(&(sa.hwaddr), saddr, 8);
-               /* intra-PAN communications */
-               da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+       memcpy(&(sa.hwaddr), saddr, 8);
+       /* intra-PAN communications */
+       da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
 
-               /*
-                * if the destination address is the broadcast address, use the
-                * corresponding short address
-                */
-               if (lowpan_is_addr_broadcast(daddr)) {
-                       da.addr_type = IEEE802154_ADDR_SHORT;
-                       da.short_addr = IEEE802154_ADDR_BROADCAST;
-               } else {
-                       da.addr_type = IEEE802154_ADDR_LONG;
-                       memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN);
-
-                       /* request acknowledgment */
-                       mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
-               }
+       /*
+        * if the destination address is the broadcast address, use the
+        * corresponding short address
+        */
+       if (lowpan_is_addr_broadcast(daddr)) {
+               da.addr_type = IEEE802154_ADDR_SHORT;
+               da.short_addr = IEEE802154_ADDR_BROADCAST;
+       } else {
+               da.addr_type = IEEE802154_ADDR_LONG;
+               memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN);
 
-               return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
-                               type, (void *)&da, (void *)&sa, skb->len);
+               /* request acknowledgment */
+               mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
        }
+
+       return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
+                       type, (void *)&da, (void *)&sa, skb->len);
 }
 
-static int lowpan_give_skb_to_devices(struct sk_buff *skb)
+static int lowpan_give_skb_to_devices(struct sk_buff *skb,
+                                       struct net_device *dev)
 {
        struct lowpan_dev_record *entry;
        struct sk_buff *skb_cp;
@@ -726,31 +191,6 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb)
        return stat;
 }
 
-static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr)
-{
-       struct sk_buff *new;
-       int stat = NET_RX_SUCCESS;
-
-       new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
-                                                               GFP_ATOMIC);
-       kfree_skb(skb);
-
-       if (!new)
-               return -ENOMEM;
-
-       skb_push(new, sizeof(struct ipv6hdr));
-       skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
-
-       new->protocol = htons(ETH_P_IPV6);
-       new->pkt_type = PACKET_HOST;
-
-       stat = lowpan_give_skb_to_devices(new);
-
-       kfree_skb(new);
-
-       return stat;
-}
-
 static void lowpan_fragment_timer_expired(unsigned long entry_addr)
 {
        struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr;
@@ -814,16 +254,12 @@ frame_err:
        return NULL;
 }
 
-static int
-lowpan_process_data(struct sk_buff *skb)
+static int process_data(struct sk_buff *skb)
 {
-       struct ipv6hdr hdr = {};
-       u8 tmp, iphc0, iphc1, num_context = 0;
+       u8 iphc0, iphc1;
        const struct ieee802154_addr *_saddr, *_daddr;
-       int err;
 
-       lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data,
-                               skb->len);
+       raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
        /* at least two bytes will be used for the encoding */
        if (skb->len < 2)
                goto drop;
@@ -925,162 +361,11 @@ lowpan_process_data(struct sk_buff *skb)
        _saddr = &mac_cb(skb)->sa;
        _daddr = &mac_cb(skb)->da;
 
-       pr_debug("iphc0 = %02x, iphc1 = %02x\n", iphc0, iphc1);
-
-       /* another if the CID flag is set */
-       if (iphc1 & LOWPAN_IPHC_CID) {
-               pr_debug("CID flag is set, increase header with one\n");
-               if (lowpan_fetch_skb_u8(skb, &num_context))
-                       goto drop;
-       }
-
-       hdr.version = 6;
-
-       /* Traffic Class and Flow Label */
-       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
-       /*
-        * Traffic Class and FLow Label carried in-line
-        * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
-        */
-       case 0: /* 00b */
-               if (lowpan_fetch_skb_u8(skb, &tmp))
-                       goto drop;
-
-               memcpy(&hdr.flow_lbl, &skb->data[0], 3);
-               skb_pull(skb, 3);
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
-                                       (hdr.flow_lbl[0] & 0x0f);
-               break;
-       /*
-        * Traffic class carried in-line
-        * ECN + DSCP (1 byte), Flow Label is elided
-        */
-       case 2: /* 10b */
-               if (lowpan_fetch_skb_u8(skb, &tmp))
-                       goto drop;
-
-               hdr.priority = ((tmp >> 2) & 0x0f);
-               hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
-               break;
-       /*
-        * Flow Label carried in-line
-        * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
-        */
-       case 1: /* 01b */
-               if (lowpan_fetch_skb_u8(skb, &tmp))
-                       goto drop;
-
-               hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
-               memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
-               skb_pull(skb, 2);
-               break;
-       /* Traffic Class and Flow Label are elided */
-       case 3: /* 11b */
-               break;
-       default:
-               break;
-       }
-
-       /* Next Header */
-       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
-               /* Next header is carried inline */
-               if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr)))
-                       goto drop;
-
-               pr_debug("NH flag is set, next header carried inline: %02x\n",
-                        hdr.nexthdr);
-       }
-
-       /* Hop Limit */
-       if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
-               hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
-       else {
-               if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit)))
-                       goto drop;
-       }
-
-       /* Extract SAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
-
-       if (iphc1 & LOWPAN_IPHC_SAC) {
-               /* Source address context based uncompression */
-               pr_debug("SAC bit is set. Handle context based source address.\n");
-               err = lowpan_uncompress_context_based_src_addr(
-                               skb, &hdr.saddr, tmp);
-       } else {
-               /* Source address uncompression */
-               pr_debug("source address stateless compression\n");
-               err = lowpan_uncompress_addr(skb, &hdr.saddr, tmp, _saddr);
-       }
-
-       /* Check on error of previous branch */
-       if (err)
-               goto drop;
-
-       /* Extract DAM to the tmp variable */
-       tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
-
-       /* check for Multicast Compression */
-       if (iphc1 & LOWPAN_IPHC_M) {
-               if (iphc1 & LOWPAN_IPHC_DAC) {
-                       pr_debug("dest: context-based mcast compression\n");
-                       /* TODO: implement this */
-               } else {
-                       err = lowpan_uncompress_multicast_daddr(
-                                       skb, &hdr.daddr, tmp);
-                       if (err)
-                               goto drop;
-               }
-       } else {
-               pr_debug("dest: stateless compression\n");
-               err = lowpan_uncompress_addr(skb, &hdr.daddr, tmp, _daddr);
-               if (err)
-                       goto drop;
-       }
-
-       /* UDP data uncompression */
-       if (iphc0 & LOWPAN_IPHC_NH_C) {
-               struct udphdr uh;
-               struct sk_buff *new;
-               if (lowpan_uncompress_udp_header(skb, &uh))
-                       goto drop;
-
-               /*
-                * replace the compressed UDP head by the uncompressed UDP
-                * header
-                */
-               new = skb_copy_expand(skb, sizeof(struct udphdr),
-                                     skb_tailroom(skb), GFP_ATOMIC);
-               kfree_skb(skb);
-
-               if (!new)
-                       return -ENOMEM;
-
-               skb = new;
-
-               skb_push(skb, sizeof(struct udphdr));
-               skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
-
-               lowpan_raw_dump_table(__func__, "raw UDP header dump",
-                                     (u8 *)&uh, sizeof(uh));
-
-               hdr.nexthdr = UIP_PROTO_UDP;
-       }
-
-       /* Not fragmented package */
-       hdr.payload_len = htons(skb->len);
-
-       pr_debug("skb headroom size = %d, data length = %d\n",
-                skb_headroom(skb), skb->len);
-
-       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t"
-                "nexthdr = 0x%02x\n\thop_lim = %d\n", hdr.version,
-                ntohs(hdr.payload_len), hdr.nexthdr, hdr.hop_limit);
-
-       lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
-                                                       sizeof(hdr));
-       return lowpan_skb_deliver(skb, &hdr);
+       return lowpan_process_data(skb, skb->dev, (u8 *)_saddr->hwaddr,
+                               _saddr->addr_type, IEEE802154_ADDR_LEN,
+                               (u8 *)_daddr->hwaddr, _daddr->addr_type,
+                               IEEE802154_ADDR_LEN, iphc0, iphc1,
+                               lowpan_give_skb_to_devices);
 
 unlock_and_drop:
        spin_unlock_bh(&flist_lock);
@@ -1112,7 +397,7 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
        hlen = (type == LOWPAN_DISPATCH_FRAG1) ?
                        LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE;
 
-       lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
+       raw_dump_inline(__func__, "6lowpan fragment header", head, hlen);
 
        frag = netdev_alloc_skb(skb->dev,
                                hlen + mlen + plen + IEEE802154_MFR_SIZE);
@@ -1132,8 +417,7 @@ lowpan_fragment_xmit(struct sk_buff *skb, u8 *head,
        skb_copy_to_linear_data_offset(frag, mlen + hlen,
                                       skb_network_header(skb) + offset, plen);
 
-       lowpan_raw_dump_table(__func__, " raw fragment dump", frag->data,
-                                                               frag->len);
+       raw_dump_table(__func__, " raw fragment dump", frag->data, frag->len);
 
        return dev_queue_xmit(frag);
 }
@@ -1316,7 +600,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
                /* Pull off the 1-byte of 6lowpan header. */
                skb_pull(local_skb, 1);
 
-               lowpan_give_skb_to_devices(local_skb);
+               lowpan_give_skb_to_devices(local_skb, NULL);
 
                kfree_skb(local_skb);
                kfree_skb(skb);
@@ -1328,7 +612,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
                        local_skb = skb_clone(skb, GFP_ATOMIC);
                        if (!local_skb)
                                goto drop;
-                       lowpan_process_data(local_skb);
+                       process_data(local_skb);
 
                        kfree_skb(skb);
                        break;
index 2869c05..2b835db 100644 (file)
 #define LOWPAN_NHC_UDP_CS_P_10 0xF2 /* source = 0xF0 + 8bit inline,
                                        dest = 16 bit inline */
 #define LOWPAN_NHC_UDP_CS_P_11 0xF3 /* source & dest = 0xF0B + 4bit inline */
+#define LOWPAN_NHC_UDP_CS_C    0x04 /* checksum elided */
+
+#ifdef DEBUG
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+                                  unsigned char *buf, int len)
+{
+       if (msg)
+               pr_debug("%s():%s: ", caller, msg);
+
+       print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, buf, len, false);
+}
+
+/* print data in a table format:
+ *
+ * addr: xx xx xx xx xx xx
+ * addr: xx xx xx xx xx xx
+ * ...
+ */
+static inline void raw_dump_table(const char *caller, char *msg,
+                                 unsigned char *buf, int len)
+{
+       if (msg)
+               pr_debug("%s():%s:\n", caller, msg);
+
+       print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET, 16, 1, buf, len, false);
+}
+#else
+static inline void raw_dump_table(const char *caller, char *msg,
+                                 unsigned char *buf, int len) { }
+static inline void raw_dump_inline(const char *caller, char *msg,
+                                  unsigned char *buf, int len) { }
+#endif
+
+static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+       if (unlikely(!pskb_may_pull(skb, 1)))
+               return -EINVAL;
+
+       *val = skb->data[0];
+       skb_pull(skb, 1);
+
+       return 0;
+}
+
+static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
+{
+       if (unlikely(!pskb_may_pull(skb, 2)))
+               return -EINVAL;
+
+       *val = (skb->data[0] << 8) | skb->data[1];
+       skb_pull(skb, 2);
+
+       return 0;
+}
 
 static inline bool lowpan_fetch_skb(struct sk_buff *skb,
                void *data, const unsigned int len)
@@ -244,4 +299,21 @@ static inline bool lowpan_fetch_skb(struct sk_buff *skb,
        return false;
 }
 
+static inline void lowpan_push_hc_data(u8 **hc_ptr, const void *data,
+                                      const size_t len)
+{
+       memcpy(*hc_ptr, data, len);
+       *hc_ptr += len;
+}
+
+typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev);
+
+int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
+               const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
+               const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
+               u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver);
+int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
+                       unsigned short type, const void *_daddr,
+                       const void *_saddr, unsigned int len);
+
 #endif /* __6LOWPAN_H__ */
diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c
new file mode 100644 (file)
index 0000000..11840f9
--- /dev/null
@@ -0,0 +1,799 @@
+/*
+ * Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+/*
+ * Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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-1301 USA.
+ */
+
+/* Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <net/ipv6.h>
+#include <net/af_ieee802154.h>
+
+#include "6lowpan.h"
+
+/*
+ * Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int uncompress_addr(struct sk_buff *skb,
+                               struct in6_addr *ipaddr, const u8 address_mode,
+                               const u8 *lladdr, const u8 addr_type,
+                               const u8 addr_len)
+{
+       bool fail;
+
+       switch (address_mode) {
+       case LOWPAN_IPHC_ADDR_00:
+               /* for global link addresses */
+               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+               break;
+       case LOWPAN_IPHC_ADDR_01:
+               /* fe:80::XXXX:XXXX:XXXX:XXXX */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+               break;
+       case LOWPAN_IPHC_ADDR_02:
+               /* fe:80::ff:fe00:XXXX */
+               ipaddr->s6_addr[0] = 0xFE;
+               ipaddr->s6_addr[1] = 0x80;
+               ipaddr->s6_addr[11] = 0xFF;
+               ipaddr->s6_addr[12] = 0xFE;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+               break;
+       case LOWPAN_IPHC_ADDR_03:
+               fail = false;
+               switch (addr_type) {
+               case IEEE802154_ADDR_LONG:
+                       /* fe:80::XXXX:XXXX:XXXX:XXXX
+                        *        \_________________/
+                        *              hwaddr
+                        */
+                       ipaddr->s6_addr[0] = 0xFE;
+                       ipaddr->s6_addr[1] = 0x80;
+                       memcpy(&ipaddr->s6_addr[8], lladdr, addr_len);
+                       /* second bit-flip (Universe/Local)
+                        * is done according RFC2464
+                        */
+                       ipaddr->s6_addr[8] ^= 0x02;
+                       break;
+               case IEEE802154_ADDR_SHORT:
+                       /* fe:80::ff:fe00:XXXX
+                        *                \__/
+                        *             short_addr
+                        *
+                        * Universe/Local bit is zero.
+                        */
+                       ipaddr->s6_addr[0] = 0xFE;
+                       ipaddr->s6_addr[1] = 0x80;
+                       ipaddr->s6_addr[11] = 0xFF;
+                       ipaddr->s6_addr[12] = 0xFE;
+                       ipaddr->s6_addr16[7] = htons(*((u16 *)lladdr));
+                       break;
+               default:
+                       pr_debug("Invalid addr_type set\n");
+                       return -EINVAL;
+               }
+               break;
+       default:
+               pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+               return -EINVAL;
+       }
+
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
+       }
+
+       raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
+                       ipaddr->s6_addr, 16);
+
+       return 0;
+}
+
+/*
+ * Uncompress address function for source context
+ * based address(non-multicast).
+ */
+static int uncompress_context_based_src_addr(struct sk_buff *skb,
+                                               struct in6_addr *ipaddr,
+                                               const u8 sam)
+{
+       switch (sam) {
+       case LOWPAN_IPHC_ADDR_00:
+               /* unspec address ::
+                * Do nothing, address is already ::
+                */
+               break;
+       case LOWPAN_IPHC_ADDR_01:
+               /* TODO */
+       case LOWPAN_IPHC_ADDR_02:
+               /* TODO */
+       case LOWPAN_IPHC_ADDR_03:
+               /* TODO */
+               netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+               return -EINVAL;
+       default:
+               pr_debug("Invalid sam value: 0x%x\n", sam);
+               return -EINVAL;
+       }
+
+       raw_dump_inline(NULL,
+                       "Reconstructed context based ipv6 src addr is",
+                       ipaddr->s6_addr, 16);
+
+       return 0;
+}
+
+static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
+               struct net_device *dev, skb_delivery_cb deliver_skb)
+{
+       struct sk_buff *new;
+       int stat;
+
+       new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
+                                                               GFP_ATOMIC);
+       kfree_skb(skb);
+
+       if (!new)
+               return -ENOMEM;
+
+       skb_push(new, sizeof(struct ipv6hdr));
+       skb_reset_network_header(new);
+       skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
+
+       new->protocol = htons(ETH_P_IPV6);
+       new->pkt_type = PACKET_HOST;
+       new->dev = dev;
+
+       raw_dump_table(__func__, "raw skb data dump before receiving",
+                       new->data, new->len);
+
+       stat = deliver_skb(new, dev);
+
+       kfree_skb(new);
+
+       return stat;
+}
+
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int
+lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+               struct in6_addr *ipaddr,
+               const u8 dam)
+{
+       bool fail;
+
+       switch (dam) {
+       case LOWPAN_IPHC_DAM_00:
+               /* 00:  128 bits.  The full address
+                * is carried in-line.
+                */
+               fail = lowpan_fetch_skb(skb, ipaddr->s6_addr, 16);
+               break;
+       case LOWPAN_IPHC_DAM_01:
+               /* 01:  48 bits.  The address takes
+                * the form ffXX::00XX:XXXX:XXXX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+               break;
+       case LOWPAN_IPHC_DAM_10:
+               /* 10:  32 bits.  The address takes
+                * the form ffXX::00XX:XXXX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+               fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+               break;
+       case LOWPAN_IPHC_DAM_11:
+               /* 11:  8 bits.  The address takes
+                * the form ff02::00XX.
+                */
+               ipaddr->s6_addr[0] = 0xFF;
+               ipaddr->s6_addr[1] = 0x02;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+               break;
+       default:
+               pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+               return -EINVAL;
+       }
+
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
+       }
+
+       raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is",
+                               ipaddr->s6_addr, 16);
+
+       return 0;
+}
+
+static int
+uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+       bool fail;
+       u8 tmp = 0, val = 0;
+
+       if (!uh)
+               goto err;
+
+       fail = lowpan_fetch_skb(skb, &tmp, 1);
+
+       if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
+               pr_debug("UDP header uncompression\n");
+               switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+               case LOWPAN_NHC_UDP_CS_P_00:
+                       fail |= lowpan_fetch_skb(skb, &uh->source, 2);
+                       fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
+                       break;
+               case LOWPAN_NHC_UDP_CS_P_01:
+                       fail |= lowpan_fetch_skb(skb, &uh->source, 2);
+                       fail |= lowpan_fetch_skb(skb, &val, 1);
+                       uh->dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+                       break;
+               case LOWPAN_NHC_UDP_CS_P_10:
+                       fail |= lowpan_fetch_skb(skb, &val, 1);
+                       uh->source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
+                       fail |= lowpan_fetch_skb(skb, &uh->dest, 2);
+                       break;
+               case LOWPAN_NHC_UDP_CS_P_11:
+                       fail |= lowpan_fetch_skb(skb, &val, 1);
+                       uh->source = htons(LOWPAN_NHC_UDP_4BIT_PORT +
+                                          (val >> 4));
+                       uh->dest = htons(LOWPAN_NHC_UDP_4BIT_PORT +
+                                        (val & 0x0f));
+                       break;
+               default:
+                       pr_debug("ERROR: unknown UDP format\n");
+                       goto err;
+                       break;
+               }
+
+               pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+                        ntohs(uh->source), ntohs(uh->dest));
+
+               /* checksum */
+               if (tmp & LOWPAN_NHC_UDP_CS_C) {
+                       pr_debug_ratelimited("checksum elided currently not supported\n");
+                       goto err;
+               } else {
+                       fail |= lowpan_fetch_skb(skb, &uh->check, 2);
+               }
+
+               /*
+                * UDP lenght needs to be infered from the lower layers
+                * here, we obtain the hint from the remaining size of the
+                * frame
+                */
+               uh->len = htons(skb->len + sizeof(struct udphdr));
+               pr_debug("uncompressed UDP length: src = %d", ntohs(uh->len));
+       } else {
+               pr_debug("ERROR: unsupported NH format\n");
+               goto err;
+       }
+
+       if (fail)
+               goto err;
+
+       return 0;
+err:
+       return -EINVAL;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
+
+int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
+               const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
+               const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
+               u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
+{
+       struct ipv6hdr hdr = {};
+       u8 tmp, num_context = 0;
+       int err;
+
+       raw_dump_table(__func__, "raw skb data dump uncompressed",
+                               skb->data, skb->len);
+
+       /* another if the CID flag is set */
+       if (iphc1 & LOWPAN_IPHC_CID) {
+               pr_debug("CID flag is set, increase header with one\n");
+               if (lowpan_fetch_skb_u8(skb, &num_context))
+                       goto drop;
+       }
+
+       hdr.version = 6;
+
+       /* Traffic Class and Flow Label */
+       switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+       /*
+        * Traffic Class and FLow Label carried in-line
+        * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+        */
+       case 0: /* 00b */
+               if (lowpan_fetch_skb_u8(skb, &tmp))
+                       goto drop;
+
+               memcpy(&hdr.flow_lbl, &skb->data[0], 3);
+               skb_pull(skb, 3);
+               hdr.priority = ((tmp >> 2) & 0x0f);
+               hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
+                                       (hdr.flow_lbl[0] & 0x0f);
+               break;
+       /*
+        * Traffic class carried in-line
+        * ECN + DSCP (1 byte), Flow Label is elided
+        */
+       case 2: /* 10b */
+               if (lowpan_fetch_skb_u8(skb, &tmp))
+                       goto drop;
+
+               hdr.priority = ((tmp >> 2) & 0x0f);
+               hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+               break;
+       /*
+        * Flow Label carried in-line
+        * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+        */
+       case 1: /* 01b */
+               if (lowpan_fetch_skb_u8(skb, &tmp))
+                       goto drop;
+
+               hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
+               memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
+               skb_pull(skb, 2);
+               break;
+       /* Traffic Class and Flow Label are elided */
+       case 3: /* 11b */
+               break;
+       default:
+               break;
+       }
+
+       /* Next Header */
+       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+               /* Next header is carried inline */
+               if (lowpan_fetch_skb_u8(skb, &(hdr.nexthdr)))
+                       goto drop;
+
+               pr_debug("NH flag is set, next header carried inline: %02x\n",
+                        hdr.nexthdr);
+       }
+
+       /* Hop Limit */
+       if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
+               hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+       else {
+               if (lowpan_fetch_skb_u8(skb, &(hdr.hop_limit)))
+                       goto drop;
+       }
+
+       /* Extract SAM to the tmp variable */
+       tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
+
+       if (iphc1 & LOWPAN_IPHC_SAC) {
+               /* Source address context based uncompression */
+               pr_debug("SAC bit is set. Handle context based source address.\n");
+               err = uncompress_context_based_src_addr(
+                               skb, &hdr.saddr, tmp);
+       } else {
+               /* Source address uncompression */
+               pr_debug("source address stateless compression\n");
+               err = uncompress_addr(skb, &hdr.saddr, tmp, saddr,
+                                       saddr_type, saddr_len);
+       }
+
+       /* Check on error of previous branch */
+       if (err)
+               goto drop;
+
+       /* Extract DAM to the tmp variable */
+       tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
+
+       /* check for Multicast Compression */
+       if (iphc1 & LOWPAN_IPHC_M) {
+               if (iphc1 & LOWPAN_IPHC_DAC) {
+                       pr_debug("dest: context-based mcast compression\n");
+                       /* TODO: implement this */
+               } else {
+                       err = lowpan_uncompress_multicast_daddr(
+                                               skb, &hdr.daddr, tmp);
+                       if (err)
+                               goto drop;
+               }
+       } else {
+               err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
+                                       daddr_type, daddr_len);
+               pr_debug("dest: stateless compression mode %d dest %pI6c\n",
+                       tmp, &hdr.daddr);
+               if (err)
+                       goto drop;
+       }
+
+       /* UDP data uncompression */
+       if (iphc0 & LOWPAN_IPHC_NH_C) {
+               struct udphdr uh;
+               struct sk_buff *new;
+               if (uncompress_udp_header(skb, &uh))
+                       goto drop;
+
+               /*
+                * replace the compressed UDP head by the uncompressed UDP
+                * header
+                */
+               new = skb_copy_expand(skb, sizeof(struct udphdr),
+                                     skb_tailroom(skb), GFP_ATOMIC);
+               kfree_skb(skb);
+
+               if (!new)
+                       return -ENOMEM;
+
+               skb = new;
+
+               skb_push(skb, sizeof(struct udphdr));
+               skb_reset_transport_header(skb);
+               skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+               raw_dump_table(__func__, "raw UDP header dump",
+                                     (u8 *)&uh, sizeof(uh));
+
+               hdr.nexthdr = UIP_PROTO_UDP;
+       }
+
+       hdr.payload_len = htons(skb->len);
+
+       pr_debug("skb headroom size = %d, data length = %d\n",
+                skb_headroom(skb), skb->len);
+
+       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t"
+                "nexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n",
+               hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
+               hdr.hop_limit, &hdr.daddr);
+
+       raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+                                                       sizeof(hdr));
+
+       return skb_deliver(skb, &hdr, dev, deliver_skb);
+
+drop:
+       kfree_skb(skb);
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(lowpan_process_data);
+
+static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
+                               const struct in6_addr *ipaddr,
+                               const unsigned char *lladdr)
+{
+       u8 val = 0;
+
+       if (is_addr_mac_addr_based(ipaddr, lladdr)) {
+               val = 3; /* 0-bits */
+               pr_debug("address compression 0 bits\n");
+       } else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+               /* compress IID to 16 bits xxxx::XXXX */
+               memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
+               *hc06_ptr += 2;
+               val = 2; /* 16-bits */
+               raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
+                       *hc06_ptr - 2, 2);
+       } else {
+               /* do not compress IID => xxxx::IID */
+               memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
+               *hc06_ptr += 8;
+               val = 1; /* 64-bits */
+               raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
+                       *hc06_ptr - 8, 8);
+       }
+
+       return rol8(val, shift);
+}
+
+static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
+{
+       struct udphdr *uh = udp_hdr(skb);
+       u8 tmp;
+
+       if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+            LOWPAN_NHC_UDP_4BIT_PORT) &&
+           ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
+            LOWPAN_NHC_UDP_4BIT_PORT)) {
+               pr_debug("UDP header: both ports compression to 4 bits\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_11;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source and destination port */
+               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
+                     ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+       } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+                       LOWPAN_NHC_UDP_8BIT_PORT) {
+               pr_debug("UDP header: remove 8 bits of dest\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_01;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
+               /* destination port */
+               tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+       } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
+                       LOWPAN_NHC_UDP_8BIT_PORT) {
+               pr_debug("UDP header: remove 8 bits of source\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_10;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* destination port */
+               lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
+       } else {
+               pr_debug("UDP header: can't compress\n");
+               /* compression value */
+               tmp = LOWPAN_NHC_UDP_CS_P_00;
+               lowpan_push_hc_data(hc06_ptr, &tmp, sizeof(tmp));
+               /* source port */
+               lowpan_push_hc_data(hc06_ptr, &uh->source, sizeof(uh->source));
+               /* destination port */
+               lowpan_push_hc_data(hc06_ptr, &uh->dest, sizeof(uh->dest));
+       }
+
+       /* checksum is always inline */
+       lowpan_push_hc_data(hc06_ptr, &uh->check, sizeof(uh->check));
+
+       /* skip the UDP header */
+       skb_pull(skb, sizeof(struct udphdr));
+}
+
+int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
+                       unsigned short type, const void *_daddr,
+                       const void *_saddr, unsigned int len)
+{
+       u8 tmp, iphc0, iphc1, *hc06_ptr;
+       struct ipv6hdr *hdr;
+       u8 head[100] = {};
+
+       if (type != ETH_P_IPV6)
+               return -EINVAL;
+
+       hdr = ipv6_hdr(skb);
+       hc06_ptr = head + 2;
+
+       pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n"
+                "\tnexthdr = 0x%02x\n\thop_lim = %d\n\tdest    = %pI6c\n",
+               hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
+               hdr->hop_limit, &hdr->daddr);
+
+       raw_dump_table(__func__, "raw skb network header dump",
+               skb_network_header(skb), sizeof(struct ipv6hdr));
+
+       /*
+        * As we copy some bit-length fields, in the IPHC encoding bytes,
+        * we sometimes use |=
+        * If the field is 0, and the current bit value in memory is 1,
+        * this does not work. We therefore reset the IPHC encoding here
+        */
+       iphc0 = LOWPAN_DISPATCH_IPHC;
+       iphc1 = 0;
+
+       /* TODO: context lookup */
+
+       raw_dump_inline(__func__, "saddr",
+                       (unsigned char *)_saddr, IEEE802154_ADDR_LEN);
+       raw_dump_inline(__func__, "daddr",
+                       (unsigned char *)_daddr, IEEE802154_ADDR_LEN);
+
+       raw_dump_table(__func__,
+                       "sending raw skb network uncompressed packet",
+                       skb->data, skb->len);
+
+       /*
+        * Traffic class, flow label
+        * If flow label is 0, compress it. If traffic class is 0, compress it
+        * We have to process both in the same time as the offset of traffic
+        * class depends on the presence of version and flow label
+        */
+
+       /* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
+       tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+       tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
+
+       if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
+            (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
+               /* flow label can be compressed */
+               iphc0 |= LOWPAN_IPHC_FL_C;
+               if ((hdr->priority == 0) &&
+                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+                       /* compress (elide) all */
+                       iphc0 |= LOWPAN_IPHC_TC_C;
+               } else {
+                       /* compress only the flow label */
+                       *hc06_ptr = tmp;
+                       hc06_ptr += 1;
+               }
+       } else {
+               /* Flow label cannot be compressed */
+               if ((hdr->priority == 0) &&
+                  ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+                       /* compress only traffic class */
+                       iphc0 |= LOWPAN_IPHC_TC_C;
+                       *hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
+                       memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
+                       hc06_ptr += 3;
+               } else {
+                       /* compress nothing */
+                       memcpy(hc06_ptr, &hdr, 4);
+                       /* replace the top byte with new ECN | DSCP format */
+                       *hc06_ptr = tmp;
+                       hc06_ptr += 4;
+               }
+       }
+
+       /* NOTE: payload length is always compressed */
+
+       /* Next Header is compress if UDP */
+       if (hdr->nexthdr == UIP_PROTO_UDP)
+               iphc0 |= LOWPAN_IPHC_NH_C;
+
+       if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+               *hc06_ptr = hdr->nexthdr;
+               hc06_ptr += 1;
+       }
+
+       /*
+        * Hop limit
+        * if 1:   compress, encoding is 01
+        * if 64:  compress, encoding is 10
+        * if 255: compress, encoding is 11
+        * else do not compress
+        */
+       switch (hdr->hop_limit) {
+       case 1:
+               iphc0 |= LOWPAN_IPHC_TTL_1;
+               break;
+       case 64:
+               iphc0 |= LOWPAN_IPHC_TTL_64;
+               break;
+       case 255:
+               iphc0 |= LOWPAN_IPHC_TTL_255;
+               break;
+       default:
+               *hc06_ptr = hdr->hop_limit;
+               hc06_ptr += 1;
+               break;
+       }
+
+       /* source address compression */
+       if (is_addr_unspecified(&hdr->saddr)) {
+               pr_debug("source address is unspecified, setting SAC\n");
+               iphc1 |= LOWPAN_IPHC_SAC;
+       /* TODO: context lookup */
+       } else if (is_addr_link_local(&hdr->saddr)) {
+               iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+                               LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr);
+               pr_debug("source address unicast link-local %pI6c "
+                       "iphc1 0x%02x\n", &hdr->saddr, iphc1);
+       } else {
+               pr_debug("send the full source address\n");
+               memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
+               hc06_ptr += 16;
+       }
+
+       /* destination address compression */
+       if (is_addr_mcast(&hdr->daddr)) {
+               pr_debug("destination address is multicast: ");
+               iphc1 |= LOWPAN_IPHC_M;
+               if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
+                       pr_debug("compressed to 1 octet\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_11;
+                       /* use last byte */
+                       *hc06_ptr = hdr->daddr.s6_addr[15];
+                       hc06_ptr += 1;
+               } else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
+                       pr_debug("compressed to 4 octets\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_10;
+                       /* second byte + the last three */
+                       *hc06_ptr = hdr->daddr.s6_addr[1];
+                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
+                       hc06_ptr += 4;
+               } else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
+                       pr_debug("compressed to 6 octets\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_01;
+                       /* second byte + the last five */
+                       *hc06_ptr = hdr->daddr.s6_addr[1];
+                       memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
+                       hc06_ptr += 6;
+               } else {
+                       pr_debug("using full address\n");
+                       iphc1 |= LOWPAN_IPHC_DAM_00;
+                       memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
+                       hc06_ptr += 16;
+               }
+       } else {
+               /* TODO: context lookup */
+               if (is_addr_link_local(&hdr->daddr)) {
+                       iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+                               LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
+                       pr_debug("dest address unicast link-local %pI6c "
+                               "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+               } else {
+                       pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
+                       memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
+                       hc06_ptr += 16;
+               }
+       }
+
+       /* UDP header compression */
+       if (hdr->nexthdr == UIP_PROTO_UDP)
+               compress_udp_header(&hc06_ptr, skb);
+
+       head[0] = iphc0;
+       head[1] = iphc1;
+
+       skb_pull(skb, sizeof(struct ipv6hdr));
+       skb_reset_transport_header(skb);
+       memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+       skb_reset_network_header(skb);
+
+       pr_debug("header len %d skb %u\n", (int)(hc06_ptr - head), skb->len);
+
+       raw_dump_table(__func__, "raw skb data dump compressed",
+                               skb->data, skb->len);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(lowpan_header_compress);
index d7716d6..951a83e 100644 (file)
@@ -1,5 +1,5 @@
 obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
-obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o
+obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o 6lowpan_iphc.o
 
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o
 af_802154-y := af_ieee802154.o raw.o dgram.o
index 12c97d8..d125fcd 100644 (file)
@@ -1816,6 +1816,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
                return addrconf_ifid_sit(eui, dev);
        case ARPHRD_IPGRE:
                return addrconf_ifid_gre(eui, dev);
+       case ARPHRD_6LOWPAN:
        case ARPHRD_IEEE802154:
                return addrconf_ifid_eui64(eui, dev);
        case ARPHRD_IEEE1394:
@@ -2658,7 +2659,8 @@ static void addrconf_dev_config(struct net_device *dev)
            (dev->type != ARPHRD_INFINIBAND) &&
            (dev->type != ARPHRD_IEEE802154) &&
            (dev->type != ARPHRD_IEEE1394) &&
-           (dev->type != ARPHRD_TUNNEL6)) {
+           (dev->type != ARPHRD_TUNNEL6) &&
+           (dev->type != ARPHRD_6LOWPAN)) {
                /* Alas, we support only Ethernet autoconfiguration. */
                return;
        }
index 02ab341..c1903f4 100644 (file)
@@ -133,11 +133,8 @@ int nfc_dev_up(struct nfc_dev *dev)
                dev->dev_up = true;
 
        /* We have to enable the device before discovering SEs */
-       if (dev->ops->discover_se) {
-               rc = dev->ops->discover_se(dev);
-               if (rc)
-                       pr_warn("SE discovery failed\n");
-       }
+       if (dev->ops->discover_se && dev->ops->discover_se(dev))
+               pr_err("SE discovery failed\n");
 
 error:
        device_unlock(&dev->dev);
index 09fc954..c129d15 100644 (file)
@@ -339,7 +339,6 @@ int digital_target_found(struct nfc_digital_dev *ddev,
        pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol);
 
        ddev->curr_rf_tech = rf_tech;
-       ddev->curr_protocol = protocol;
 
        if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) {
                ddev->skb_add_crc = digital_skb_add_crc_none;
@@ -541,8 +540,14 @@ static int digital_dep_link_up(struct nfc_dev *nfc_dev,
                               __u8 comm_mode, __u8 *gb, size_t gb_len)
 {
        struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+       int rc;
+
+       rc = digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len);
 
-       return digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len);
+       if (!rc)
+               ddev->curr_protocol = NFC_PROTO_NFC_DEP;
+
+       return rc;
 }
 
 static int digital_dep_link_down(struct nfc_dev *nfc_dev)
@@ -557,6 +562,20 @@ static int digital_dep_link_down(struct nfc_dev *nfc_dev)
 static int digital_activate_target(struct nfc_dev *nfc_dev,
                                   struct nfc_target *target, __u32 protocol)
 {
+       struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
+
+       if (ddev->poll_tech_count) {
+               pr_err("Can't activate a target while polling\n");
+               return -EBUSY;
+       }
+
+       if (ddev->curr_protocol) {
+               pr_err("A target is already active\n");
+               return -EBUSY;
+       }
+
+       ddev->curr_protocol = protocol;
+
        return 0;
 }
 
@@ -565,6 +584,11 @@ static void digital_deactivate_target(struct nfc_dev *nfc_dev,
 {
        struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev);
 
+       if (!ddev->curr_protocol) {
+               pr_err("No active target\n");
+               return;
+       }
+
        ddev->curr_protocol = 0;
 }
 
index 07bbc24..43e450f 100644 (file)
@@ -32,7 +32,6 @@
 #define DIGITAL_ATR_REQ_MIN_SIZE 16
 #define DIGITAL_ATR_REQ_MAX_SIZE 64
 
-#define DIGITAL_NFCID3_LEN ((u8)8)
 #define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
 #define DIGITAL_GB_BIT 0x02
 
@@ -206,10 +205,9 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
        atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
        atr_req->cmd = DIGITAL_CMD_ATR_REQ;
        if (target->nfcid2_len)
-               memcpy(atr_req->nfcid3, target->nfcid2,
-                      max(target->nfcid2_len, DIGITAL_NFCID3_LEN));
+               memcpy(atr_req->nfcid3, target->nfcid2, NFC_NFCID2_MAXSIZE);
        else
-               get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN);
+               get_random_bytes(atr_req->nfcid3, NFC_NFCID3_MAXSIZE);
 
        atr_req->did = 0;
        atr_req->bs = 0;
@@ -382,6 +380,33 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
                                   data_exch);
 }
 
+static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
+{
+       ddev->curr_rf_tech = rf_tech;
+
+       ddev->skb_add_crc = digital_skb_add_crc_none;
+       ddev->skb_check_crc = digital_skb_check_crc_none;
+
+       if (DIGITAL_DRV_CAPS_TG_CRC(ddev))
+               return;
+
+       switch (ddev->curr_rf_tech) {
+       case NFC_DIGITAL_RF_TECH_106A:
+               ddev->skb_add_crc = digital_skb_add_crc_a;
+               ddev->skb_check_crc = digital_skb_check_crc_a;
+               break;
+
+       case NFC_DIGITAL_RF_TECH_212F:
+       case NFC_DIGITAL_RF_TECH_424F:
+               ddev->skb_add_crc = digital_skb_add_crc_f;
+               ddev->skb_check_crc = digital_skb_check_crc_f;
+               break;
+
+       default:
+               break;
+       }
+}
+
 static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
                                    struct sk_buff *resp)
 {
@@ -472,11 +497,13 @@ int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
 static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
                                             void *arg, struct sk_buff *resp)
 {
-       u8 rf_tech = PTR_ERR(arg);
+       u8 rf_tech = (unsigned long)arg;
 
        if (IS_ERR(resp))
                return;
 
+       digital_tg_set_rf_tech(ddev, rf_tech);
+
        digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech);
 
        digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL);
@@ -508,7 +535,7 @@ static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did,
        ddev->skb_add_crc(skb);
 
        rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
-                                ERR_PTR(rf_tech));
+                                (void *)(unsigned long)rf_tech);
 
        if (rc)
                kfree_skb(skb);
@@ -661,16 +688,10 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
 
        if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) {
                min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2;
-
-               ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_106A;
-               ddev->skb_add_crc = digital_skb_add_crc_a;
-               ddev->skb_check_crc = digital_skb_check_crc_a;
+               digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_106A);
        } else {
                min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1;
-
-               ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_212F;
-               ddev->skb_add_crc = digital_skb_add_crc_f;
-               ddev->skb_check_crc = digital_skb_check_crc_f;
+               digital_tg_set_rf_tech(ddev, NFC_DIGITAL_RF_TECH_212F);
        }
 
        if (resp->len < min_size) {
@@ -678,10 +699,7 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
                goto exit;
        }
 
-       if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) {
-               ddev->skb_add_crc = digital_skb_add_crc_none;
-               ddev->skb_check_crc = digital_skb_check_crc_none;
-       }
+       ddev->curr_protocol = NFC_PROTO_NFC_DEP_MASK;
 
        rc = ddev->skb_check_crc(resp);
        if (rc) {
index 3b96100..d45b638 100644 (file)
@@ -335,11 +335,8 @@ exit:
        kfree_skb(skb);
 
 exit_noskb:
-       if (r) {
-               /* TODO: There was an error dispatching the event,
-                * how to propagate up to nfc core?
-                */
-       }
+       if (r)
+               nfc_hci_driver_failure(hdev, r);
 }
 
 static void nfc_hci_cmd_timeout(unsigned long data)
index 693cd1a..bec6ed1 100644 (file)
@@ -675,7 +675,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
        do {
                remote_miu = sock->remote_miu > LLCP_MAX_MIU ?
-                               local->remote_miu : sock->remote_miu;
+                               LLCP_DEFAULT_MIU : sock->remote_miu;
 
                frag_len = min_t(size_t, remote_miu, remaining_len);
 
@@ -684,8 +684,10 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
 
                pdu = llcp_allocate_pdu(sock, LLCP_PDU_I,
                                        frag_len + LLCP_SEQUENCE_SIZE);
-               if (pdu == NULL)
+               if (pdu == NULL) {
+                       kfree(msg_data);
                        return -ENOMEM;
+               }
 
                skb_put(pdu, LLCP_SEQUENCE_SIZE);
 
index 1349074..6184bd1 100644 (file)
@@ -943,7 +943,6 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
        new_sock->local = nfc_llcp_local_get(local);
        new_sock->rw = sock->rw;
        new_sock->miux = sock->miux;
-       new_sock->remote_miu = local->remote_miu;
        new_sock->nfc_protocol = sock->nfc_protocol;
        new_sock->dsap = ssap;
        new_sock->target_idx = local->target_idx;
index 69fbc8d..4a53bb5 100644 (file)
@@ -700,7 +700,6 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
 
        llcp_sock->dev = dev;
        llcp_sock->local = nfc_llcp_local_get(local);
-       llcp_sock->remote_miu = llcp_sock->local->remote_miu;
        llcp_sock->ssap = nfc_llcp_get_local_ssap(local);
        if (llcp_sock->ssap == LLCP_SAP_MAX) {
                ret = -ENOMEM;
index f0e955e..46bda01 100644 (file)
@@ -301,6 +301,9 @@ static int nci_open_device(struct nci_dev *ndev)
        rc = __nci_request(ndev, nci_reset_req, 0,
                           msecs_to_jiffies(NCI_RESET_TIMEOUT));
 
+       if (ndev->ops->setup(ndev))
+               ndev->ops->setup(ndev);
+
        if (!rc) {
                rc = __nci_request(ndev, nci_init_req, 0,
                                   msecs_to_jiffies(NCI_INIT_TIMEOUT));
@@ -361,6 +364,8 @@ static int nci_close_device(struct nci_dev *ndev)
                      msecs_to_jiffies(NCI_RESET_TIMEOUT));
        clear_bit(NCI_INIT, &ndev->flags);
 
+       del_timer_sync(&ndev->cmd_timer);
+
        /* Flush cmd wq */
        flush_workqueue(ndev->cmd_wq);
 
@@ -408,12 +413,26 @@ static int nci_dev_down(struct nfc_dev *nfc_dev)
        return nci_close_device(ndev);
 }
 
+int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val)
+{
+       struct nci_set_config_param param;
+
+       if (!val || !len)
+               return 0;
+
+       param.id = id;
+       param.len = len;
+       param.val = val;
+
+       return __nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+                            msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+}
+EXPORT_SYMBOL(nci_set_config);
+
 static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
 {
        struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
        struct nci_set_config_param param;
-       __u8 local_gb[NFC_MAX_GT_LEN];
-       int i;
 
        param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
        if ((param.val == NULL) || (param.len == 0))
@@ -422,11 +441,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
        if (param.len > NFC_MAX_GT_LEN)
                return -EINVAL;
 
-       for (i = 0; i < param.len; i++)
-               local_gb[param.len-1-i] = param.val[i];
-
        param.id = NCI_PN_ATR_REQ_GEN_BYTES;
-       param.val = local_gb;
 
        return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
                           msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
index a271c27..722da61 100644 (file)
@@ -124,6 +124,10 @@ int ieee80211_radiotap_iterator_init(
        /* find payload start allowing for extended bitmap(s) */
 
        if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+               if ((unsigned long)iterator->_arg -
+                   (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+                   (unsigned long)iterator->_max_length)
+                       return -EINVAL;
                while (get_unaligned_le32(iterator->_arg) &
                                        (1 << IEEE80211_RADIOTAP_EXT)) {
                        iterator->_arg += sizeof(uint32_t);
index c854f1c..a635091 100644 (file)
@@ -625,6 +625,16 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
        }
 #endif
 
+       if (!bss && (status == WLAN_STATUS_SUCCESS)) {
+               WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
+               bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
+                                      wdev->ssid, wdev->ssid_len,
+                                      WLAN_CAPABILITY_ESS,
+                                      WLAN_CAPABILITY_ESS);
+               if (bss)
+                       cfg80211_hold_bss(bss_from_pub(bss));
+       }
+
        if (wdev->current_bss) {
                cfg80211_unhold_bss(wdev->current_bss);
                cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
@@ -642,16 +652,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
                return;
        }
 
-       if (!bss) {
-               WARN_ON_ONCE(!wiphy_to_dev(wdev->wiphy)->ops->connect);
-               bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
-                                      wdev->ssid, wdev->ssid_len,
-                                      WLAN_CAPABILITY_ESS,
-                                      WLAN_CAPABILITY_ESS);
-               if (WARN_ON(!bss))
-                       return;
-               cfg80211_hold_bss(bss_from_pub(bss));
-       }
+       if (WARN_ON(!bss))
+               return;
 
        wdev->current_bss = bss_from_pub(bss);